/* Evolve locked type system — Space Grotesk display + Satoshi body */
/* ONE face, not three.
   This is a variable font — fvar wght 300-700, default 300 — and all three blocks carried
   the identical payload. Declaring a single font-weight on a variable font tells the browser
   'this file IS that weight', so it never touched the axis: 500, 600 and 700 all rendered at
   the default 300 and the heading hierarchy did not exist. Measured, same string at 56px:
   300 -> 3806 ink pixels, 500 -> 5064, 700 -> 5765, once the range is declared.
   A range also means the 29KB is fetched once instead of three times. */
@font-face{font-family:'Space Grotesk';font-style:normal;font-weight:300 700;font-display:block;src:url(space-grotesk-300.a0d054c4.woff2) format('woff2');}
@font-face{font-family:'Satoshi';font-style:normal;font-weight:400;font-display:swap;src:url(satoshi-400.50dca57f.woff2) format('woff2');}
@font-face{font-family:'Satoshi';font-style:normal;font-weight:500;font-display:swap;src:url(satoshi-500.af02a722.woff2) format('woff2');}
@font-face{font-family:'Satoshi';font-style:normal;font-weight:700;font-display:swap;src:url(satoshi-700.353a7fbf.woff2) format('woff2');}

/* ============================================================================
   FutureSafe — Evolve. Website wireframes, Phase 1.

   The design law is BRIEF-website-03-evolve.md §1 and nothing here departs from it.
   Three things this stylesheet is built to make hard to get wrong:

   1. Every ground sets BOTH its background and its text color. There is no rule anywhere that
      sets a background alone, because a light card on a dark page inheriting the dark-ground
      secondary landed at 1.93:1 earlier in this run.
   2. Secondary text is a pair, not a value. Slate on light grounds, Mist on dark. No single
      value clears 4.5:1 on both navy and ice — the window between the two requirements is
      empty, so this is arithmetic and not taste.
   3. Signal sets type and rules on dark grounds only. On ice it is 1.52:1. Signal deep is the
      answer on light, at 4.78:1.
   ========================================================================= */

:root{
  --navy:#07141F; --raised:#0E2233; --ice:#EAF2F8; --paper:#FDFEFF;
  --blue:#008FE8; --bridge:#00BBD4; --signal:#00E0A4; --deep:#00795A;
  --slate:#5A6E83; --mist:#9DB2C4;
  --ramp:linear-gradient(118deg,#008FE8 0%,#00BBD4 52%,#00E0A4 100%);

  /* All spacing is a multiple of 8. Use the scale rather than arbitrary values. */
  --s1:8px; --s2:16px; --s3:24px; --s4:32px; --s5:48px; --s6:72px; --s7:112px; --s8:168px;
  /* The scroll felt stacked and close. The top of the scale is what governs how a page BREATHES —
     the distance between one idea ending and the next beginning — so that is where the room went,
     rather than inflating the gaps inside a section, which would only have loosened the content. */

  --display:'Space Grotesk',system-ui,sans-serif;
  --body:'Satoshi',system-ui,sans-serif;
  --measure:68ch;
  color-scheme:dark;    /* pairs with the meta: Auto Dark must never repaint these pages */
}

*,*::before,*::after{box-sizing:border-box}
html{-webkit-text-size-adjust:100%}
body{
  margin:0; background:var(--navy); color:var(--paper);
  font-family:var(--body); font-weight:400; font-size:17px; line-height:1.65;
  -webkit-font-smoothing:antialiased; font-synthesis-weight:none;
}
img,svg{max-width:100%}
img{height:auto}

/* ── focus and skip ─────────────────────────────────────────────────────── */
:where(a,button,summary,[tabindex]):focus-visible{
  outline:3px solid var(--signal); outline-offset:3px; border-radius:2px;
}
.ice :where(a,button,summary):focus-visible,
.paper :where(a,button,summary):focus-visible{outline-color:var(--deep)}
.skip{
  position:absolute; left:var(--s2); top:-64px; z-index:99;
  background:var(--signal); color:var(--navy); padding:12px 20px; border-radius:6px;
  font-family:var(--body); font-weight:700; text-decoration:none; transition:top .12s;
}
.skip:focus{top:var(--s2)}

/* ── grounds ────────────────────────────────────────────────────────────────
   Four grounds, each a background AND a color. Nothing sets one without the other. */
.navy  {background:var(--navy);  color:var(--paper)}
.raised{background:var(--raised);color:var(--paper)}
.ice   {background:var(--ice);   color:var(--navy)}
.paper {background:var(--paper); color:var(--navy)}

/* Secondary ink is set on PROSE ONLY — never on `li` generically.
   `.ice li{color:slate}` beats `.wtile{color:navy}` on specificity, so a generic list rule
   quietly repainted the step headings and every partner tile in the secondary ink. Each list
   below states its own color instead, and headings are pinned per ground so nothing can
   demote them by inheritance. */
.navy p,.raised p{color:var(--mist)}
.ice p,.paper p{color:var(--slate)}
.navy .lead,.raised .lead{color:var(--paper)}
.ice .lead,.paper .lead{color:var(--navy)}

.navy :is(h1,h2,h3,h4),.raised :is(h1,h2,h3,h4){color:var(--paper)}
.ice :is(h1,h2,h3,h4),.paper :is(h1,h2,h3,h4){color:var(--navy)}

/* ── layout ─────────────────────────────────────────────────────────────── */
.wrap{width:min(1180px,calc(100% - var(--s5))); margin-inline:auto}
.band{position:relative; padding-block:var(--s8); overflow:hidden}
.band + .band{border-top:1px solid transparent}
/* A near-white block on a near-white ground with no edge is invisible. Ice meeting paper needs
   a line, and so does every light surface sitting on a light ground. */
.ice + .paper, .paper + .ice{border-top:1px solid #D3DFEA}

/* ── type ───────────────────────────────────────────────────────────────────
   Space Grotesk is display. Satoshi is everything read at length, and every figure. The two
   never swap lanes: if a rule below sets --display on something that is read at length, it is
   a bug, not a choice. */
/* ── the scale ──────────────────────────────────────────────────────────────
   Rebuilt against the measured reference (see 06-briefs/REFERENCE-AUDIT-bouayaben.md).

   The old scale topped out at 62px against 17px body — a 3.6 : 1 spread, which reads as a
   well-set document rather than a statement. The reference runs 144 : 17, or 8.5 : 1. The RATIO
   is the effect, not the absolute size: a page needs somewhere to go between its loudest and its
   quietest voice, and ours had less than half the distance available.

   Matching 8.5 : 1 outright was too far — a 124px headline ate whole viewports on a page that has
   to carry argument, not just a name. This sits at 92 : 17, or 5.4 : 1: half again on the original
   spread, which is where the contrast starts working without the page shouting.

   Two rules that come with the size and are not optional:
     · leading collapses below 1 as type grows — 0.95 on display. Sub-one leading is what stops a
       huge headline reading as a paragraph.
     · tracking tightens as type grows and LOOSENS as it shrinks. A flat -.024em everywhere was
       too tight at 13px and too loose at 120px. */
h1,h2,h3,h4{font-family:var(--display); font-weight:700; margin:0}
h1{font-size:clamp(42px,6.6vw,92px); line-height:.97; letter-spacing:-.026em}
h2{font-size:clamp(30px,4.4vw,56px); font-weight:600; line-height:1.02; letter-spacing:-.022em}
h3{font-size:clamp(21px,2.2vw,27px); font-weight:600; line-height:1.16; letter-spacing:-.015em}
h4{font-size:20px; font-weight:600; letter-spacing:-.012em}
p{margin:0 0 var(--s3); max-width:var(--measure)}
p:last-child{margin-bottom:0}

.lead{font-family:var(--body); font-weight:500; font-size:clamp(19px,1.85vw,24px); line-height:1.4;
  letter-spacing:-.008em; max-width:40ch}
h2 + .lead,h1 + .lead{margin-top:var(--s3)}
/* A heading followed straight by a two-column block had no gap at all — the grids each carry
   their own top margin and `.split` did not. */
h2 + .split,h2 + .split-40,h2 + .figure-row{margin-top:var(--s5)}
/* 58ch of Satoshi at 22px is 884px — a lead line long enough to lose, and wide enough to run
   into the corner signature. 40ch holds it to a readable measure and clears the device. */
.standfirst{font-family:var(--body); font-weight:400; font-size:clamp(18px,1.5vw,22px);
  line-height:1.55; max-width:46ch; margin-top:var(--s4)}
.navy .standfirst,.raised .standfirst{color:var(--mist)}
.ice .standfirst,.paper .standfirst{color:var(--slate)}

.eyebrow{
  display:flex; align-items:center; gap:var(--s2);
  font-family:var(--body); font-weight:700; font-size:12px; letter-spacing:.16em;
  text-transform:uppercase; margin:0 0 var(--s4); max-width:none;
}
.navy .eyebrow,.raised .eyebrow{color:var(--signal)}
.ice .eyebrow,.paper .eyebrow{color:var(--deep)}
.data{font-variant-numeric:tabular-nums; font-weight:500}
.navy .data,.raised .data{color:var(--blue)}
.ice .data,.paper .data{color:var(--deep)}

/* ── the cascade ────────────────────────────────────────────────────────────
   Section opener, call to action, wayfinding. Never as pattern, fill or decoration. There is no
   rule in this file that tiles, repeats or backgrounds it. */
.chev{display:inline-flex; align-items:center; gap:var(--g,4px); flex:none}
.chev b{
  width:calc(var(--s) * .58); height:var(--s); background:var(--c);
  clip-path:polygon(0 0,42% 0,100% 50%,42% 100%,0 100%,58% 50%);
}

/* ── the signature ──────────────────────────────────────────────────────────
   The mark as a fine line, in the corner of a surface at 42% of its height, with a margin of
   0.15 × the mark's own height on both edges. Not used on paper — the panel goes on ice instead. */
/* 42% of a book page is a corner mark. 42% of a 700px web band is a billboard, so it is capped:
   the proportion is honored where the band is short, and the cap stops it becoming the surface. */
.sig{position:absolute; right:6.3%; bottom:6.3%; height:min(42%, 260px); width:auto;
  pointer-events:none}

/* ── buttons: the CTA ladder ────────────────────────────────────────────────
   Rung 1 commit, rung 2 consider, rung 3 explore. One rung 1 per page, always paired with a
   rung 2 at the page end. Signal fills the primary on dark grounds; on light it cannot, because
   Signal against ice is 1.52:1 and the button would not be distinguishable from the page — navy
   fills it there instead. */
.btn{
  display:inline-flex; align-items:center; gap:12px;
  font-family:var(--body); font-weight:700; font-size:15px; letter-spacing:.005em;
  padding:15px 26px; border-radius:8px; text-decoration:none; border:1.5px solid transparent;
  transition:transform .12s ease, background-color .12s ease, border-color .12s ease;
}
.btn:hover{transform:translateY(-1px)}
.primary{background:var(--signal); color:var(--navy)}
.primary:hover{background:#3AE9BA}
.primary.on-light{background:var(--navy); color:var(--paper)}
.primary.on-light:hover{background:#122636}
.secondary{background:transparent; color:var(--paper); border-color:var(--mist)}
.secondary:hover{border-color:var(--paper); background:rgba(157,178,196,.10)}
.secondary.on-light{color:var(--navy); border-color:var(--slate)}
.secondary.on-light:hover{border-color:var(--navy); background:rgba(90,110,131,.08)}

.ladder{display:flex; flex-wrap:wrap; gap:var(--s2); margin-top:var(--s4)}

.lateral{
  display:inline-flex; align-items:center; gap:8px; font-weight:700; font-size:15px;
  text-decoration:none; border-bottom:1.5px solid currentColor; padding-bottom:2px;
}
.navy .lateral,.raised .lateral{color:var(--signal)}
.ice .lateral,.paper .lateral{color:var(--deep)}
.lateral::after{content:"→"; font-weight:400}
.laterals{display:flex; flex-wrap:wrap; gap:var(--s4); margin-top:var(--s4)}

/* Body links inside running text — not the ladder, but they still have to clear AA. */
.navy main a:not(.btn):not(.lateral):not(.card),
.raised main a:not(.btn):not(.lateral):not(.card){color:var(--signal)}
.ice main a:not(.btn):not(.lateral):not(.card),
.paper main a:not(.btn):not(.lateral):not(.card){color:var(--deep)}

/* ── header ─────────────────────────────────────────────────────────────── */
.site-head{position:sticky; top:0; z-index:40; background:rgba(7,20,31,.94);
  backdrop-filter:blur(10px); border-bottom:1px solid #16293A; color:var(--paper)}

/* The header is in flow and sticky, so a hero asking for 100svh produces header PLUS a screen —
   the hero runs past the fold and everything in it sits lower than it should. Named here so the
   hero can subtract it, and re-declared at the breakpoint where the bar gets shorter. */
:root{--head-h:77px}
.head-in{display:flex; align-items:center; gap:var(--s5); min-height:76px}
.brand{display:flex; align-items:center; flex:none}
/* The nav lockup renders at a 13.3px cap height. Below roughly 40px the guidelines require the
   flat or single-color master, so this is the flat one — checked, not assumed. */
.brand img{height:30px; width:auto; display:block}
.site-head nav{margin-inline-start:auto}
.site-head nav ul{display:flex; gap:var(--s4); list-style:none; margin:0; padding:0}
.site-head nav a{color:var(--mist); text-decoration:none; font-weight:500; font-size:15px;
  padding-block:6px; border-bottom:2px solid transparent}
.site-head nav a:hover{color:var(--paper)}
.site-head nav a[aria-current="page"]{color:var(--paper); border-bottom-color:var(--signal)}
.head-cta{padding:11px 20px; font-size:14px; flex:none}

/* ── footer ─────────────────────────────────────────────────────────────── */
.site-foot{background:var(--raised); color:var(--paper); padding-top:var(--s7);
  border-top:1px solid #16293A}
.foot-in{display:grid; grid-template-columns:1.5fr 1fr 1.3fr 1.2fr; gap:var(--s5);
  padding-bottom:var(--s6)}
/* The one place on the site where the mark renders in full expression: 112px tall is a 41.5px
   cap height, which clears the 40px threshold. Below 768px it drops to the flat master. */
.foot-lockup{height:112px; width:auto; display:block}
.foot-lockup-sm{display:none; height:60px; width:auto}
.foot-brand p{font-size:15px; margin-top:var(--s3); color:var(--mist); max-width:34ch}
.social{display:flex; gap:var(--s3); list-style:none; margin:var(--s3) 0 0; padding:0}
.social a{color:var(--mist); font-size:14px; font-weight:700}
.foot-nav h2{font-family:var(--body); font-size:11px; font-weight:700; letter-spacing:.14em;
  text-transform:uppercase; color:var(--signal); margin:0 0 var(--s3)}
.foot-nav ul{list-style:none; margin:0; padding:0; display:grid; gap:12px}
.foot-nav li{color:var(--mist); font-size:15px}
.foot-nav a{color:var(--mist); text-decoration:none}
.foot-nav a:hover{color:var(--paper); text-decoration:underline}
/* The footer's ground is Raised, which is dark — so its secondary ink is Mist, not Slate.
   Slate here measured 3.08:1. This is the pair rule doing its job: the value is chosen by the
   ground, not by the role. */
.fl{display:block; font-size:11px; font-weight:700; letter-spacing:.1em; text-transform:uppercase;
  color:var(--mist); margin-bottom:2px}
.foot-base{border-top:1px solid #1B3145; padding-block:var(--s3) var(--s4)}
.foot-base p{font-size:13px; color:var(--mist); margin:0}

/* ── hero ───────────────────────────────────────────────────────────────── */
.hero{padding-block:var(--s8) var(--s8)}
.hero h1{max-width:19ch}
.hero .ladder{margin-top:var(--s5)}

/* ── the gradient moment ────────────────────────────────────────────────────
   Exactly one per page, full-bleed, never behind running text. Contrast shifts across the ramp:
   Slate manages 1.46:1 against the blue end. Navy is the only ink that clears 4.5:1 at every
   stop — 5.40:1 at the worst of them — so anything set on the ramp is navy. */
.ramp{background:var(--ramp); color:var(--navy); padding-block:var(--s7)}
.ramp h2,.ramp h3,.ramp p,.ramp li,.ramp .eyebrow{color:var(--navy)}
.ramp .eyebrow{color:var(--navy)}
.ramp .btn.primary{background:var(--navy); color:var(--paper)}
.ramp .btn.secondary{color:var(--navy); border-color:rgba(7,20,31,.55)}
.ramp .rows{display:grid; gap:var(--s3); margin-top:var(--s4); max-width:none}
.ramp .rows p{font-family:var(--display); font-weight:600; font-size:clamp(24px,3.6vw,48px);
  letter-spacing:-.022em; line-height:1.08; margin:0; max-width:none; color:var(--navy)}

/* ── cards and grids ────────────────────────────────────────────────────── */
.grid2{display:grid; grid-template-columns:repeat(2,1fr); gap:var(--s4)}
.grid3{display:grid; grid-template-columns:repeat(3,1fr); gap:var(--s4)}
.grid4{display:grid; grid-template-columns:repeat(4,1fr); gap:var(--s3)}
.split{display:grid; grid-template-columns:minmax(0,1fr) minmax(0,1fr); gap:var(--s6); align-items:start}
.split-40{display:grid; grid-template-columns:minmax(0,.85fr) minmax(0,1.15fr); gap:var(--s6); align-items:start}

.card{padding:var(--s4); border-radius:12px; display:block}
/* Cards set their own ground, so they set their own ink. A card on navy is raised; a card on
   ice is paper AND carries an edge, because a near-white block on a near-white ground with no
   edge is simply not there. */
.navy .card{background:var(--raised); color:var(--paper); border:1px solid #1B3145}
.raised .card{background:var(--navy); color:var(--paper); border:1px solid #1B3145}
.ice .card{background:var(--paper); color:var(--navy); border:1px solid #D3DFEA}
.paper .card{background:var(--ice); color:var(--navy); border:1px solid #D3DFEA}
.card h3{margin-bottom:12px}
.card p{font-size:16px; margin-bottom:0}
.navy .card p,.raised .card p{color:var(--mist)}
.ice .card p,.paper .card p{color:var(--slate)}

/* ── numbered sequences: wayfinding lives here ──────────────────────────── */
.steps{display:grid; grid-template-columns:repeat(4,1fr); gap:var(--s5); margin-top:var(--s5);
  list-style:none; padding:0; align-items:stretch}
.step{position:relative}
.step .num{font-family:var(--body); font-weight:700; font-size:13px; letter-spacing:.12em;
  font-variant-numeric:tabular-nums; display:block; margin-bottom:var(--s2)}
.navy .step .num,.raised .step .num{color:var(--blue)}
.ice .step .num,.paper .step .num{color:var(--deep)}
.step h3{margin-bottom:12px}
.step p{font-size:16px; max-width:32ch; margin:0}
/* The two chevrons between steps — the only place on a page the device may repeat. They sit in
   the middle of the gutter, belonging to neither step, which is what "between" has to look like:
   sat tight against the next number they read as part of it. */
.step .link{position:absolute; right:calc(var(--s5) / -2); top:15px; transform:translateX(50%) translateY(-50%)}
.step:last-child .link{display:none}

/* ── plain lists that used to be headings ───────────────────────────────── */
.ticks{list-style:none; margin:var(--s4) 0 0; padding:0; display:grid; gap:14px}
.ticks li{display:flex; gap:14px; align-items:flex-start; font-size:17px; line-height:1.5}
.navy .ticks li,.raised .ticks li{color:var(--mist)}
.ice .ticks li,.paper .ticks li{color:var(--slate)}
.ticks li::before{content:""; flex:none; width:8px; height:8px; border-radius:50%; margin-top:9px}
.navy .ticks li::before,.raised .ticks li::before{background:var(--signal)}
.ice .ticks li::before,.paper .ticks li::before{background:var(--deep)}
.ticks.two{grid-template-columns:repeat(2,1fr); gap:14px var(--s5)}

.rule-list{list-style:none; margin:var(--s4) 0 0; padding:0; display:grid;
  grid-template-columns:repeat(3,1fr); gap:var(--s3)}
.rule-list li{font-family:var(--display); font-weight:600; font-size:clamp(20px,2.4vw,30px);
  letter-spacing:-.02em; line-height:1.05; padding-top:var(--s3); border-top:2px solid}
.navy .rule-list li,.raised .rule-list li{border-color:var(--blue); color:var(--paper)}
.ice .rule-list li,.paper .rule-list li{border-color:var(--deep); color:var(--navy)}

/* ── pull quote ─────────────────────────────────────────────────────────── */
.pull{font-family:var(--display); font-weight:600; font-size:clamp(29px,4.8vw,64px);
  letter-spacing:-.024em; line-height:1.0; margin:0; max-width:19ch}
.pull em{font-style:normal; display:block}
.ramp .pull{color:var(--navy)}

/* ── placeholders ───────────────────────────────────────────────────────── */
.ph{margin:0; display:flex; flex-direction:column; justify-content:flex-end; gap:6px;
  padding:var(--s3); border-radius:12px; position:relative; overflow:hidden}
.ph.raised{background:var(--raised); color:var(--paper); border:1px dashed #24405A}
.ph.ice{background:var(--ice); color:var(--navy); border:1px dashed #B9CBDB}
.ph.paper{background:var(--paper); color:var(--navy); border:1px dashed #C9D8E5}
.ph-what{font-family:var(--body); font-weight:700; font-size:11px; letter-spacing:.14em;
  text-transform:uppercase}
.ph.raised .ph-what{color:var(--mist)} .ph.ice .ph-what,.ph.paper .ph-what{color:var(--slate)}
.ph-alt{font-size:13px; line-height:1.4; display:block}
.ph.raised .ph-alt{color:var(--mist)} .ph.ice .ph-alt,.ph.paper .ph-alt{color:var(--slate)}
.ph-alt i{font-style:normal; font-weight:700; font-size:10px; letter-spacing:.12em;
  text-transform:uppercase; margin-right:8px; padding:2px 6px; border-radius:3px}
.ph.raised .ph-alt i{background:#1B3145; color:var(--signal)}
/* Signal deep on #DCE7F0 is 4.31:1 — under AA for 10px text. The chip takes the next surface
   along instead: paper behind an ice placeholder (5.36:1), ice behind a paper one (4.78:1). */
.ph.ice .ph-alt i{background:var(--paper); color:var(--deep); border:1px solid #C9D8E5}
.ph.paper .ph-alt i{background:var(--ice); color:var(--deep)}

/* ── real photography ───────────────────────────────────────────────────────
   The same box a placeholder occupies, so swapping one for the other cannot move the layout.
   `object-fit:cover` and an explicit intrinsic width/height on the tag mean the space is reserved
   before the image decodes — without that, every page with a photo reflows as it loads and the
   reveal animations fire against a layout that then shifts under them. */
.shot{margin:0; position:relative; overflow:hidden; border-radius:12px; background:var(--raised)}
.shot.ice{background:#DCE7F0} .shot.paper{background:var(--ice)}
.shot img{width:100%; height:100%; object-fit:cover; display:block}
/* A photograph on a dark ground wants its edge softened into the band, not a hard rectangle
   floating on it. On the light grounds it takes a hairline instead, for the same reason a
   near-white tile needs one. */
.shot.raised{box-shadow:0 12px 34px rgba(0,0,0,.30)}
/* The hero photograph takes the same box the home hero's diagram takes, and hides at the same
   breakpoint the grid collapses at — a 6:5 photograph stacked under a 42px headline on a phone
   pushes the first call to action below the fold for no gain. */
.hero-shot{max-width:540px; margin-inline:auto; width:100%}
.believe-shot{margin-top:var(--s4)}
/* Shown below the words rather than dropped. Same reasoning as the hero graphic: a hero given a
   full screen and then emptied of its picture is worse than one that keeps it. */
@media (max-width:1080px){ .hero-shot{margin-top:var(--s5)} }

/* The blog index hero is two lines and no ladder, so it takes a full-width strip rather than the
   square-ish art the other heroes carry — a 4:3 picture beside two lines reads as a caption. The
   strip stays on small screens; unlike the square art it costs almost no vertical room. */
.hero-strip{margin-top:var(--s5); max-width:none}
@media (max-width:700px){ .hero-strip{aspect-ratio:16 / 7 !important} }
.shot.ice,.shot.paper{box-shadow:0 8px 24px rgba(7,20,31,.09); outline:1px solid rgba(7,20,31,.07);
  outline-offset:-1px}

/* ── the partner wall ───────────────────────────────────────────────────────
   Ten full-color third-party marks on a dark ground, competing with a device and a three-stop
   ramp, is the hardest version of this problem across the three routes. It is not solved by
   fighting it: the wall goes on ice, where a vendor's own colors can be themselves.

   The marks are the client's own published artwork, which is line art on a white ground.
   `mix-blend-mode:multiply` drops that white into the light band and leaves every pixel of the
   mark itself alone. The alternative — knocking each one out to a flat white or navy silhouette —
   is the one thing a partner wall must not do to somebody else's trademark. */
.wall{list-style:none; margin:var(--s5) 0 0; padding:0;
  display:grid; grid-template-columns:repeat(5,1fr); gap:var(--s2)}
.pmark{display:flex; align-items:center; justify-content:center; min-height:104px;
  padding:var(--s2) var(--s3);
  background:var(--paper); border:1px solid #D3DFEA; border-radius:10px}
/* On the paper ground a paper tile is the ground: it would read as an outline and nothing else.
   The tile takes ice there, which is the same relationship one step along the ramp of lights. */
.paper .pmark{background:var(--ice)}
.pmark img{max-width:100%; max-height:56px; width:auto; height:auto; object-fit:contain;
  mix-blend-mode:multiply}
.pmark{transition:transform .3s cubic-bezier(.22,.8,.28,1), border-color .3s ease, box-shadow .3s ease}
.pmark:hover{border-color:#9FBBD2; box-shadow:0 8px 22px rgba(7,20,31,.09)}
/* The mark itself must not be dimmed by a hover state — multiply already ties it to the tile. */

/* ── people ─────────────────────────────────────────────────────────────── */
.people{list-style:none; margin:var(--s5) 0 0; padding:0;
  display:grid; grid-template-columns:repeat(4,1fr); gap:var(--s3)}
.person .ph{aspect-ratio:1/1; margin-bottom:var(--s3)}
.person h3{font-size:20px; margin-bottom:4px}
.person .role{font-size:12px; font-weight:700; letter-spacing:.1em; text-transform:uppercase;
  margin:0 0 12px}
.navy .person .role,.raised .person .role{color:var(--blue)}
.ice .person .role,.paper .person .role{color:var(--deep)}
.person p{font-size:15px; max-width:none}

/* ── blog ───────────────────────────────────────────────────────────────── */
.posts{list-style:none; margin:var(--s6) 0 0; padding:0;
  display:grid; grid-template-columns:repeat(3,1fr); gap:var(--s5) var(--s4)}
.post .ph{aspect-ratio:768/403; margin-bottom:var(--s3)}
.post h3{margin-bottom:12px}
.post h3 a{text-decoration:none; color:inherit}
.post h3 a:hover{text-decoration:underline}
.post p{font-size:15px; max-width:none; margin-bottom:12px}
.post time{font-size:12px; font-weight:700; letter-spacing:.1em; text-transform:uppercase}
.navy .post time,.raised .post time{color:var(--mist)}
.ice .post time,.paper .post time{color:var(--slate)}

/* ── long-form reading: posts and legal ─────────────────────────────────────
   Satoshi at length, on ice, at a measure that does not need re-finding on every page. */
.doc{max-width:72ch; margin-inline:auto}
.doc p,.doc li{font-size:17px; line-height:1.72; max-width:none}
.ice .doc li,.paper .doc li{color:var(--slate)}
.navy .doc li,.raised .doc li{color:var(--mist)}
.doc h2{font-size:clamp(24px,2.4vw,30px); margin:var(--s6) 0 var(--s3)}
.doc h3{margin:var(--s5) 0 var(--s2)}
.doc ul,.doc ol{padding-left:1.3em; display:grid; gap:10px; margin:0 0 var(--s3)}
.docmeta{display:flex; flex-wrap:wrap; gap:var(--s3); font-size:13px; font-weight:700;
  letter-spacing:.1em; text-transform:uppercase; margin-top:var(--s3)}
.ice .docmeta,.paper .docmeta{color:var(--slate)}
.toc{margin:var(--s5) 0 0; padding:var(--s4); border-radius:12px;
  background:var(--paper); color:var(--navy); border:1px solid #D3DFEA}
.toc h2{font-family:var(--body); font-size:11px; font-weight:700; letter-spacing:.14em;
  text-transform:uppercase; color:var(--deep); margin:0 0 var(--s2)}
.toc ol{margin:0; padding-left:1.2em; display:grid; gap:8px}
.toc a{color:var(--navy); font-size:15px}
.crumb{display:flex; align-items:center; gap:10px; font-size:12px; font-weight:700;
  letter-spacing:.1em; text-transform:uppercase; margin:0 0 var(--s4); flex-wrap:wrap}
.ice .crumb,.paper .crumb{color:var(--slate)}
.crumb a{text-decoration:none}

/* ── contact ────────────────────────────────────────────────────────────── */
.contact-card .big{font-family:var(--display); font-weight:600; font-size:clamp(19px,1.9vw,24px);
  letter-spacing:-.014em; text-decoration:none; display:block; margin-top:var(--s3); word-break:break-word}
.ice .contact-card .big,.paper .contact-card .big{color:var(--deep)}
.navy .contact-card .big,.raised .contact-card .big{color:var(--signal)}
.contact-card .tel{display:block; margin-top:12px; font-size:17px; font-weight:700;
  font-variant-numeric:tabular-nums}

/* ── review note ────────────────────────────────────────────────────────── */
.rnote{position:fixed; right:var(--s3); bottom:var(--s3); z-index:60; max-width:min(430px,calc(100vw - 32px))}
.rnote details{background:var(--paper); color:var(--navy); border:1px solid #C9D8E5;
  border-radius:12px; box-shadow:0 18px 44px rgba(7,20,31,.28)}
.rnote summary{cursor:pointer; padding:14px 18px; font-family:var(--body); font-weight:700;
  font-size:13px; line-height:1.4; color:var(--navy); border-radius:12px}
.rnote[open] summary,.rnote details[open] summary{border-bottom:1px solid #E2EAF2}
.rnote .rlead{font-size:13px; line-height:1.5; color:var(--navy); padding:14px 18px 0; margin:0;
  max-width:none}
.rnote ul{margin:12px 0 0; padding:0 18px 18px 34px; display:grid; gap:9px}
.rnote li{font-size:13px; line-height:1.5; color:var(--slate)}
.rnote li b{color:var(--navy)}

/* ── responsive ─────────────────────────────────────────────────────────────
   The current site is desktop-shaped. Checked at 375 / 768 / 1280 / 1920. */
@media (max-width:1080px){
  .wrap{width:min(1180px,calc(100% - var(--s4)))}
  .grid4,.wall{grid-template-columns:repeat(3,1fr)}
  .people{grid-template-columns:repeat(2,1fr)}
  .posts{grid-template-columns:repeat(2,1fr)}
  .foot-in{grid-template-columns:repeat(2,1fr); gap:var(--s5)}
  .steps{grid-template-columns:repeat(2,1fr); gap:var(--s5)}
  /* The wayfinding chevrons only mean anything while the four steps are one row. Wrapped into a
     2 × 2 grid they point from step 1 at step 2's number and from step 3 at step 4's — direction
     with nothing to direct. The device is removed rather than re-aimed. */
  .step .link{display:none}
  .site-head nav ul{gap:var(--s3)}
  .head-in{gap:var(--s3)}
}
@media (max-width:820px){
  body{font-size:16px}
  .band{padding-block:var(--s7)}
  .hero{padding-block:var(--s6) var(--s7)}
  .split,.split-40,.grid2,.grid3,.rule-list,.ticks.two{grid-template-columns:1fr; gap:var(--s4)}
  .foot-lockup{display:none} .foot-lockup-sm{display:block}
  /* Below this width the nav becomes a disclosure panel. The button is `hidden` in the markup and
     revealed by the script, so a browser with no JavaScript keeps a visible, working nav instead
     of a dead hamburger — the failure mode of every menu that hides itself in CSS. */
  .head-in{justify-content:space-between; min-height:64px; flex-wrap:wrap; gap:var(--s2)}
  :root{--head-h:65px}
  html:not(.nvd) .head-cta{display:none}
  html:not(.nvd) .nav-toggle{order:3}
  html:not(.nvd) .site-head nav{order:4; width:100%; margin-inline-start:0}
  html:not(.nvd) .site-head nav ul{flex-direction:column; gap:0; padding-bottom:var(--s2)}
  html:not(.nvd) .site-head nav li{border-top:1px solid #16293A}
  html:not(.nvd) .site-head nav a{display:block; padding:14px 2px; font-size:17px; border-bottom:0}
  html:not(.nvd) .site-head nav a[aria-current="page"]{border-bottom:0; color:var(--signal)}
  .rnote{right:var(--s2); bottom:var(--s2)}
}
@media (max-width:560px){
  .wall,.grid4,.posts,.people{grid-template-columns:repeat(2,1fr)}
  .steps{grid-template-columns:1fr; gap:var(--s4)}
  .step{padding-right:0}
  .step .link{display:none}
  .ladder .btn{width:100%; justify-content:center}
  .band{padding-block:var(--s6)}
  .sig{display:none}
  .head-cta{padding:10px 14px; font-size:13px}
  .head-cta .chev{display:none}
}
@media (min-width:1600px){ .wrap{width:min(1320px,calc(100% - var(--s6)))} }

@media (prefers-reduced-motion:reduce){
  *{animation:none !important; transition:none !important}
  .btn:hover{transform:none}
}

/* ============================================================================
   Components introduced by the five remaining commercial pages and the two
   long-form templates. Same two disciplines as above: anything that sets a
   background sets an ink, and no generic element selector paints a component.
   ========================================================================= */

/* ── hero variants ──────────────────────────────────────────────────────── */
.hero.short{padding-block:var(--s7) var(--s7)}
.hero.short h1{max-width:16ch}
.lead-hero{margin-top:var(--s3)}
.kicker{font-family:var(--body); font-weight:700; font-size:13px; letter-spacing:.12em;
  text-transform:uppercase; margin:0 0 var(--s3)}
.navy .kicker,.raised .kicker{color:var(--signal)}
.ice .kicker,.paper .kicker{color:var(--deep)}
.hero .story{margin-top:var(--s5); gap:var(--s5)}
.hero .story p{max-width:46ch}
.hero .laterals{margin-top:var(--s4)}

/* ── the single rule line ───────────────────────────────────────────────────
   Safeguard's "No client contact. No loss of control. No added operational burden." is ONE line
   in the source, unlike the homepage's three. It is not split into three here, because splitting
   a sentence is a rewrite. */
.rule-single{font-family:var(--display); font-weight:600; font-size:clamp(22px,2.8vw,36px);
  letter-spacing:-.02em; line-height:1.12; padding-top:var(--s3); border-top:2px solid;
  max-width:22ch}
.navy .rule-single,.raised .rule-single{border-color:var(--blue); color:var(--paper)}
.ice .rule-single,.paper .rule-single{border-color:var(--deep); color:var(--navy)}

/* ── the figure ─────────────────────────────────────────────────────────────
   $500,000 is the only hard number anywhere on the site, and on the live page it is one h2 among
   sixty. Space Grotesk, tabular figures, given a line of its own. */
.figure-row{display:grid; grid-template-columns:auto minmax(0,1fr); gap:var(--s5);
  align-items:center; margin-top:var(--s6); padding-block:var(--s4); border-block:1px solid}
.ice .figure-row,.paper .figure-row{border-color:#D3DFEA}
.navy .figure-row,.raised .figure-row{border-color:#1B3145}
.figure{font-family:var(--display); font-weight:700; font-size:clamp(46px,7.4vw,100px);
  letter-spacing:-.03em; line-height:1; margin:0; font-variant-numeric:tabular-nums; max-width:none}
.ice .figure,.paper .figure{color:var(--deep)}
.navy .figure,.raised .figure{color:var(--signal)}
.figure-note{margin:0; max-width:46ch}
.tail{margin-top:var(--s4)}

/* ── lifecycle phases ───────────────────────────────────────────────────── */
.phases{list-style:none; margin:var(--s5) 0 0; padding:0; display:grid;
  grid-template-columns:repeat(3,1fr); gap:var(--s4)}
.phases li{padding-top:var(--s3); border-top:2px solid}
.navy .phases li,.raised .phases li{border-color:var(--signal)}
.ice .phases li,.paper .phases li{border-color:var(--deep)}
.phases h3{font-size:clamp(19px,1.9vw,23px); margin-bottom:10px}
.phases p{font-size:16px; margin:0; max-width:30ch}
.coverage-tail{margin-top:var(--s6); align-items:center}
.sub{margin-top:var(--s5); margin-bottom:var(--s2)}

/* ── service cards ──────────────────────────────────────────────────────── */
.cards{display:grid; grid-template-columns:repeat(3,1fr); gap:var(--s3); margin-top:var(--s5)}
.card.svc{display:flex; flex-direction:column}
.card.svc .num{font-family:var(--body); font-weight:700; font-size:12px; letter-spacing:.14em;
  font-variant-numeric:tabular-nums; margin-bottom:var(--s3)}
.navy .card.svc .num,.raised .card.svc .num{color:var(--blue)}
.ice .card.svc .num,.paper .card.svc .num{color:var(--deep)}
.card.svc h3{font-size:clamp(19px,1.8vw,22px); margin-bottom:var(--s2)}
.card.svc p + p{margin-top:var(--s2)}
.card.svc p{margin-bottom:0}
/* The last two of five sit on a three-column grid. Rather than leave a hole, they take the
   remaining width — the count is content, not layout. */
.cards .card.svc:nth-child(4){grid-column:span 2}

/* ── the word swap ──────────────────────────────────────────────────────────
   "Our job is / not to add noise. / to create stability." arrives from the live page as three
   fragments with empty elements between them. Rendered as the swap it plainly is: no words added,
   no words dropped, and the strike carries the meaning the empty elements used to. */
.swap{margin:var(--s4) 0 0; max-width:none; display:grid; gap:4px;
  font-family:var(--display); font-weight:600; letter-spacing:-.026em; line-height:1.0;
  font-size:clamp(28px,4.6vw,60px); color:var(--navy)}
/* Navy at 50% composites to 2.41:1 against the blue end of the ramp. At 72% it is 3.64:1, which
   clears the 3:1 large-text requirement at every stop — the strike and the weight carry the
   hierarchy, the opacity only softens it. */
.swap .stem{opacity:.72}
.swap s{text-decoration-thickness:2px; opacity:.72}
.swap b{font-weight:700}

/* ── about ──────────────────────────────────────────────────────────────── */
.role-lead{max-width:56ch}
.team-h{margin-top:var(--s7)}

/* ── contact ────────────────────────────────────────────────────────────── */
.contact-grid{margin-top:var(--s5)}
.contact-card{padding:var(--s5)}
.contact-card p{margin-bottom:0}
.contact-card .big{border-bottom:1.5px solid currentColor; padding-bottom:3px}
.ice .contact-card .tel,.paper .contact-card .tel{color:var(--navy); text-decoration:none}
.navy .contact-card .tel,.raised .contact-card .tel{color:var(--paper); text-decoration:none}

/* ── blog index ─────────────────────────────────────────────────────────── */
.post h2{font-size:clamp(20px,1.9vw,24px); font-weight:600; margin-bottom:12px}
.post h2 a{text-decoration:none; color:inherit}
.post h2 a:hover{text-decoration:underline}
.post time{display:block; margin-bottom:10px}
.posts.three{grid-template-columns:repeat(3,1fr)}
.posts.three h3{font-size:19px}

/* ── long-form: posts and legal ─────────────────────────────────────────────
   Satoshi at length on the light ground, at a 72-character measure set once and not improvised
   per page. This is the longest reading on the site — 5,502 words on the Privacy Policy alone. */
.doc-head{padding-block:var(--s7) var(--s4)}
.doc-head .doc h1{font-size:clamp(32px,4.2vw,52px)}
.doc-body{padding-top:var(--s5)}
.doc .ph{margin-bottom:var(--s6)}
.doc > h2:first-of-type{margin-top:var(--s5)}
.doc h2{scroll-margin-top:100px}
.doc h3{scroll-margin-top:100px}
.crumb a{color:inherit}
.crumb a:hover{text-decoration:underline}
.toc{margin-bottom:var(--s6)}
.toc ol{counter-reset:none}
.toc a{text-decoration:none; border-bottom:1px solid transparent}
.toc a:hover{border-bottom-color:currentColor}

/* ── responsive, for the components above ───────────────────────────────── */
@media (max-width:1080px){
  .cards{grid-template-columns:repeat(2,1fr)}
  .cards .card.svc:nth-child(4){grid-column:auto}
  .phases{grid-template-columns:1fr; gap:var(--s3)}
  .posts.three{grid-template-columns:repeat(2,1fr)}
}
@media (max-width:820px){
  .figure-row{grid-template-columns:1fr; gap:var(--s3)}
  .cards,.contact-grid,.posts.three{grid-template-columns:1fr}
  .hero .story{gap:var(--s3)}
  .doc-head{padding-block:var(--s6) var(--s3)}
}

/* ── tables inside long-form documents ──────────────────────────────────────
   The Privacy Policy carries three of them and the live page renders them as a run of loose
   lines. A table wider than its measure scrolls inside its own box; the page body never does. */
.tw{overflow-x:auto; margin:0 0 var(--s5); border:1px solid #D3DFEA; border-radius:10px;
  background:var(--paper)}
.doc table{border-collapse:collapse; width:100%; min-width:520px; color:var(--navy)}
.doc th,.doc td{text-align:left; vertical-align:top; padding:14px 18px; font-size:15px;
  line-height:1.55; border-bottom:1px solid #E2EAF2}
.doc th{font-family:var(--body); font-weight:700; font-size:11px; letter-spacing:.12em;
  text-transform:uppercase; color:var(--deep); background:var(--ice); border-bottom:1px solid #D3DFEA}
.doc td{color:var(--slate)}
.doc td:first-child{color:var(--navy); font-weight:700; width:34%}
.doc tbody tr:last-child :is(td,th){border-bottom:0}

/* ── the mobile disclosure ──────────────────────────────────────────────────
   Ships hidden and is revealed by the script that can also reopen the panel, so no-JS keeps a
   working nav. Above 820px the button is gone and the panel is the ordinary inline nav. */
.vh{position:absolute; width:1px; height:1px; margin:-1px; padding:0; overflow:hidden;
  clip:rect(0 0 0 0); clip-path:inset(50%); white-space:nowrap; border:0}
.nav-toggle{display:none; align-items:center; justify-content:center; width:44px; height:44px;
  background:transparent; border:1px solid #1B3145; border-radius:8px; cursor:pointer; padding:0}
.nav-toggle[hidden]{display:none}
.nav-bars{display:grid; gap:4px; width:18px}
.nav-bars i{display:block; height:2px; background:var(--paper); border-radius:2px}
.nav-toggle[aria-expanded="true"]{border-color:var(--signal)}
/* The panel scrolls if the viewport is short — a nav taller than the screen with no scroll is a
   nav whose last item cannot be reached. */
/* One owner for the panel CTA, defined last and scoped to the header, so a later `display` rule
   in another block cannot silently win and drop it out of flex layout. */
.site-head .nav-panel-cta{display:none}
/* The same ownership has to cover COLOR, and for the same reason. This button is an <a> sitting
   inside .site-head nav, so `.site-head nav a{color:var(--mist)}` — 0,2,1 — outranked
   `.primary{color:var(--navy)}` at 0,1,0 and painted the label mist on the green gradient. The
   chevron kept its navy because it is drawn, not typed, which is the tell: an arrow you can see
   beside words you cannot. Matched at 0,3,1 so the button owns its own foreground. */
.site-head nav a.nav-panel-cta,
.site-head nav a.nav-panel-cta span{color:var(--navy); font-weight:700}
@media (max-width:820px){
  html:not(.nvd) .nav-toggle:not([hidden]){display:inline-flex}
  html:not(.nvd) .site-head .nav-panel-cta{display:flex; width:100%; justify-content:center;
    margin:var(--s2) 0 var(--s3)}
  html:not(.nvd) #nav-panel{max-height:calc(100vh - 64px); overflow-y:auto; overflow-x:hidden}
  html.nav-open:not(.nvd), html.nav-open:not(.nvd) body{overflow:hidden}
  /* Locking the background scroll is right; doing it with overflow:hidden takes away the scroll
     container that position:sticky resolves against, so the header dropped to its static place at
     the top of the DOCUMENT while the viewport stayed where it was. Open the menu 3,000px down the
     page and it opened 3,000px above your screen — the menu was working perfectly and was simply
     nowhere near you. Fixed positioning is anchored to the viewport and does not care about the
     lock. */
  html.nav-open:not(.nvd) .site-head{position:fixed; top:0; left:0; right:0}
  /* Fixed takes the bar out of flow, so everything under it jumps up by exactly its height the
     moment the menu opens — measured at 65px, and very visible with the scroll locked. Put the
     height back as padding for as long as the menu is open. */
  html.nav-open:not(.nvd) body{padding-top:var(--head-h)}
}

/* ============================================================================
   ICONS — drawn on the mark's angle system, stroked with the brand ramp.
   The set is bespoke; nothing from the live site is reused.
   ========================================================================= */
.isprite{position:absolute; width:0; height:0; overflow:hidden}
.ico{
  width:30px; height:30px; flex:none; display:block;
  fill:none; stroke:url(#ig); stroke-width:1.7;
  stroke-linecap:round; stroke-linejoin:round;
  /* The ramp is painted per-icon in the symbol's own 0–24 space, so an icon at any size
     traverses the whole ramp rather than sampling a slice of a page-wide gradient. */
  overflow:visible;
}
/* Signal is 1.52:1 on ice. The accent's ground picks its direction — it always has. */
.ico.on-light{stroke:url(#igL)}
/* The accent is the part that carries the icon's meaning; it is what moves. */
.ico .a{stroke-width:1.9}

/* ── the four behaviors ────────────────────────────────────────────────────
   Assigned per icon in icons.mjs, so the thing that animates is the thing that means
   something — not the whole glyph wobbling. Triggered from the enclosing item, so the
   hover target is the row, not a 28px square. */
.ico *{transform-box:fill-box; transform-origin:center; transition:transform .32s cubic-bezier(.22,.8,.28,1)}
.ico .a{transition:transform .32s cubic-bezier(.22,.8,.28,1), stroke-dashoffset .5s ease}

:is(.ico-row,.card,.step,.svc,.phases li):hover .m-slide .a{transform:translateX(2.5px)}
:is(.ico-row,.card,.step,.svc,.phases li):hover .m-pop   .a{transform:scale(1.22)}
:is(.ico-row,.card,.step,.svc,.phases li):hover .m-sweep .a{transform:translateY(4px)}
.m-draw .a{stroke-dasharray:1; stroke-dashoffset:0; pathLength:1}
:is(.ico-row,.card,.step,.svc,.phases li):hover .m-draw .a{
  animation:ico-redraw .62s cubic-bezier(.22,.8,.28,1)}
@keyframes ico-redraw{ from{stroke-dashoffset:1} to{stroke-dashoffset:0} }

/* the whole glyph acknowledges the hover, quietly */
:is(.ico-row,.card,.step,.svc,.phases li):hover .ico{transform:translateY(-1px)}
.ico{transition:transform .32s cubic-bezier(.22,.8,.28,1)}

/* ── icon rows: the list form ─────────────────────────────────────────────── */
.ico-list{list-style:none; margin:var(--s5) 0 0; padding:0; display:grid; gap:var(--s2)}
.ico-list.two{grid-template-columns:repeat(2,1fr); gap:var(--s2) var(--s5)}
.ico-row{
  display:flex; gap:var(--s3); align-items:flex-start; padding:14px 16px 14px 0;
  border-top:1px solid; font-size:17px; line-height:1.45;
}
.navy .ico-row,.raised .ico-row{border-color:#1B3145; color:var(--mist)}
.ice .ico-row,.paper .ico-row{border-color:#D3DFEA; color:var(--slate)}
.ico-row:hover{border-color:var(--signal)}
.ice .ico-row:hover,.paper .ico-row:hover{border-color:var(--deep)}
.ico-row span{padding-top:3px}

@media (max-width:820px){ .ico-list.two{grid-template-columns:1fr} }

/* ── icons inside the existing components ─────────────────────────────────── */
.step .ico{margin-bottom:var(--s3)}
.phases li{display:grid; gap:10px}
.phases li .ico{margin-bottom:4px}
.svc-top{display:flex; align-items:center; justify-content:space-between; margin-bottom:var(--s3)}
.svc-top .num{margin-bottom:0}
.card.svc .num{margin-bottom:0}

/* ============================================================================
   MOTION

   Everything here is scoped to `html.anim`, set by an inline script before paint and ONLY when
   the reader has not asked for reduced motion. No class → no hidden state, no transform, no
   observer. A browser that never runs the script shows the finished page rather than a blank one,
   which is the failure mode of every reveal library that hides in CSS and shows in JS.
   ========================================================================= */

.anim [data-reveal] > .wrap > *,
.anim [data-reveal] > .wrap { will-change:opacity, transform }

/* The band itself rises; its children follow, staggered, so a section arrives as a phrase rather
   than as one slab. */
.anim [data-reveal] .wrap > *{
  opacity:0; transform:translateY(18px);
  transition:opacity .62s cubic-bezier(.22,.8,.28,1), transform .62s cubic-bezier(.22,.8,.28,1);
}
.anim [data-reveal].in .wrap > *{opacity:1; transform:none}
.anim [data-reveal].in .wrap > :nth-child(2){transition-delay:.07s}
.anim [data-reveal].in .wrap > :nth-child(3){transition-delay:.14s}
.anim [data-reveal].in .wrap > :nth-child(4){transition-delay:.20s}
.anim [data-reveal].in .wrap > :nth-child(n+5){transition-delay:.25s}

/* Lists and grids stagger their own items — the interest is in the sequence, not the block. */
.anim [data-reveal] :is(.ico-list,.steps,.cards,.people,.posts,.wall,.phases,.rule-list) > *{
  opacity:0; transform:translateY(14px);
  transition:opacity .5s cubic-bezier(.22,.8,.28,1), transform .5s cubic-bezier(.22,.8,.28,1);
}
.anim [data-reveal].in :is(.ico-list,.steps,.cards,.people,.posts,.wall,.phases,.rule-list) > *{
  opacity:1; transform:none;
}
.anim [data-reveal].in :is(.ico-list,.steps,.cards,.people,.posts,.wall,.phases,.rule-list) > :nth-child(1){transition-delay:.10s}
.anim [data-reveal].in :is(.ico-list,.steps,.cards,.people,.posts,.wall,.phases,.rule-list) > :nth-child(2){transition-delay:.16s}
.anim [data-reveal].in :is(.ico-list,.steps,.cards,.people,.posts,.wall,.phases,.rule-list) > :nth-child(3){transition-delay:.22s}
.anim [data-reveal].in :is(.ico-list,.steps,.cards,.people,.posts,.wall,.phases,.rule-list) > :nth-child(4){transition-delay:.28s}
.anim [data-reveal].in :is(.ico-list,.steps,.cards,.people,.posts,.wall,.phases,.rule-list) > :nth-child(5){transition-delay:.34s}
.anim [data-reveal].in :is(.ico-list,.steps,.cards,.people,.posts,.wall,.phases,.rule-list) > :nth-child(n+6){transition-delay:.40s}

/* The hero does not wait to be scrolled to — it is already there. */
.anim .hero .wrap > *{opacity:0; transform:translateY(20px); animation:rise .8s cubic-bezier(.22,.8,.28,1) forwards}
.anim .hero .wrap > :nth-child(1){animation-delay:.05s}
.anim .hero .wrap > :nth-child(2){animation-delay:.13s}
.anim .hero .wrap > :nth-child(3){animation-delay:.21s}
.anim .hero .wrap > :nth-child(4){animation-delay:.29s}
.anim .hero .wrap > :nth-child(n+5){animation-delay:.36s}
@keyframes rise{ to{opacity:1; transform:none} }
.anim .hero .sig{opacity:0; animation:sig-in 1.5s cubic-bezier(.22,.8,.28,1) .25s forwards; transition:none}
@keyframes sig-in{ from{opacity:0; transform:translateY(24px)} to{opacity:1} }

/* ── hover: the glow ────────────────────────────────────────────────────────
   Cards pick up a ramp-lit edge and a lift. The glow is the brand ramp at low opacity behind the
   card, not a generic drop shadow — so the thing that lights up is the thing the brand is made of. */
.card,.pmark,.post .ph,.post .shot,.contact-card{position:relative; transition:transform .34s cubic-bezier(.22,.8,.28,1), border-color .34s ease}
.card::after,.contact-card::after{
  content:""; position:absolute; inset:-1px; border-radius:inherit; z-index:-1;
  background:var(--ramp); opacity:0; transition:opacity .34s ease; filter:blur(14px);
}
.anim .card:hover,.anim .contact-card:hover{transform:translateY(-3px)}
.card:hover::after,.contact-card:hover::after{opacity:.28}
.navy .card:hover,.raised .card:hover{border-color:#2C4A66}
.ice .card:hover,.paper .card:hover{border-color:#B9CBDB}

.anim .pmark:hover{transform:translateY(-2px)}

/* Post cards: the image plate lifts, the title picks up the accent. */
.post{transition:transform .34s cubic-bezier(.22,.8,.28,1)}
.anim .post:hover{transform:translateY(-3px)}
.ice .post:hover :is(h2,h3) a,.paper .post:hover :is(h2,h3) a{color:var(--deep)}
.navy .post:hover :is(h2,h3) a,.raised .post:hover :is(h2,h3) a{color:var(--signal)}

/* Lateral links: the arrow travels, the rule fills. */
.lateral{position:relative; border-bottom-color:transparent;
  background-image:linear-gradient(currentColor,currentColor);
  background-repeat:no-repeat; background-position:0 100%; background-size:0% 1.5px;
  transition:background-size .34s cubic-bezier(.22,.8,.28,1)}
.lateral::after{transition:transform .34s cubic-bezier(.22,.8,.28,1); display:inline-block}
.lateral:hover{background-size:100% 1.5px}
.anim .lateral:hover::after{transform:translateX(4px)}

/* Buttons: the cascade advances. */
.btn .chev b{transition:transform .3s cubic-bezier(.22,.8,.28,1)}
.anim .btn:hover .chev b:nth-child(1){transform:translateX(3px)}
.anim .btn:hover .chev b:nth-child(2){transform:translateX(4.5px)}
.anim .btn:hover .chev b:nth-child(3){transform:translateX(6px)}

/* Steps: the wayfinding chevrons lean forward as the row is read. */
.anim .steps:hover .step .link .chev b{transform:translateX(2px)}
.step .link .chev b{transition:transform .3s cubic-bezier(.22,.8,.28,1)}

/* Nav: the current-page rule grows rather than appearing. */
.site-head nav a{position:relative; transition:color .24s ease}
.site-head nav a::after{content:""; position:absolute; left:0; right:0; bottom:-2px; height:2px;
  background:var(--signal); transform:scaleX(0); transform-origin:left;
  transition:transform .3s cubic-bezier(.22,.8,.28,1)}
.anim .site-head nav a:hover::after{transform:scaleX(1)}

@media (prefers-reduced-motion:reduce){
  /* Belt and braces: the class is never set, but if anything above is reached it does nothing. */
  [data-reveal] .wrap > *,[data-reveal] :is(.ico-list,.steps,.cards,.people,.posts,.wall,.phases,.rule-list) > *{
    opacity:1 !important; transform:none !important; transition:none !important; animation:none !important;
  }
  .hero .sig{opacity:1 !important; animation:none !important}
}

/* ============================================================================
   RHYTHM — breaking the sameness.

   Every band was eyebrow → heading → two columns, at one width, with one vertical rhythm. Read
   end to end that is a template, not a page: the eye learns the shape by the third section and
   stops looking. Four levers, applied deliberately rather than everywhere:

     .band-tight / .band-tall   vertical rhythm varies with the weight of what is in the band
     .wrap-narrow / .wrap-wide  the measure itself changes, so the page has a waist
     .band-center               one band per page addresses the reader head-on
     .offset                    a column steps down, so a two-up stops reading as a table

   The grounds already alternate. This makes the SHAPE alternate too.
   ========================================================================= */

.band-tight{padding-block:var(--s7)}
.band-tall{padding-block:calc(var(--s8) + var(--s7))}
.wrap-narrow > .wrap{width:min(880px,calc(100% - var(--s5)))}
.wrap-wide > .wrap{width:min(1400px,calc(100% - var(--s5)))}

.band-center{text-align:center}
.band-center .wrap > *{margin-inline:auto}
.band-center :is(p,.lead,.standfirst,h1,h2){max-width:34ch; margin-inline:auto}
.band-center .ladder,.band-center .laterals{justify-content:center}
.band-center .eyebrow{justify-content:center}

/* A stepped column: the second column starts lower, so the pair reads as a sequence rather than
   as two cells of a table. Only above the breakpoint — stacked, a step is just a gap. */
@media (min-width:1081px){
  /* `.offset` stepped the second column down to break the table feel. On The Problem it just read
     as misalignment — the heading and the list are a pair and should start on the same line. */
  .offset > div:last-child{margin-top:0}
  /* An asymmetric split, for sections where one side genuinely carries more. */
  .split-wide{grid-template-columns:minmax(0,1.35fr) minmax(0,.85fr)}
}

/* The hero gets a light behind the mark — Andrew's "glowy". It is the brand ramp, heavily blurred
   and clipped to the band, not a generic radial: the light in the corner is the same light the
   logo is made of. */
/* Full viewport. The band below was cresting into the fold and undercutting the headline — a hero
   that is nearly a screen tall reads as a mistake, where exactly one screen reads as a decision. */
/* One screen, and the content has to FIT one screen — pinning the height without sizing the type
   to it just moves the overflow below the fold, which is the problem it was meant to solve. The
   headline now shares its band with the graphic, so it takes the narrower column's measure. */
.hero{isolation:isolate; min-height:100svh; display:flex; align-items:center;
  padding-block:var(--s7)}
.hero-grid h1{font-size:clamp(36px,4.4vw,64px); line-height:1.0}
.hero-grid .standfirst{font-size:clamp(17px,1.25vw,19px); max-width:42ch; margin-top:var(--s3)}
.hero-grid .ladder{margin-top:var(--s5)}
.hero > .wrap{width:min(1180px,calc(100% - var(--s5)))}
.hero-grid{display:grid; grid-template-columns:minmax(0,1.05fr) minmax(0,.95fr);
  gap:var(--s7); align-items:center}
@media (max-width:1080px){
  /* One screen INCLUDING the header, not one screen after it. 100svh on its own made the hero a
     full screen that then began 77px down, so it overhung the fold by exactly the height of the
     bar and pushed its own content that much lower.
     Padding drops with it. At --s7 the hero carried 112px top and bottom on top of being a full
     screen tall, which on a 390px phone left 215px of nothing above the headline before a word
     was read. The content is flex-centered, so the padding only ever needed to be a floor for a
     headline too tall to center. */
  .hero{min-height:calc(100svh - var(--head-h)); padding-block:var(--s5)}
  .hero-grid{grid-template-columns:1fr; gap:var(--s5)}
  /* The graphic stays, below the words. Hiding it left the full-height hero with nothing in the
     space it had just been given, and it is the one picture on the page that explains the product
     rather than decorating it. */
  .hero-art{max-width:340px}
}
@supports not (height: 100svh){
  /* svh is the small viewport height — the one that does not change as mobile browser chrome
     hides on scroll. Without it, vh is measured against the LARGE viewport and the hero is taller
     than the screen it is meant to fill. */
  @media (max-width:1080px){ .hero{min-height:calc(100vh - var(--head-h))} }
}
.hero::before{
  content:""; position:absolute; z-index:-1; pointer-events:none;
  right:-8%; top:-30%; width:min(760px,72vw); aspect-ratio:1;
  background:var(--ramp); opacity:.14; filter:blur(120px); border-radius:50%;
}
.anim .hero::before{animation:aura 14s ease-in-out infinite alternate}
@keyframes aura{
  from{transform:translate3d(0,0,0) scale(1); opacity:.11}
  to  {transform:translate3d(-4%,4%,0) scale(1.12); opacity:.18}
}

/* A full-bleed hairline of the ramp, for a band edge that needs marking without a whole moment. */
.edge-ramp{position:relative}
.edge-ramp::after{content:""; position:absolute; left:0; right:0; bottom:0; height:2px;
  background:var(--ramp); opacity:.85}

@media (prefers-reduced-motion:reduce){ .hero::before{animation:none} }
@media (max-width:1080px){
  .band-tall{padding-block:var(--s8)}
  .wrap-wide > .wrap{width:min(1180px,calc(100% - var(--s4)))}
}

/* Prose in two columns — for a centered band where a single 34ch measure would run too long. */
.two-col-prose{columns:2; column-gap:var(--s6); max-width:none; margin-top:var(--s4)}
.two-col-prose p{max-width:none; break-inside:avoid}
.band-center .two-col-prose{text-align:left}
.band-center .rule-list{text-align:left; margin-top:var(--s6)}
.band-center .pull{max-width:20ch}
@media (max-width:820px){ .two-col-prose{columns:1} }

/* ── the index row ──────────────────────────────────────────────────────────
   From the reference audit §2. A list of statements set as display type, one per full-width row,
   hairline-separated, ordinal far left, icon far right. Roughly 4× scale contrast inside one row.
   Only for items with no body copy — a row with a paragraph under it is a card. */
.index-list{list-style:none; margin:var(--s6) 0 0; padding:0; counter-reset:none}
.index-row{
  display:grid; grid-template-columns:auto 1fr auto; align-items:center; gap:var(--s5);
  padding:var(--s4) 0; border-top:1px solid;
  transition:padding-left .7s cubic-bezier(.19,1,.22,1), border-color .35s ease;
}
.index-list li:last-child{border-bottom:1px solid}
.navy .index-row,.raised .index-row{border-color:#1B3145}
.ice .index-row,.paper .index-row{border-color:#D3DFEA}
.navy .index-list li:last-child,.raised .index-list li:last-child{border-bottom-color:#1B3145}
.ice .index-list li:last-child,.paper .index-list li:last-child{border-bottom-color:#D3DFEA}

.ix-n{font-family:var(--body); font-weight:700; font-size:12px; letter-spacing:.16em;
  font-variant-numeric:tabular-nums; padding-top:.4em}
.navy .ix-n,.raised .ix-n{color:var(--mist)}
.ice .ix-n,.paper .ix-n{color:var(--slate)}
.ix-t{font-family:var(--display); font-weight:600; font-size:clamp(21px,2.6vw,34px);
  line-height:1.08; letter-spacing:-.018em}
.navy .ix-t,.raised .ix-t{color:var(--paper)}
.ice .ix-t,.paper .ix-t{color:var(--navy)}
.index-row .ico{width:34px; height:34px}

/* The row acknowledges the cursor by stepping in — the reference's 0.7s, not a 0.3s twitch. */
.anim .index-row:hover{padding-left:var(--s4)}
.navy .index-row:hover,.raised .index-row:hover{border-color:var(--signal)}
.ice .index-row:hover,.paper .index-row:hover{border-color:var(--deep)}

@media (max-width:820px){
  .index-row{grid-template-columns:auto 1fr; gap:var(--s3); padding:var(--s3) 0}
  .index-row .ico{display:none}
  .anim .index-row:hover{padding-left:var(--s2)}
}

/* ── the heading assembles ─────────────────────────────────────────────────
   Reference audit §3, at word level rather than character level — see the note in lib.mjs for why.
   One inline-block per word, each rising on a stagger, and each answering the cursor. */
.anim .wsplit .word{display:inline-block; white-space:nowrap; vertical-align:baseline;
  opacity:0; transform:translateY(.34em);
  animation:word-in .82s cubic-bezier(.19,1,.22,1) forwards;
  transition:transform .5s cubic-bezier(.19,1,.22,1), color .4s ease}
@keyframes word-in{ to{opacity:1; transform:none} }
.anim .wsplit .word:hover{color:var(--signal)}
.ice .anim .wsplit .word:hover,.paper .anim .wsplit .word:hover{color:var(--deep)}

/* ── the index row ──────────────────────────────────────────────────────────
   From the reference audit §2. A list of statements set as display type, one per full-width row,
   hairline-separated, ordinal far left, icon far right. Roughly 4× scale contrast inside one row.
   Only for items with no body copy — a row with a paragraph under it is a card. */
.index-list{list-style:none; margin:var(--s6) 0 0; padding:0; counter-reset:none}
.index-row{
  display:grid; grid-template-columns:auto 1fr auto; align-items:center; gap:var(--s5);
  padding:var(--s4) 0; border-top:1px solid;
  transition:padding-left .7s cubic-bezier(.19,1,.22,1), border-color .35s ease;
}
.index-list li:last-child{border-bottom:1px solid}
.navy .index-row,.raised .index-row{border-color:#1B3145}
.ice .index-row,.paper .index-row{border-color:#D3DFEA}
.navy .index-list li:last-child,.raised .index-list li:last-child{border-bottom-color:#1B3145}
.ice .index-list li:last-child,.paper .index-list li:last-child{border-bottom-color:#D3DFEA}

.ix-n{font-family:var(--body); font-weight:700; font-size:12px; letter-spacing:.16em;
  font-variant-numeric:tabular-nums; padding-top:.4em}
.navy .ix-n,.raised .ix-n{color:var(--mist)}
.ice .ix-n,.paper .ix-n{color:var(--slate)}
.ix-t{font-family:var(--display); font-weight:600; font-size:clamp(21px,2.6vw,34px);
  line-height:1.08; letter-spacing:-.018em}
.navy .ix-t,.raised .ix-t{color:var(--paper)}
.ice .ix-t,.paper .ix-t{color:var(--navy)}
.index-row .ico{width:34px; height:34px}

/* The row acknowledges the cursor by stepping in — the reference's 0.7s, not a 0.3s twitch. */
.anim .index-row:hover{padding-left:var(--s4)}
.navy .index-row:hover,.raised .index-row:hover{border-color:var(--signal)}
.ice .index-row:hover,.paper .index-row:hover{border-color:var(--deep)}

@media (max-width:820px){
  .index-row{grid-template-columns:auto 1fr; gap:var(--s3); padding:var(--s3) 0}
  .index-row .ico{display:none}
  .anim .index-row:hover{padding-left:var(--s2)}
}

/* ── 1 · the heading unmask ─────────────────────────────────────────────────
   A fade says "here is a heading". A mask sliding off it says "you have arrived somewhere". The
   clip is on a wrapper so the type itself is never transformed — text scaled or translated during
   a transition is text rendered at a fraction of its real quality. */
.anim [data-reveal] :is(h2,.pull,.ix-t,.rule-single){
  clip-path:inset(0 0 108% 0);
  transition:clip-path .95s cubic-bezier(.19,1,.22,1);
}
.anim [data-reveal].in :is(h2,.pull,.ix-t,.rule-single){clip-path:inset(0 0 -12% 0)}
/* The h1 is handled per character instead — see the split. */
.anim [data-reveal] h1{clip-path:none}

/* ── 2 · hairlines draw ─────────────────────────────────────────────────────
   Borders cannot be animated from nothing, so the rule is a pseudo-element that scales from its
   left edge. It reads as the section drawing its own structure as you arrive. */
.index-row{position:relative; border-top-color:transparent !important}
.index-row::before{
  content:""; position:absolute; left:0; right:0; top:0; height:1px; background:currentColor;
  opacity:.22; transform:scaleX(0); transform-origin:left;
  transition:transform 1.1s cubic-bezier(.19,1,.22,1);
}
/* The closing hairline is the last row's own border-bottom, left static. It was drawn by a
   pseudo-element twice over: first on li:last-child::after, where it fought the hover fill for
   the same pseudo; then on the list's ::after, where it underlined the text mid-glyph, because
   the last row's box extends below the list's. A border needs no pseudo, cannot collide with
   the fill, and sits exactly on the row's real bottom edge. */
[data-reveal].in .index-row::before{transform:scaleX(1)}
.anim [data-reveal]:not(.in) .index-row::before{transform:scaleX(0)}
.index-row:nth-child(2)::before{transition-delay:.06s}
.index-row:nth-child(3)::before{transition-delay:.12s}
.index-row:nth-child(4)::before{transition-delay:.18s}
.index-row:nth-child(5)::before{transition-delay:.24s}
.index-row:nth-child(n+6)::before{transition-delay:.30s}

.rule-list li{position:relative; border-top-color:transparent !important}
.rule-list li::before{
  content:""; position:absolute; left:0; right:0; top:0; height:2px; background:currentColor;
  transform:scaleX(0); transform-origin:left;
  transition:transform 1s cubic-bezier(.19,1,.22,1);
}
.navy .rule-list li::before,.raised .rule-list li::before{background:var(--blue)}
.ice .rule-list li::before,.paper .rule-list li::before{background:var(--deep)}
[data-reveal].in .rule-list li::before{transform:scaleX(1)}
.rule-list li:nth-child(2)::before{transition-delay:.1s}
.rule-list li:nth-child(3)::before{transition-delay:.2s}

/* ── 3 · further, and slower ────────────────────────────────────────────────
   The old reveal moved 14–18px in .5–.62s, which is a twitch. The reference travels further and
   takes 0.7s+; that is what reads as deliberate rather than nervous. */
.anim [data-reveal] .wrap > *{transform:translateY(38px);
  transition:opacity .85s cubic-bezier(.19,1,.22,1), transform .85s cubic-bezier(.19,1,.22,1)}
.anim [data-reveal] :is(.ico-list,.steps,.cards,.people,.posts,.wall,.phases,.index-list) > *{
  transform:translateY(30px);
  transition:opacity .72s cubic-bezier(.19,1,.22,1), transform .72s cubic-bezier(.19,1,.22,1);
}
.anim [data-reveal].in .wrap > :nth-child(2){transition-delay:.10s}
.anim [data-reveal].in .wrap > :nth-child(3){transition-delay:.19s}
.anim [data-reveal].in .wrap > :nth-child(4){transition-delay:.27s}

/* ── 4 · letters answer the cursor ──────────────────────────────────────────
   The h1 is already split per character for its entrance. The same spans make each letter a hover
   target: it lifts, takes the accent, and settles back. Neighbours are nudged by the script, so
   the whole word responds rather than one letter twitching in isolation. */
/* the hover lives on .word — see "the heading assembles" above */

/* ── icons: draw themselves in, and answer harder ───────────────────────── */
.anim [data-reveal] .ico path{stroke-dasharray:1; stroke-dashoffset:1; pathLength:1}
.anim [data-reveal].in .ico path{animation:ico-draw 1.1s cubic-bezier(.19,1,.22,1) forwards}
.anim [data-reveal].in .ico path:nth-child(2){animation-delay:.09s}
.anim [data-reveal].in .ico path:nth-child(3){animation-delay:.17s}
.anim [data-reveal].in .ico path:nth-child(4){animation-delay:.24s}
@keyframes ico-draw{ to{stroke-dashoffset:0} }

/* hover, roughly doubled */
:is(.ico-row,.card,.step,.svc,.phases li,.index-row):hover .m-slide .a{transform:translateX(5px)}
:is(.ico-row,.card,.step,.svc,.phases li,.index-row):hover .m-pop   .a{transform:scale(1.42)}
:is(.ico-row,.card,.step,.svc,.phases li,.index-row):hover .m-sweep .a{transform:translateY(7px)}
:is(.ico-row,.card,.step,.svc,.phases li,.index-row):hover .m-draw  .a{animation:ico-redraw .72s cubic-bezier(.19,1,.22,1)}
:is(.ico-row,.card,.step,.svc,.phases li,.index-row):hover .ico{transform:translateY(-3px) scale(1.06)}
.ico,.ico *{transition:transform .42s cubic-bezier(.19,1,.22,1)}

@media (prefers-reduced-motion:reduce){
  [data-reveal] :is(h1,h2,.pull,.ix-t,.rule-single){clip-path:none !important}
  .index-row::before,.rule-list li::before{transform:none !important}
  .ico path{stroke-dashoffset:0 !important; animation:none !important}
}

/* ============================================================================
   THE CURSOR, AND THE GLOW
   ========================================================================= */


/* ── buttons glow ───────────────────────────────────────────────────────────
   The glow is the brand ramp behind the button, not a gray drop shadow — the light a button gives
   off should be made of the same thing the brand is. */
.btn{position:relative; isolation:isolate}
.btn::before{
  content:""; position:absolute; inset:-3px; z-index:-1; border-radius:inherit;
  background:var(--ramp); opacity:0; filter:blur(16px);
  transition:opacity .42s ease, inset .42s cubic-bezier(.19,1,.22,1);
}
.btn:hover::before{opacity:.75; inset:-9px}
.btn.secondary::before{opacity:0}
.btn.secondary:hover::before{opacity:.34; inset:-7px}
/* the fill sweeps rather than switches */
.btn.primary{background-image:linear-gradient(118deg,var(--signal),var(--bridge) 62%,var(--signal));
  background-size:220% 100%; background-position:0 0;
  transition:background-position .7s cubic-bezier(.19,1,.22,1), transform .34s cubic-bezier(.19,1,.22,1)}
.anim .btn.primary:hover{background-position:100% 0}
.btn.primary.on-light{background-image:none}

/* ============================================================================
   THE VERTICAL TIMELINE — "How This Works" / "How Safeguard Works"

   Four stages of one process, stacked, with a spine down the left that fills as you scroll past.
   Sideways was a misread: a horizontal track is a gallery, and this is a sequence you read top to
   bottom like everything else on the page.

   The fill is scroll-linked. Without JavaScript the spine is simply drawn in full and the steps
   are an ordinary stacked list — nothing here depends on the motion.
   ========================================================================= */
.tl{list-style:none; margin:var(--s6) 0 0; padding:0; position:relative; max-width:820px}
.tl-spine{position:absolute; left:23px; top:12px; bottom:12px; width:2px; background:currentColor;
  opacity:.16}
.tl-spine i{position:absolute; left:0; right:0; top:0; height:var(--tl-p,0%); display:block;
  background:var(--signal)}
.ice .tl-spine i,.paper .tl-spine i{background:var(--deep)}

.tl-step{position:relative; display:grid; grid-template-columns:48px 1fr; gap:var(--s5);
  padding-block:var(--s5)}
.tl-step:first-child{padding-top:0}
.tl-step:last-child{padding-bottom:0}
.tl-dot{position:relative; z-index:1; width:48px; height:48px; border-radius:50%;
  display:grid; place-items:center; border:1px solid;
  transition:border-color .5s ease, background-color .5s ease, transform .5s cubic-bezier(.19,1,.22,1)}
.navy .tl-dot,.raised .tl-dot{background:var(--navy); border-color:#1B3145}
.ice .tl-dot,.paper .tl-dot{background:var(--ice); border-color:#D3DFEA}
.paper .tl-dot{background:var(--paper)}
.tl-dot .ico{width:24px; height:24px}
/* the step the spine has reached lights up */
.tl-step.lit .tl-dot{border-color:var(--signal); transform:scale(1.08)}
.ice .tl-step.lit .tl-dot,.paper .tl-step.lit .tl-dot{border-color:var(--deep)}

.tl-body{padding-top:2px}
.tl-body .num{display:block; margin-bottom:var(--s2)}
.tl-body h3{margin-bottom:var(--s2)}
.tl-body p{max-width:48ch; margin:0}

@media (max-width:820px){
  .tl-step{grid-template-columns:40px 1fr; gap:var(--s3); padding-block:var(--s5)}
  .tl-dot{width:40px; height:40px}
  .tl-spine{left:19px}
}
@media (prefers-reduced-motion:reduce){
  .tl-spine i{height:100% !important}
}

/* ============================================================================
   THE CURSOR, AND THE GLOW
   ========================================================================= */

/* ── the cursor ─────────────────────────────────────────────────────────────
   A ring that follows the pointer with lag, swells over anything interactive, and takes a label
   where one is useful. Pointer-fine only — a touch device has no cursor to replace, and a stylus
   or a switch user should never lose the real one. The native cursor is NOT hidden: hiding it is
   how these break, because the moment the script stalls the page has no pointer at all. */
.cursor{
  position:fixed; top:0; left:0; z-index:80; pointer-events:none;
  width:34px; height:34px; margin:-17px 0 0 -17px; border-radius:50%;
  border:1.5px solid var(--signal); opacity:0;
  transition:width .34s cubic-bezier(.19,1,.22,1), height .34s cubic-bezier(.19,1,.22,1),
             margin .34s cubic-bezier(.19,1,.22,1), opacity .3s ease,
             background-color .34s ease, border-color .34s ease;
  display:grid; place-items:center;
}
.cursor.on{opacity:.9}
.cursor b{font-family:var(--body); font-weight:700; font-size:10px; letter-spacing:.14em;
  text-transform:uppercase; color:var(--navy); opacity:0; transition:opacity .24s ease;
  white-space:nowrap}
/* over something interactive it swells and fills */
.cursor.hot{width:76px; height:76px; margin:-38px 0 0 -38px; background:var(--signal);
  border-color:var(--signal)}
.cursor.hot b{opacity:1}
/* on light grounds the ring has to be the ink that works there */
.cursor.light{border-color:var(--deep)}
.cursor.light.hot{background:var(--deep); border-color:var(--deep)}
.cursor.light.hot b{color:var(--paper)}
@media (hover:none),(pointer:coarse){ .cursor{display:none} }
@media (prefers-reduced-motion:reduce){ .cursor{display:none} }

/* ── buttons glow ───────────────────────────────────────────────────────────
   The glow is the brand ramp behind the button, not a gray drop shadow — the light a button gives
   off should be made of the same thing the brand is. */
.btn{position:relative; isolation:isolate}
.btn::before{
  content:""; position:absolute; inset:-3px; z-index:-1; border-radius:inherit;
  background:var(--ramp); opacity:0; filter:blur(16px);
  transition:opacity .42s ease, inset .42s cubic-bezier(.19,1,.22,1);
}
.btn:hover::before{opacity:.75; inset:-9px}
.btn.secondary::before{opacity:0}
.btn.secondary:hover::before{opacity:.34; inset:-7px}
/* the fill sweeps rather than switches */
.btn.primary{background-image:linear-gradient(118deg,var(--signal),var(--bridge) 62%,var(--signal));
  background-size:220% 100%; background-position:0 0;
  transition:background-position .7s cubic-bezier(.19,1,.22,1), transform .34s cubic-bezier(.19,1,.22,1)}
.anim .btn.primary:hover{background-position:100% 0}
.btn.primary.on-light{background-image:none}

/* ============================================================================
   THE HORIZONTAL TIMELINE — "How This Works" / "How Safeguard Works"

   The section pins to the viewport and the four steps travel sideways as you scroll down. It is
   the right shape for the content: these are four stages of one process, and a horizontal track
   says "sequence" in a way a 2×2 grid never can.

   It is built so the fallback IS the design, not a degraded version of it. With no JavaScript, or
   under reduced motion, or on a narrow screen, `.track` is an ordinary flex row that scrolls with
   the finger and the section is a normal band. Nothing is hidden and nothing is pinned; the
   sideways reading is simply done by hand instead of by the scroll.
   ========================================================================= */
.rail{position:relative}
.track{
  /* it is an <ol> because it is an ordered process — the markers are carried by the 01–04 labels,
     so the browser's own numbering is turned off rather than shown alongside them */
  list-style:none; padding-left:0;
  display:flex; gap:var(--s5); margin-top:var(--s6); padding-bottom:var(--s3);
  overflow-x:auto; scroll-snap-type:x proximity; -webkit-overflow-scrolling:touch;
}
.track > *{flex:0 0 min(420px,78vw); scroll-snap-align:start}
.track::-webkit-scrollbar{height:4px}
.track::-webkit-scrollbar-thumb{background:#1B3145; border-radius:4px}
.ice .track::-webkit-scrollbar-thumb,.paper .track::-webkit-scrollbar-thumb{background:#D3DFEA}

/* the connecting line the steps travel along */
.track .step{position:relative; padding-top:var(--s5)}
.track .step::before{
  content:""; position:absolute; left:0; right:calc(var(--s5) * -1); top:14px; height:2px;
  background:currentColor; opacity:.16;
}
.track .step:last-child::before{right:0}
.track .step::after{
  content:""; position:absolute; left:0; top:9px; width:12px; height:12px; border-radius:50%;
  background:var(--signal);
}
.ice .track .step::after,.paper .track .step::after{background:var(--deep)}

/* ── pinned mode — added by script only when it can be done properly ──────── */
.rail.pinned{height:var(--rail-h)}
.rail.pinned .rail-in{
  position:sticky; top:0; height:100vh; display:flex; flex-direction:column;
  justify-content:center; overflow:hidden;
}
.rail.pinned .track{
  overflow:visible; margin-top:var(--s5); padding-bottom:0;
  transform:translate3d(calc(var(--rail-x) * -1),0,0);
  transition:none; scroll-snap-type:none;
}
.rail.pinned .track > *{flex:0 0 clamp(320px,30vw,460px)}
/* a progress rule across the foot of the pinned section */
.rail.pinned .rail-bar{position:relative; height:2px; margin-top:var(--s6); background:currentColor;
  opacity:.14}
.rail.pinned .rail-bar i{position:absolute; inset:0 auto 0 0; width:var(--rail-p); background:var(--signal)}
.ice .rail.pinned .rail-bar i,.paper .rail.pinned .rail-bar i{background:var(--deep)}
.rail:not(.pinned) .rail-bar{display:none}

@media (prefers-reduced-motion:reduce){
  .rail.pinned{height:auto !important}
  .rail.pinned .rail-in{position:static; height:auto}
  .rail.pinned .track{transform:none; overflow-x:auto}
}

/* `position: sticky` does not work inside an ancestor with `overflow: hidden` — the band clips its
   own corner signature, which quietly disabled the pin and left a 1,420px empty section. The band
   that carries a pinned rail opts out of the clip. */
.band:has(.rail.pinned){overflow:visible}
.band:has(.rail.pinned) > .wrap{overflow:visible}

/* ── the one-pager page ─────────────────────────────────────────────────────
   The number the website does not currently carry, given a band of its own. */
.bignum{font-family:var(--display); font-weight:700; font-size:clamp(96px,17vw,240px);
  line-height:.82; letter-spacing:-.04em; margin:0; font-variant-numeric:tabular-nums;
  display:flex; align-items:baseline; justify-content:center; gap:.12em}
.navy .bignum,.raised .bignum{color:var(--signal)}
.ice .bignum,.paper .bignum{color:var(--deep)}
.bignum small{font-size:.18em; font-weight:600; letter-spacing:-.01em}
.bignum-note{font-family:var(--display); font-weight:600; font-size:clamp(22px,3vw,40px);
  letter-spacing:-.022em; line-height:1.05; margin-top:var(--s3); max-width:none}
.navy .bignum-note,.raised .bignum-note{color:var(--paper)}
.ramp-note{margin-top:var(--s4); color:var(--navy); max-width:52ch; font-size:clamp(17px,1.5vw,20px)}
.band-center .ramp-note{margin-inline:auto}

/* ── the strip ──────────────────────────────────────────────────────────────
   Not every section needs a room. "Safeguard is built into what we deliver" is a single statement
   with one supporting paragraph — it earns a band the height of its content and no more. */
.band.strip{padding-block:var(--s7)}
.band.strip .split{gap:var(--s7); align-items:center}
.band.strip h2{font-size:clamp(26px,3.2vw,40px); line-height:1.06}
.band.strip p{font-size:17px; line-height:1.6}
.band.strip .laterals{margin-top:var(--s3)}

/* ── 5 · the index row fills with the ramp on hover ──────────────────────────
   The whole strip lights, and the ink goes to paper so it stays legible on the gradient at every
   stop — navy would be right for type ON the ramp, but this is type OVER a ramp that has just
   arrived under it, and paper is what holds against the blue end. */
.index-row{overflow:hidden}
.index-row::after{
  content:""; position:absolute; inset:0; z-index:-1; background:var(--ramp);
  transform:scaleX(0); transform-origin:left;
  /* Faster than the ink, and fast-start: the fill must be UNDER the text before the text darkens.
     Independently-timed, the ink used to arrive first and sit navy on the navy band for a beat. */
  transition:transform .5s cubic-bezier(.19,1,.22,1);
}
.index-row{z-index:0; padding-inline:var(--s3); margin-inline:calc(var(--s3) * -1)}
.index-row:hover::after{transform:scaleX(1)}
.index-row:hover .ix-t,.index-row:hover .ix-n{color:var(--navy)}
.index-row:hover .ico{stroke:var(--navy)}
.index-row .ix-t,.index-row .ix-n,.index-row .ico{transition:color .3s ease .08s, stroke .3s ease .08s}
/* The reveal system declares `transition: clip-path …` on .ix-t at (0,3,0), and a transition
   SHORTHAND resets every other transition on the element — so the ink flip above never animated
   inside a reveal band: it snapped to navy in one frame. This rule outranks it at (0,4,0) and
   carries both jobs, so the title can still unclip on arrival AND ease its ink on hover. */
.anim [data-reveal] .index-row .ix-t,
.anim [data-reveal] .index-row .ix-n,
.anim [data-reveal] .index-row .ico{
  transition:clip-path .95s cubic-bezier(.19,1,.22,1), color .3s ease .08s, stroke .3s ease .08s}
/* the list itself sits back a size — the section heading carries the volume, not every row */
.ix-t{font-size:clamp(19px,1.9vw,26px) !important; line-height:1.2 !important}
.index-row{padding-block:var(--s3)}

/* ── the hero graphic ───────────────────────────────────────────────────────
   An escalation path on the mark's own 60° lattice: an alert arrives at the edge, is detected,
   triaged, escalated, answered. The route is lit and the standing mesh is not, so the eye is
   told where to look, and the nodes lean toward the pointer. */
.hero-art{position:relative; aspect-ratio:1; width:100%; max-width:540px; margin-inline:auto}
.hero-art svg{width:100%; height:100%; overflow:visible}

/* The standing mesh — present, and subordinate. At #1B3145 on .35 it was invisible against navy,
   so the diagram read as seven loose dots rather than as a system with a route through it. */
.ha-edges line{stroke:#2C4E68; stroke-width:.45}

/* The route stays legible between pulses. Without a resting rail the path only existed while the
   dash was on it, which is half the reason the graphic looked empty. */
.ha-rail{fill:none; stroke:url(#haG); stroke-width:.6; opacity:.3;
  stroke-linecap:round; stroke-linejoin:round}

/* Two dashes share the route: a dim trail and a bright head. Both patterns sum to exactly 100,
   which is the polyline's pathLength — so each wraps seamlessly and there is ALWAYS one dash in
   flight. The first version ran a 22-unit dash against a 210-unit gap on a 108-unit path: for
   roughly half of every loop nothing at all was moving. */
.ha-trail,.ha-head{fill:none; stroke:url(#haG); stroke-linecap:round; stroke-linejoin:round}
.ha-trail{stroke-width:.6; opacity:.5; stroke-dasharray:26 74; stroke-dashoffset:100}
.ha-head{stroke-width:.95; stroke-dasharray:4 96; stroke-dashoffset:100;
  filter:drop-shadow(0 0 1.6px rgba(0,224,164,.85))}
/* Two signals, running opposite ways and at different speeds.
   One pulse alone reads as a loading bar — it arrives, it stops, it starts again. Traffic in both
   directions is what the diagram is actually describing: an alert travelling up to be answered,
   and the answer coming back down. The periods are deliberately not multiples of each other
   (4.6s against 6.1s), so the two never settle into lockstep and the graphic never looks like it
   is repeating. */
.anim .ha-trail,.anim .ha-head{animation:ha-run 4.6s linear infinite}
.anim .ha-back{animation:ha-run-back 6.1s linear infinite}
@keyframes ha-run{ from{stroke-dashoffset:100} to{stroke-dashoffset:0} }
@keyframes ha-run-back{ from{stroke-dashoffset:0} to{stroke-dashoffset:100} }

/* The return signal: dimmer and thinner than the outbound one, so the route still reads in one
   direction and this sits underneath it as an answer rather than competing for the same line. */
.ha-back{fill:none; stroke:url(#haG); stroke-linecap:round; stroke-linejoin:round;
  stroke-width:.5; opacity:.34; stroke-dasharray:3 97; stroke-dashoffset:0}

/* Nodes: an entry, three transits, a terminus, and the standing work. Uniform dots were the
   other half of the problem — nothing marked where the story began or ended. */
.ha-dot{fill:var(--signal)}
.ha-n.entry .ha-dot{fill:none; stroke:var(--blue); stroke-width:.62}
.ha-n.mid   .ha-dot{fill:var(--bridge)}
.ha-n.term  .ha-dot{fill:var(--signal); filter:drop-shadow(0 0 2.4px rgba(0,224,164,.7))}
.ha-n.ctx   .ha-dot{fill:#4A7391}

/* Only the two ends breathe. Seven pulsing halos was noise. */
.ha-halo{fill:none; stroke-width:.3; opacity:.32; stroke:var(--signal)}
.ha-n.entry .ha-halo{stroke:var(--blue)}
.anim .ha-n.entry .ha-halo{animation:ha-pulse-e 3.6s ease-in-out infinite}
.anim .ha-n.term  .ha-halo{animation:ha-pulse-t 3.6s ease-in-out infinite; animation-delay:.9s}
@keyframes ha-pulse-e{ 0%,100%{r:5.1; opacity:.34} 50%{r:7.4; opacity:.05} }
@keyframes ha-pulse-t{ 0%,100%{r:5.5; opacity:.40} 50%{r:8.6; opacity:.05} }

/* Micro-labels. Every one is a word the live site already uses — this introduces no new copy.
   Mist on navy is 8.5:1; mist is the DARK-ground secondary and the hero is the only ground it
   appears on here. */
.ha-lb{font-family:var(--body); font-weight:700; font-size:2.5px; letter-spacing:.15em;
  text-transform:uppercase; fill:var(--mist)}
.ha-n.term .ha-lb{fill:var(--signal)}
.ha-n.ctx  .ha-lb{fill:#6E8CA4}

.ha-n{transition:transform .5s cubic-bezier(.19,1,.22,1)}
@media (prefers-reduced-motion:reduce){
  .ha-trail,.ha-head{stroke-dasharray:none; stroke-dashoffset:0; animation:none}
  .ha-back{display:none}
  .ha-halo{animation:none}
}

/* ── 6 · the timeline down the middle, steps alternating ────────────────────
   A spine down the center with the steps taking turns either side. It reads as a sequence with
   two hands rather than a list with a rule beside it — and each side gets the full measure.
   Below the breakpoint it collapses to the single left-hand spine, because alternating sides in
   a 375px column is just a zigzag. */
@media (min-width:1081px){
  .tl{max-width:none}
  .tl-spine{left:50%; margin-left:-1px}
  .tl-step{grid-template-columns:1fr 96px 1fr; gap:0; align-items:center; padding-block:var(--s5)}
  .tl-step .tl-dot{grid-column:2; justify-self:center}
  .tl-step.left  .tl-body{grid-column:1; grid-row:1; text-align:right; padding-right:var(--s5)}
  .tl-step.right .tl-body{grid-column:3; grid-row:1; text-align:left;  padding-left:var(--s5)}
  .tl-step.left  .tl-body p{margin-left:auto}
  .tl-body p{max-width:38ch}
  /* the spine glows as the fill passes */
  .tl-spine i{box-shadow:0 0 14px 2px rgba(0,224,164,.55)}
  .ice .tl-spine i,.paper .tl-spine i{box-shadow:0 0 14px 2px rgba(0,121,90,.4)}
  /* the icon draws itself in as its step lights */
  .tl-step .ico path{stroke-dasharray:1; stroke-dashoffset:1}
  .tl-step.lit .ico path{animation:ico-draw .8s cubic-bezier(.19,1,.22,1) forwards}
  .tl-step.lit .ico path:nth-child(2){animation-delay:.08s}
  .tl-step.lit .ico path:nth-child(3){animation-delay:.15s}
  .tl-step.lit .tl-dot{box-shadow:0 0 22px 3px rgba(0,224,164,.30)}
  .ice .tl-step.lit .tl-dot,.paper .tl-step.lit .tl-dot{box-shadow:0 0 22px 3px rgba(0,121,90,.22)}
}
@media (prefers-reduced-motion:reduce){
  .tl-step .ico path{stroke-dashoffset:0 !important; animation:none !important}
}

/* ── 7 · the guarantee band breathes at the foot ────────────────────────────
   The copy sat centered in the band and read as floating. More room below than above lifts it and
   gives the gradient somewhere to run out. */
.ramp{padding-block:var(--s7) var(--s8)}

/* ── 8 · the closing statement arrives a line at a time ─────────────────────
   The last thing on the page is the only thing on its band, so it can afford to be read to you.
   Clipped per line and unmasked in sequence — the same device as the section headings, given the
   room to be noticed. */
/* [data-reveal].close, not [data-reveal] .close. The band carries BOTH on the same element —
   <section class="band navy close ..." data-reveal> — so the descendant combinator that was here
   asked for a .close INSIDE a [data-reveal] and never matched anything. The whole block was dead:
   the per-line reveal never ran, and worse, .ln never became a block, so the line spans the script
   builds ran together inline and ate the space at every line break. "your environment" rendered as
   "yourenvironment" on every page with a closing statement. */
/* A close band with no closing statement. The blog page has none of its own and deliberately
   does not borrow one, but it kept the padding of a band built to hold a headline — so two
   buttons sat alone in most of a screen and read as content that had failed to load. */
/* The scheduler frame on /book-a-call/. Only rendered when a share link is configured, so this
   sits unused until one is. Sized generously because every scheduler is a different height and a
   short frame turns into a nested scrollbar, which is the worst version of a booking form. */
.sched{border:1px solid var(--line); border-radius:12px; overflow:hidden; background:#fff}
.sched iframe{display:block; width:100%; height:min(78vh,860px); border:0}

.close.bare{padding-block:var(--s6)}
.close.bare .sig{display:none}

.anim [data-reveal].close .pull{clip-path:none}
.anim [data-reveal].close .pull .ln{display:block; clip-path:inset(0 0 108% 0);
  transition:clip-path 1s cubic-bezier(.19,1,.22,1)}
.anim [data-reveal].in.close .pull .ln{clip-path:inset(0 0 -14% 0)}
.anim [data-reveal].in.close .pull .ln:nth-child(2){transition-delay:.13s}
.anim [data-reveal].in.close .pull .ln:nth-child(3){transition-delay:.26s}
.anim [data-reveal].in.close .pull .ln:nth-child(n+4){transition-delay:.39s}
