/* ══════════════════════════════════════════════════════════════
   Kashi Media — design system v2
   Editorial type · cinematic scroll · diagnostic mechanic.
   Replaces site.css. Every page built from these primitives.
   ══════════════════════════════════════════════════════════════ */

@font-face{
  font-family:'Fraunces';
  src:url('fonts/fraunces-latin-wght-normal.woff2') format('woff2-variations');
  font-weight:100 900; font-style:normal; font-display:swap;
}

:root{
  --ink:#12100E;
  --ink2:#1C1916;
  --ink3:#262119;
  --paper:#F2EFE9;
  --paper2:#E6E1D6;
  --onpaper:#141210;
  --onpaper-mute:#4A453D;
  --txt:#F7F5F1;
  --mute:#9C9891;
  --dim:rgba(247,245,241,.66);
  --line:rgba(255,255,255,.10);
  --line2:rgba(255,255,255,.20);
  --paper-line:rgba(20,18,16,.14);
  --ember:#E4572E;
  --ember-l:#F27A54;
  --brass:var(--ember); /* v1 markup alias: 9 pages colour links with var(--brass), which was undefined */
  --absent:#4A4A4F;
  --maxw:1180px;
  --pad:36px;
  --head:70px;
}
@media(max-width:700px){:root{--pad:20px;--head:60px}}

*{margin:0;padding:0;box-sizing:border-box}
/* `scroll-behavior:smooth` is deliberately absent. An in-page anchor would
   animate through the 340vh and 400vh pinned sections, running every scroll
   handler for the whole trip — the one case where this page cannot hold 60fps.
   Jumping straight there is both faster and calmer. */
html{scroll-padding-top:var(--head)}
body{
  background:var(--ink);color:var(--txt);
  font-family:'Inter',-apple-system,BlinkMacSystemFont,'Segoe UI',Helvetica,Arial,sans-serif;
  font-size:17px;line-height:1.7;-webkit-font-smoothing:antialiased;
  overflow-x:clip;
}
img{max-width:100%;height:auto;display:block}
a{color:inherit}

.wrap{max-width:var(--maxw);margin:0 auto;padding:0 var(--pad)}
.narrow{max-width:760px}
section{padding:clamp(84px,12vw,168px) 0}
.pagehero{padding-top:calc(var(--head) + clamp(64px,9vw,120px))!important}
.paper{background:var(--paper);color:var(--onpaper)}
.paper .lead,.paper .dim{color:var(--onpaper-mute)}

/* ── Typography ─────────────────────────────────────────────── */
.display{
  text-wrap:balance;
  font-family:'Fraunces',Georgia,serif;font-weight:600;
  font-variation-settings:'SOFT' 0,'WONK' 1;
  line-height:1.02;letter-spacing:-.025em;
  font-size:clamp(42px,7vw,96px);
}
h1.display{margin-bottom:0}
h2.sec{
  text-wrap:balance;
  font-family:'Fraunces',Georgia,serif;font-weight:600;
  font-size:clamp(30px,4.2vw,56px);line-height:1.08;letter-spacing:-.02em;
}
h3.sub{font-family:'Fraunces',Georgia,serif;font-weight:600;font-size:22px;line-height:1.25;letter-spacing:-.01em}
.eyebrow{
  font-size:12px;letter-spacing:.16em;text-transform:uppercase;
  color:var(--ember);font-weight:600;margin-bottom:20px
}
.lead{font-size:clamp(18px,1.9vw,21px);line-height:1.65;color:var(--dim);max-width:56ch}
.statement{
  font-family:'Fraunces',Georgia,serif;font-weight:500;
  font-size:clamp(26px,4.4vw,52px);line-height:1.18;letter-spacing:-.02em;max-width:19ch
}
.statement span{color:var(--ember)}
.note{font-size:13.5px;color:var(--mute);letter-spacing:.02em}

/* ── Buttons ────────────────────────────────────────────────── */
.btn{
  display:inline-block;background:var(--ember);color:#fff;text-decoration:none;
  padding:16px 30px;border-radius:10px;font-weight:600;font-size:16px;
  border:1px solid var(--ember);cursor:pointer;
  transition:background .2s,transform .2s,border-color .2s
}
.btn:hover{background:var(--ember-l);border-color:var(--ember-l);transform:translateY(-1px)}
.btn-o{background:transparent;border-color:var(--line2);color:var(--txt)}
.btn-o:hover{background:rgba(255,255,255,.06);border-color:var(--txt);transform:translateY(-1px)}
.paper .btn-o{border-color:var(--paper-line);color:var(--onpaper)}
.paper .btn-o:hover{background:rgba(20,18,16,.05);border-color:var(--onpaper)}

/* ── Header — floating capsule, nav left / mark centre / actions right.
      Transparent over the hero, condenses into a pill on scroll. ────── */
.sitehead{
  position:fixed;top:0;left:0;right:0;z-index:60;
  padding:13px var(--pad);background:transparent;border:0;
  /* `contain` scopes the condense relayout to the header's own subtree so it
     cannot invalidate the page behind it. */
  contain:layout style;
  transition:padding .3s cubic-bezier(.2,.7,.2,1)
}
.headin{
  max-width:var(--maxw);margin:0 auto;
  display:grid;grid-template-columns:1fr auto 1fr;align-items:center;
  gap:clamp(20px,3.4vw,48px);
  padding:9px 9px 9px 26px;border-radius:999px;
  border:1px solid transparent;background:transparent;
  transition:background .3s,border-color .3s,box-shadow .3s,max-width .3s cubic-bezier(.2,.7,.2,1)
}
/* The capsule CONTRASTS with the band behind it rather than matching it.
   Matching made it disappear — a light bar on a light section and an ink bar
   on an ink section are both invisible. Light capsule over dark bands, ink
   capsule over light ones, so the menu is always a distinct object. */
.sitehead.stuck .headin{
  max-width:1000px;background:rgba(252,252,251,.93);backdrop-filter:blur(8px);
  -webkit-backdrop-filter:blur(8px);
  border-color:rgba(20,18,16,.12);box-shadow:0 10px 40px rgba(0,0,0,.26)
}
.sitehead.stuck.overlight .headin{
  background:rgba(24,21,18,.93);
  border-color:rgba(255,255,255,.15);box-shadow:0 12px 44px rgba(0,0,0,.34)
}
/* No `will-change` here on purpose. It was set to `padding` and `transform`,
   neither of which the compositor can animate, so it bought nothing and kept
   two extra layers alive for the life of the page. `contain` is the useful
   part: it bounds how far a change inside these boxes can propagate. */
.climb-sticky{contain:layout}
.browser{contain:layout}

.navleft{display:flex;align-items:center;gap:clamp(16px,1.9vw,26px);justify-self:start}
.navright{display:flex;align-items:center;gap:clamp(12px,1.6vw,20px);justify-self:end}
.navtel{display:inline-flex;align-items:center;gap:7px}
.navtel svg{width:15px;height:15px;flex:0 0 15px;opacity:.75}
.navtel:hover svg{opacity:1}

.brand{
  font-family:'Fraunces',Georgia,serif;font-weight:600;font-size:23px;
  letter-spacing:-.025em;text-decoration:none;color:var(--h-fg);white-space:nowrap;
  display:inline-flex;align-items:center;line-height:1;justify-self:center
}
.brand .caret{
  display:inline-block;width:2px;height:.82em;background:var(--ember);
  margin-left:4px;border-radius:1px;animation:caretblink 1.15s step-end infinite
}
@keyframes caretblink{50%{opacity:0}}

.navleft a,.navright .navlink,.has-sub>a{
  text-decoration:none;color:var(--h-mute);font-size:14.5px;font-weight:500;
  transition:color .18s;white-space:nowrap
}
.navleft a:hover,.navright .navlink:hover,.has-sub>a:hover,
.navleft a[aria-current],.navright .navlink[aria-current]{color:var(--h-fg)}
.has-sub{position:relative}
.chev{display:inline-block;width:6px;height:6px;margin-left:5px;vertical-align:1px;
  border-right:1.5px solid currentColor;border-bottom:1.5px solid currentColor;transform:rotate(45deg)}
.submenu{
  position:absolute;top:calc(100% + 16px);left:-18px;min-width:330px;
  background:rgba(28,25,22,.98);
  border:1px solid var(--line);border-radius:16px;padding:10px;
  opacity:0;visibility:hidden;transform:translateY(-6px);transition:.2s;
  box-shadow:0 30px 70px rgba(0,0,0,.55)
}
.has-sub:hover .submenu,.has-sub:focus-within .submenu{opacity:1;visibility:visible;transform:none}
.submenu a{display:block;padding:11px 14px;border-radius:11px;text-decoration:none;transition:background .15s}
.submenu a:hover{background:rgba(255,255,255,.06)}
.submenu strong{display:block;font-size:15px;color:var(--txt);font-weight:600}
.submenu small{display:block;font-size:13px;color:var(--mute);margin-top:2px}
.submenu .all{color:var(--ember);font-weight:600;font-size:14px;margin-top:4px;
  border-top:1px solid var(--line);border-radius:0}

.headcta{
  background:var(--ember);color:#fff;text-decoration:none;padding:11px 22px;
  border-radius:999px;font-size:14.5px;font-weight:600;white-space:nowrap;
  transition:background .2s,transform .2s
}
.headcta:hover{background:var(--ember-l);transform:translateY(-1px)}
.navtoggle{display:none;background:none;border:0;cursor:pointer;padding:8px;
  flex-direction:column;gap:5px}
.navtoggle span{display:block;width:22px;height:2px;background:var(--h-fg);transition:background .25s}

/* Over the white search page everything inverts */
/* Header foreground resolves from two variables so every child follows one
   decision instead of each needing its own override.

   Dark text is needed when EXACTLY ONE of `stuck`/`overlight` is true:
     not stuck, not overlight -> transparent bar over a dark hero -> light text
     not stuck, overlight     -> transparent bar over a light band -> DARK text
     stuck,     not overlight -> light capsule over a dark band    -> DARK text
     stuck,     overlight     -> ink capsule over a light band     -> light text
   That is an exclusive-or, which is why the two selectors below look odd. */
.sitehead{--h-fg:var(--txt);--h-mute:var(--dim)}
.sitehead.stuck:not(.overlight),
.sitehead.overlight:not(.stuck){--h-fg:#1A1C1E;--h-mute:#55595E}
/* The flip happens mid-scroll at a band edge, so it has to be eased or it
   reads as a flicker. The nav links already transitioned; these did not. */
.sitehead .brand{transition:color .25s}

@media(max-width:1040px){
  .headin{grid-template-columns:auto 1fr auto;padding:9px 9px 9px 18px}
  .brand{justify-self:start;order:-1}
  .navleft{
    position:fixed;inset:calc(var(--head) + 8px) var(--pad) auto;flex-direction:column;
    align-items:stretch;gap:0;padding:12px 20px 20px;border-radius:20px;
    background:rgba(28,25,22,.97);backdrop-filter:blur(18px);
    border:1px solid var(--line);box-shadow:0 24px 60px rgba(0,0,0,.5);display:none
  }
  .navleft.open{display:flex}
  .navleft a,.navleft .has-sub>a{padding:14px 0;border-bottom:1px solid var(--line);font-size:17px}
  .submenu{position:static;opacity:1;visibility:visible;transform:none;box-shadow:none;
    border:0;background:transparent;padding:4px 0 10px;min-width:0;backdrop-filter:none}
  .navtoggle{display:flex}
  .navright .navtel span{display:none}
  .navtel{padding:9px;border-radius:999px;border:1px solid var(--line)}
  .navtel svg{width:17px;height:17px;opacity:1}
  .sitehead.stuck:not(.overlight) .navtel,.sitehead.overlight:not(.stuck) .navtel{border-color:rgba(20,18,16,.16)}
  .sitehead.stuck:not(.overlight) .navleft,.sitehead.overlight:not(.stuck) .navleft{background:rgba(252,252,251,.98)}
  .sitehead.stuck:not(.overlight) .navleft a,.sitehead.overlight:not(.stuck) .navleft a{border-bottom-color:rgba(20,18,16,.10)}
}
@media(max-width:520px){.headcta{padding:10px 16px;font-size:13.5px}}

/* ── Act 1: the search. Everything sits in normal flow inside the pinned
      viewport, so the window and the caption can never overlap. ─────── */
.act-scroll{height:340vh;position:relative;background:#fff}
.act-sticky{
  position:sticky;top:0;height:100vh;overflow:hidden;
  background:#fff;color:#202124;
  display:flex;flex-direction:column;align-items:center;justify-content:center;
  gap:clamp(14px,2.4vh,32px);
  padding:calc(var(--head) + 52px) var(--pad) 8vh
}
/* macOS browser window around the results — the frame is what makes it
   read as "this is my screen" rather than "this is a graphic". */
.browser{
  width:min(1080px,94vw);margin-inline:auto;flex:0 1 auto;min-height:0;max-height:100%;
  display:flex;flex-direction:column;
  background:#fff;border-radius:14px;overflow:hidden;
  box-shadow:0 1px 2px rgba(0,0,0,.10),0 12px 28px rgba(0,0,0,.10),0 42px 90px rgba(0,0,0,.16);
  border:1px solid rgba(0,0,0,.10);color:#202124
}
.browser-chrome{
  display:flex;align-items:center;gap:18px;padding:12px 18px;
  background:#f1f1f3;border-bottom:1px solid rgba(0,0,0,.09)
}
.dots{display:flex;gap:8px;flex:0 0 auto}
.dots i{width:12px;height:12px;border-radius:50%;display:block}
.dots i:nth-child(1){background:#ff5f57}
.dots i:nth-child(2){background:#febc2e}
.dots i:nth-child(3){background:#28c840}
.omnibox{
  flex:1;display:flex;align-items:center;gap:9px;background:#fff;
  border:1px solid rgba(0,0,0,.08);border-radius:8px;padding:7px 14px;
  font-size:13.5px;color:#5f6368;max-width:520px;margin:0 auto;
  white-space:nowrap;overflow:hidden;user-select:none
}
.omnibox svg{width:12px;height:12px;flex:0 0 12px;opacity:.55}

.serp{width:100%;flex:1 1 auto;min-height:0;overflow:hidden;
  background:#fff;border:0;border-radius:0;padding:38px 0 30px;box-shadow:none;color:#202124}
.serp-bar{
  display:flex;align-items:center;gap:16px;background:#fff;
  border:1px solid #dfe1e5;border-radius:999px;padding:16px 26px;margin:0 auto 40px;
  box-shadow:0 1px 6px rgba(32,33,36,.12);width:min(660px,86%)
}
.serp-bar svg{flex:0 0 20px;opacity:1;color:#9aa0a6}
.serp-q{font-size:18px;color:#202124;white-space:nowrap;overflow:hidden;
  user-select:none;-webkit-user-select:none}
.serp-q b{border-right:2px solid #4285f4;padding-right:2px;font-weight:400}

.serp-res{padding:0 0 34px;border-top:0;opacity:1;transition:opacity .4s;
  width:min(760px,86%);margin:0 auto}
.serp-res .u{font-size:15px;color:#4d5156;margin-bottom:6px;display:flex;align-items:center;gap:12px}
.serp-res .u::before{content:"";width:30px;height:30px;border-radius:50%;flex:0 0 30px;
  background:#f1f3f4;border:1px solid #dadce0}
.serp-res .t{font-size:25px;line-height:1.3;color:#1a0dab;margin-bottom:6px;
  font-family:Arial,Helvetica,sans-serif}
.serp-res .d{font-size:16.5px;color:#4d5156;line-height:1.58}

/* ── The empty slot ──────────────────────────────────────────────────
   This is the emotional payload of the whole first act: the hole in the
   results where the visitor's business should be. It has two states —
   muted while the search is still cycling, ember once the payoff line
   lands. It was losing the argument in the first state: light grey on
   near-white read as a disabled input, so the eye skipped it entirely.

   The absence still has to read as absence, so it stays dashed and empty
   rather than filling with colour. What changed is that it now *claims
   space*: darker dash, real contrast on the label, and a slow breath so
   it moves while everything around it is still. */
.serp-gap{border-top:0;margin:0 auto;padding:0 0 34px;width:min(760px,86%);text-align:left}
.serp-gap .box{width:100%;border:2px dashed #9aa0a6;border-radius:12px;padding:44px 22px;
  background:
    repeating-linear-gradient(135deg,
      rgba(154,160,166,.055) 0 10px, transparent 10px 20px),
    #f4f5f6;
  transition:border-color .5s,background .5s,box-shadow .5s,transform .5s;
  text-align:center;animation:gapbreath 3.4s ease-in-out infinite}
.serp-gap .lbl{font-size:14.5px;letter-spacing:.15em;text-transform:uppercase;color:#5f6368;
  font-weight:800;transition:color .5s,transform .5s}
@keyframes gapbreath{
  0%,100%{border-color:#9aa0a6;box-shadow:0 0 0 0 rgba(154,160,166,0)}
  50%    {border-color:#7c8287;box-shadow:0 0 0 7px rgba(154,160,166,.10)}
}
/* The payoff. Ember, a firmer edge and a small lift — the slot stops being
   a blank and becomes the thing the whole section was pointing at. */
.serp.fixed .serp-gap .box{
  border:2px solid var(--ember);
  background:rgba(228,87,46,.09);
  box-shadow:0 0 0 6px rgba(228,87,46,.10),0 16px 40px rgba(228,87,46,.16);
  transform:scale(1.02);animation:none}
.serp.fixed .serp-gap .lbl{color:var(--ember);transform:scale(1.04)}
.serp.fixed .serp-res{opacity:.28}
@media(prefers-reduced-motion:reduce){
  .serp-gap .box{animation:none;border-color:#7c8287}
  .serp.fixed .serp-gap .box{transform:none}
  .serp.fixed .serp-gap .lbl{transform:none}
}

@media(max-width:820px){
  .serp-res .t{font-size:20px}
  .serp-res .d{font-size:15px}
  .serp{padding:30px 0 26px}
  .omnibox{font-size:12px;padding:6px 11px}
  .browser-chrome{padding:10px 13px;gap:12px}
}
@media(max-height:800px){
  .serp{padding:26px 0 20px}
  .serp-res{padding-bottom:22px}
  .serp-gap{padding-bottom:22px}
  .serp-gap .box{padding:26px 18px}
  .serp-bar{margin-bottom:26px;padding:12px 22px}
}

.act-cap{position:static;flex:0 0 auto;text-align:center;
  padding:0 var(--pad);pointer-events:none}
/* vertical line drawing the eye down from the results to the line */
.act-cap::before{
  content:"";display:block;width:1px;height:clamp(14px,2.6vh,32px);margin:0 auto clamp(9px,1.4vh,17px);
  background:linear-gradient(180deg,rgba(32,33,36,0),rgba(32,33,36,.30));
  animation:capline 2.4s ease-in-out infinite
}
@keyframes capline{0%,100%{opacity:.35;transform:scaleY(.8)}50%{opacity:1;transform:scaleY(1)}}
.act-cap p{
  font-family:'Fraunces',Georgia,serif;font-size:clamp(24px,3.6vw,42px);line-height:1.2;
  letter-spacing:-.02em;color:#202124;opacity:0;transform:translateY(14px);
  transition:opacity .5s,transform .5s;max-width:20ch;margin:0 auto;font-weight:600
}

/* Beat counter — three dots showing there is more to come */
.act-beats{display:flex;gap:7px;justify-content:center;margin-top:24px}
.act-beats i{width:22px;height:3px;border-radius:2px;background:rgba(32,33,36,.16);
  transition:background .35s}
.act-beats i.on{background:var(--ember)}

/* Scroll cue — the thing that stops a first-timer scrolling straight past */
.scrollcue{
  position:absolute;left:0;right:0;bottom:16px;display:flex;
  flex-direction:column;align-items:center;gap:7px;pointer-events:none;
  transition:opacity .4s;color:#5f6368
}
.scrollcue.gone{opacity:0}
.scrollcue span{font-size:11px;letter-spacing:.18em;text-transform:uppercase;font-weight:600}
.scrollcue svg{width:17px;height:17px;animation:cuebob 1.7s ease-in-out infinite}
@keyframes cuebob{0%,100%{transform:translateY(0);opacity:.5}50%{transform:translateY(5px);opacity:1}}

@media(prefers-reduced-motion:reduce){
  .act-cap::before,.scrollcue svg{animation:none}
}
@media(max-height:720px){
  .act-cap::before{height:22px;margin-bottom:12px}
  .act-beats{margin-top:14px}
  .scrollcue{display:none}
}

@media(max-width:700px){
  .serp-res .t{font-size:19px}
  .serp-res .d{font-size:14px}
  .serp-bar{padding:13px 18px}
  .act-cap{bottom:5vh}
}
/* ── Act 2: the climb — scroll-scrubbed rank journey ────────── */
.climb-scroll{height:400vh;position:relative}
.climb-sticky{position:sticky;top:0;height:100vh;display:grid;place-items:center;overflow:hidden}
.climb-in{width:min(1060px,92vw);display:grid;grid-template-columns:.72fr 1.28fr;gap:clamp(28px,4vw,56px);align-items:center}
@media(max-width:820px){.climb-in{grid-template-columns:1fr;gap:30px}}
@media(max-width:620px){
  .rung{flex-wrap:wrap;row-gap:9px;padding:12px 15px}
  .rung .track{flex:1 1 60%}
  .rung .out{width:100%;flex:1 1 100%;font-size:13px;padding-left:38px}
  .rung.gap .gaptxt{padding-left:0}
}

.climb-num{font-family:'Fraunces',Georgia,serif;font-weight:600;line-height:.86;
  letter-spacing:-.04em;font-size:clamp(84px,15vw,190px);font-variant-numeric:tabular-nums}
.climb-num small{display:block;font-family:'Inter',sans-serif;font-weight:600;font-size:12px;
  letter-spacing:.16em;text-transform:uppercase;color:var(--ember);margin-bottom:18px;line-height:1}
.climb-cap{font-size:clamp(19px,2vw,23px);line-height:1.5;color:var(--txt);margin-top:26px;max-width:24ch;font-weight:500;
  min-height:5.2em}

.ladder-lbl{font-size:11.5px;letter-spacing:.15em;text-transform:uppercase;
  font-weight:700;color:var(--mute);margin:0 0 16px}
.ladder{display:flex;flex-direction:column;gap:7px}
.rung{display:flex;align-items:center;gap:14px;padding:13px 18px;border-radius:11px;
  border:1px solid var(--line);background:rgba(255,255,255,.02)}
.rung .pos{font-size:12.5px;font-weight:700;color:var(--mute);width:24px;flex:0 0 24px;
  font-variant-numeric:tabular-nums}
/* A fixed track with a fill inside, so bar length is read against a constant
   scale. Previously each bar was its own width with nothing to compare it to,
   which is why the row meant nothing on its own. */
.rung .track{flex:1;height:8px;border-radius:4px;background:rgba(255,255,255,.05);
  overflow:hidden}
.rung .track i{display:block;height:100%;border-radius:4px;background:var(--absent);
  transition:width .5s cubic-bezier(.2,.7,.2,1)}
/* The outcome column. This replaced a bare "YOU" tag, which named the row
   without saying anything about it — the bars beside it carried no meaning at
   all and the whole graphic read as a loading skeleton. This is now the widest
   and most legible thing in the rung, because it is the argument. */
.rung .out{font-size:13.5px;line-height:1.3;color:var(--mute);
  width:168px;flex:0 0 168px;text-align:left}
.rung.you{background:rgba(228,87,46,.10);border-color:var(--ember)}
.rung.you .pos{color:var(--ember)}
.rung.you .track i{background:var(--ember)}
.rung.you .out{color:var(--ember);font-weight:600;
  /* An animation, not a transition. paintLadder rebuilds the rungs on every
     stage, so these nodes are new each time and have no previous value to
     transition FROM — a transition would simply never play. */
  animation:outswap .5s cubic-bezier(.2,.7,.2,1) both}
@keyframes outswap{
  from{opacity:0;transform:translateY(7px)}
  to  {opacity:1;transform:none}
}
/* Row one is the thing being lost to, so it reads at full strength; two and
   three step down. The eye should land on "gets the call" and on your row. */
.rung:first-child .out{color:var(--txt)}
/* The break in the list. This is the whole point of the graphic — the visitor
   should feel the distance, not read a tidy sequence of adjacent numbers. */
.rung.gap{border:0;background:none;padding:6px 18px;gap:14px}
.rung.gap .pos{color:var(--absent);font-size:17px;line-height:1}
.rung.gap .gaptxt{font-size:13px;color:var(--mute);letter-spacing:.01em}

/* ── Word-by-word reveal (the Apple text move) ──────────────── */
.wr span{display:inline-block;opacity:.14;transition:opacity .45s ease}
.wr span.lit{opacity:1}
@media(prefers-reduced-motion:reduce){.wr span{opacity:1!important}}

/* ── Cost calculator ────────────────────────────────────────── */
.calc{background:var(--paper2);border:1px solid var(--paper-line);border-radius:16px;padding:clamp(24px,4vw,44px)}
.calc-row{display:grid;grid-template-columns:1fr auto;gap:18px;align-items:center;
  padding:18px 0;border-bottom:1px solid var(--paper-line)}
.calc-row:last-of-type{border-bottom:0}
.calc-row label{font-size:16px;color:var(--onpaper-mute)}
.calc-row input{
  width:130px;background:#fff;border:1px solid rgba(20,18,16,.16);color:var(--onpaper);
  border-radius:9px;padding:11px 14px;font:inherit;font-size:17px;text-align:right
}
.calc-row input:focus{outline:2px solid var(--ember);outline-offset:2px}
.calc-out{margin-top:30px;padding-top:28px;border-top:1px solid var(--paper-line)}
.calc-out .n{font-family:'Fraunces',Georgia,serif;font-weight:600;
  font-size:clamp(44px,8vw,84px);line-height:1;letter-spacing:-.03em;color:var(--ember)}
.calc-out .c{font-size:15px;color:var(--onpaper-mute);margin-top:12px;max-width:46ch}

/* ── Cards / grids ──────────────────────────────────────────── */
/* Explicit column counts, not auto-fit. auto-fit chose 3 columns for a
   4-card grid (3 + 1 orphan) and 4 columns for a 6-card grid (4 + 2).
   Every grid2 on this site holds 4 or 6 cards and every grid3 holds 6 or
   11, so fixed counts divide evenly and nothing is left stranded. */
.grid2{display:grid;grid-template-columns:repeat(2,1fr);gap:22px}
@media(max-width:760px){.grid2{grid-template-columns:1fr}}
.grid3{display:grid;grid-template-columns:repeat(3,1fr);gap:20px}
@media(max-width:960px){.grid3{grid-template-columns:repeat(2,1fr)}}
@media(max-width:640px){.grid3{grid-template-columns:1fr}}
.card{background:var(--ink2);border:1px solid var(--line);border-radius:14px;padding:30px;
  transition:border-color .25s,transform .25s}
a.card{text-decoration:none;display:block}
a.card:hover{border-color:var(--line2);transform:translateY(-3px)}
.paper .card{background:#fff;border-color:var(--paper-line)}
.card .k{font-size:12px;letter-spacing:.14em;text-transform:uppercase;color:var(--ember);font-weight:600;margin-bottom:14px}
.card p{color:var(--dim);font-size:15.5px;line-height:1.65;margin-top:10px}
.paper .card p{color:var(--onpaper-mute)}

/* ── Footer ─────────────────────────────────────────────────── */
.sitefoot{background:var(--ink2);border-top:1px solid var(--line);padding:74px 0 34px}
.footgrid{display:grid;grid-template-columns:1.5fr 1fr 1fr 1fr;gap:44px}
@media(max-width:860px){.footgrid{grid-template-columns:1fr 1fr;gap:34px}}
@media(max-width:520px){.footgrid{grid-template-columns:1fr}}
.footbrand{font-family:'Fraunces',Georgia,serif;font-weight:600;font-size:26px;
  letter-spacing:-.025em;display:inline-flex;align-items:center;line-height:1}
.footbrand .caret{display:inline-block;width:2px;height:.82em;background:var(--ember);
  margin-left:4px;border-radius:1px;animation:caretblink 1.15s step-end infinite}
.sitefoot h2{font-size:12px;letter-spacing:.14em;text-transform:uppercase;color:var(--mute);
  font-weight:600;margin-bottom:16px}
.sitefoot ul{list-style:none}
.sitefoot li{margin-bottom:10px}
.sitefoot a{color:var(--dim);text-decoration:none;font-size:15px;transition:color .18s}
.sitefoot a:hover{color:var(--txt)}
.footbot{margin-top:52px;padding-top:26px;border-top:1px solid var(--line);
  display:flex;justify-content:space-between;gap:16px;flex-wrap:wrap;
  font-size:14px;color:var(--mute)}

/* ── Forms ──────────────────────────────────────────────────── */
.field{margin-bottom:18px}
.field label{display:block;font-size:14px;color:var(--dim);margin-bottom:7px;font-weight:500}
.paper .field label{color:var(--onpaper-mute)}
.field input,.field select,.field textarea{
  width:100%;background:var(--ink3);border:1px solid var(--line);color:var(--txt);
  border-radius:9px;padding:13px 15px;font:inherit;font-size:16px
}
.paper .field input,.paper .field select,.paper .field textarea{
  background:#fff;border-color:var(--paper-line);color:var(--onpaper)}
.field textarea{min-height:110px;resize:vertical}
.field input:focus,.field select:focus,.field textarea:focus{outline:2px solid var(--ember);outline-offset:1px}
.formnote{font-size:13.5px;color:var(--mute);margin-top:14px}

@media(prefers-reduced-motion:reduce){
  .brand .caret,.footbrand .caret{animation:none}
  html{scroll-behavior:auto}
  .rev{opacity:1!important;transform:none!important;animation:none!important}
  .act-cap p.is-in{opacity:1}
  .act-scroll{height:auto}
  .act-sticky{position:static;height:auto;padding:80px 0}
  *{transition-duration:.01ms!important}
}

/* ══════════════════════════════════════════════════════════════
   Compatibility layer — the fifteen pages carried over from v1
   use these class names. Mapping them to the v2 tokens converts
   every page without rewriting its body markup.
   ══════════════════════════════════════════════════════════════ */

/* .rv was the v1 reveal class — 175 uses. Behaves as .rev now. */
.rv{opacity:0;transform:translateY(22px)}
.rv.in{opacity:1;transform:none;
  transition:opacity .7s cubic-bezier(.2,.6,.2,1),transform .7s cubic-bezier(.2,.6,.2,1)}


.tight{padding:clamp(64px,9vw,120px) 0}
.pagehero{padding:clamp(72px,11vw,150px) 0 clamp(48px,7vw,80px);
  border-bottom:1px solid var(--line)}
.pagehero .lead{margin-top:24px}

.band-light{background:var(--paper);color:var(--onpaper)}
.band-light .lead,.band-light .prose{color:var(--onpaper-mute)}
.band-light .eyebrow{color:var(--ember)}
.band-light .card{background:#fff;border-color:var(--paper-line)}
.band-light .card p{color:var(--onpaper-mute)}
.band-light .field label{color:var(--onpaper-mute)}
.band-light .field input,.band-light .field select,.band-light .field textarea{
  background:#fff;border-color:var(--paper-line);color:var(--onpaper)}
.band-light h1,.band-light h2,.band-light h3{color:var(--onpaper)}

.band-tint{background:var(--ink2)}
.band-glow{background:radial-gradient(120% 90% at 50% 0%,rgba(228,87,46,.11),transparent 62%),var(--ink)}
.band-photo{background:var(--ink2)}

.ctaband{background:var(--paper);color:var(--onpaper);text-align:center;
  padding:clamp(72px,10vw,130px) 0}
.ctaband h2{color:var(--onpaper)}
.ctaband .lead{color:var(--onpaper-mute);margin-left:auto;margin-right:auto}
.ctagrid{display:flex;gap:12px;justify-content:center;flex-wrap:wrap;margin-top:34px}
.orcall{margin-top:20px;font-size:15px;color:var(--onpaper-mute)}
.orcall a{color:var(--onpaper);font-weight:600}

.crumbs{font-size:13.5px;color:var(--mute);margin-bottom:22px}
.crumbs a{color:var(--dim);text-decoration:none}
.crumbs a:hover{color:var(--txt)}

.prose{max-width:62ch;color:var(--dim);font-size:18.5px;line-height:1.72}
/* Space above a heading is large, below it small — that binds the heading to
   the text it introduces instead of letting it float between two blocks. */
.prose h2{text-wrap:balance;font-family:'Fraunces',Georgia,serif;font-weight:600;
  font-size:clamp(26px,3.4vw,36px);line-height:1.15;letter-spacing:-.02em;
  color:var(--txt);margin:72px 0 16px}
.prose h3{text-wrap:balance;font-family:'Fraunces',Georgia,serif;font-weight:600;font-size:21px;
  color:var(--txt);margin:46px 0 10px}
.prose h2+p,.prose h3+p{margin-top:0}
.prose p{margin-bottom:26px}
/* The opening paragraph is the entry point — bigger and brighter lowers the
   cost of starting to read. */
.prose>p:first-of-type{font-size:21px;line-height:1.62;color:var(--txt);margin-bottom:32px}
/* Bold should carry the argument, so a scanner who reads only bold still gets it. */
.prose strong,.prose b{color:var(--txt);font-weight:600}
.band-light .prose strong,.band-light .prose b{color:var(--onpaper)}
.prose ul,.prose ol{margin:0 0 28px 20px}
.prose li{margin-bottom:12px;padding-left:4px}
.prose li::marker{color:var(--ember)}
.prose a{color:var(--ember);text-underline-offset:3px}
/* Rest stop between dense blocks. */
.pull{border-left:3px solid var(--ember);padding:6px 0 6px 26px;margin:44px 0;
  font-family:'Fraunces',Georgia,serif;font-weight:500;font-size:clamp(21px,2.6vw,27px);
  line-height:1.35;letter-spacing:-.015em;color:var(--txt);max-width:26ch;text-wrap:pretty}
.band-light .pull{color:var(--onpaper)}
.band-light .prose h2,.band-light .prose h3{color:var(--onpaper)}

.actions{display:flex;gap:12px;flex-wrap:wrap;margin-top:32px}
.arrow{display:inline-block;transition:transform .2s}
a:hover .arrow{transform:translateX(3px)}
.tag{display:inline-block;font-size:12px;letter-spacing:.12em;text-transform:uppercase;
  font-weight:600;color:var(--ember);margin-bottom:12px}
.grp{font-size:12px;letter-spacing:.14em;text-transform:uppercase;color:var(--mute);
  font-weight:600;margin:16px 0 8px}
.sub{display:block}
.brass{color:var(--ember)}

.btn-p{display:inline-block;background:var(--ember);color:#fff;text-decoration:none;
  padding:16px 30px;border-radius:10px;font-weight:600;font-size:16px;border:1px solid var(--ember);
  transition:background .2s,transform .2s}
.btn-p:hover{background:var(--ember-l);transform:translateY(-1px)}
.btn-ghost{display:inline-block;background:transparent;border:1px solid var(--line2);
  color:var(--txt);text-decoration:none;padding:16px 30px;border-radius:10px;
  font-weight:600;font-size:16px;transition:.2s}
.btn-ghost:hover{background:rgba(255,255,255,.06);border-color:var(--txt)}
.band-light .btn-ghost,.ctaband .btn-ghost{border-color:var(--paper-line);color:var(--onpaper)}
.band-light .btn-ghost:hover,.ctaband .btn-ghost:hover{background:rgba(20,18,16,.05)}

@media(prefers-reduced-motion:reduce){.rv{opacity:1!important;transform:none!important;animation:none!important}}

/* ══════════════════════════════════════════════════════════════
   Missing primitives — classes the v1 pages use that had no rule.
   Their absence is why About rendered flush to the viewport edge.
   ══════════════════════════════════════════════════════════════ */

/* Full-bleed photo band with text pinned over it */
.band-photo{position:relative;padding:0;min-height:clamp(420px,58vw,640px);
  display:grid;align-items:end;overflow:hidden;background:var(--ink2)}
.band-photo>img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;z-index:0}
.pscrim{position:absolute;inset:0;z-index:1;
  background:linear-gradient(180deg,rgba(18,16,14,.15) 0%,rgba(18,16,14,.55) 48%,rgba(18,16,14,.95) 100%)}
.pin{position:relative;z-index:2;max-width:var(--maxw);width:100%;margin:0 auto;
  padding:0 var(--pad) clamp(48px,7vw,84px)}
.pin .lead{max-width:52ch}
.accent-rule{width:54px;height:3px;background:var(--ember);border-radius:2px;margin-bottom:24px}

/* .big — the emphasised body paragraph used on About and service pages */
.big{font-size:clamp(18px,2vw,21px);line-height:1.65;color:var(--dim);max-width:56ch;text-wrap:pretty}
.band-light .big,.paper .big,.ctaband .big{color:var(--onpaper-mute)}

/* Bare headings inside content blocks, so nothing falls back to browser default */
h1{font-family:'Fraunces',Georgia,serif;font-weight:600;text-wrap:balance;
  font-size:clamp(38px,6vw,74px);line-height:1.04;letter-spacing:-.025em}
h4{font-family:'Fraunces',Georgia,serif;font-weight:600;font-size:18px;line-height:1.3;margin-bottom:8px}
.card h3,.grid2 h3,.grid3 h3{font-family:'Fraunces',Georgia,serif;font-weight:600;
  font-size:20px;line-height:1.25;letter-spacing:-.01em;margin-bottom:8px}
blockquote{border-left:3px solid var(--ember);padding-left:24px;margin:32px 0;
  font-family:'Fraunces',Georgia,serif;font-size:21px;line-height:1.4;color:var(--txt)}

/* ── Buttons: submit buttons were falling back to the browser default ── */
button[type="submit"]{
  display:inline-block;background:var(--ember);color:#fff;border:1px solid var(--ember);
  padding:16px 30px;border-radius:10px;font:inherit;font-weight:600;font-size:16px;
  cursor:pointer;transition:background .2s,transform .2s;margin-top:6px
}
button[type="submit"]:hover{background:var(--ember-l);transform:translateY(-1px)}
button[type="submit"]:focus-visible{outline:2px solid var(--txt);outline-offset:3px}

/* ── Forms on light bands: .ctaband and .paper needed the same treatment
      that .band-light already had, or inputs render dark on cream. ────── */
.ctaband .field label,.paper .field label,.band-light .field label{color:var(--onpaper-mute)}
.ctaband .field input,.ctaband .field select,.ctaband .field textarea,
.paper .field input,.paper .field select,.paper .field textarea{
  background:#fff;border:1px solid var(--paper-line);color:var(--onpaper)
}
.ctaband .field input::placeholder,.paper .field input::placeholder,
.band-light .field input::placeholder{color:#8C877E}
.ctaband .formnote,.paper .formnote,.band-light .formnote{color:var(--onpaper-mute)}
.ctaband form,.paper form,.band-light form{text-align:left}

/* Forms sit better constrained — a full-width input on a wide band reads as a wall */
form{max-width:520px}
.ctagrid form{max-width:none}

/* ══════════════════════════════════════════════════════════════
   Alignment — .narrow had no auto margin, so narrow content sat
   hard-left inside a 1180px wrap with half the screen empty.
   ══════════════════════════════════════════════════════════════ */
.narrow{margin-left:auto;margin-right:auto}

/* Short intro blocks centre. Long-form prose never does — a centred
   paragraph has a ragged left edge, and the eye needs a straight one
   to find the start of the next line. */
.center{text-align:center}
.center .lead,.center .big,.center .statement,.center p{margin-left:auto;margin-right:auto}
.center .statement{max-width:22ch}
.center .actions,.center .ctagrid{justify-content:center}
.center .accent-rule{margin-left:auto;margin-right:auto}

/* Centre the pieces that were already meant to be centred */
.ctaband .wrap{text-align:center}
.ctaband .lead{margin-left:auto;margin-right:auto}

/* The climb reads centred because its grid fills the wrap — keep the
   symptom/how-we-work grids consistent with it. */
.grid2,.grid3{margin-left:auto;margin-right:auto}

/* ── Stat grid for case studies ─────────────────────────────── */
.statgrid{display:grid;grid-template-columns:repeat(4,1fr);gap:18px;
  margin-top:56px;max-width:1000px;margin-left:auto;margin-right:auto}
@media(max-width:900px){.statgrid{grid-template-columns:repeat(2,1fr)}}
@media(max-width:480px){.statgrid{grid-template-columns:1fr}}
.stat{background:#fff;border:1px solid var(--paper-line);border-radius:14px;
  padding:30px 16px;text-align:center}
/* The figure must never wrap. "774 → 3,700" breaking across two lines reads as
   two unrelated numbers and destroys the before/after comparison that is the
   whole point of the card. Column min-width, padding and max font-size are set
   together so the longest string clears the narrowest column at full size. */
.stat-n{font-family:'Fraunces',Georgia,serif;font-weight:600;font-size:clamp(23px,2.5vw,31px);
  white-space:nowrap;font-variant-numeric:lining-nums;
  line-height:1.05;letter-spacing:-.02em;color:var(--ember)}
.stat-l{font-size:14px;color:var(--onpaper-mute);margin-top:10px;letter-spacing:.01em}

/* ── Form submit states ───────────────────────────────────────────────
   Every form carried `data-ajax` and `data-confirm` markup that nothing
   implemented, so submitting navigated the browser to /api/submit and
   rendered the raw JSON response. These style the states the handler needs.

   ⚠️ `.ctaband` is a LIGHT band (`background:var(--paper)`), despite the name
   reading like a dark call-to-action strip. An earlier version of this block
   grouped it with `.band-dark` and painted the confirmation text `--dim`
   (near-white) onto cream — invisible. This is the second bug caused by
   guessing what `.ctaband` is; do not add it to a dark-context selector. */

/* `.formnote` sits directly on the band, so it has to follow the band's
   colour. Inheriting is safer than enumerating band classes — whatever the
   band sets as its text colour is correct by construction. */
.formnote{margin:12px 0 0;font-size:14px;line-height:1.5;color:inherit;opacity:.72}
.formnote.bad{opacity:1;color:#B3341A}
.band-dark .formnote.bad,.band-ink .formnote.bad,.band-glow .formnote.bad{color:var(--ember-l)}

/* `.formdone` is deliberately self-contained: opaque background, explicit
   text colours, no dependence on the band behind it. It is the single most
   important thing a visitor ever sees on this site — the proof their enquiry
   landed — so it must not be able to break again from an unrelated change. */
.formdone{padding:24px 26px;border-radius:16px;
  background:#F6F1EA;border:1px solid rgba(228,87,46,.30);
  color:#141210;text-align:left}
.formdone strong{display:block;font-family:'Fraunces',Georgia,serif;
  font-weight:600;font-size:clamp(20px,2.4vw,26px);line-height:1.2;
  margin-bottom:8px;color:#141210}
.formdone p{margin:0;font-size:15px;line-height:1.6;color:#4A453D}
form[data-ajax] button[disabled]{opacity:.6;cursor:progress}

/* ── Cookie / consent card ────────────────────────────────────────────
   A small card pinned bottom-left, not a full-width bar. The bar version
   spanned the viewport and sat over the content the visitor came to read —
   on the diagnostic it covered the answer. Consent should be askable
   without holding the page hostage, so this takes one corner and stays
   out of the reading column entirely. */
.cc{position:fixed;left:18px;bottom:18px;right:auto;z-index:80;
  width:min(316px,calc(100vw - 36px));
  background:rgba(24,21,18,.97);backdrop-filter:blur(10px);
  -webkit-backdrop-filter:blur(10px);
  border:1px solid rgba(255,255,255,.14);border-radius:14px;
  box-shadow:0 14px 40px rgba(0,0,0,.40);
  padding:16px 17px;
  display:flex;flex-direction:column;gap:12px;
  animation:ccup .4s cubic-bezier(.2,.7,.2,1)}
@keyframes ccup{from{opacity:0;transform:translateY(10px)}to{opacity:1;transform:none}}
.cc[hidden]{display:none}
.cc-t{margin:0;font-size:12.5px;line-height:1.5;color:var(--dim)}
.cc-b{display:flex;gap:8px}
.cc-btn{font:inherit;font-size:12.5px;font-weight:600;cursor:pointer;
  padding:7px 14px;border-radius:999px;border:1px solid transparent;
  transition:background .2s,border-color .2s,color .2s}
.cc-yes{background:var(--ember);color:#fff;border-color:var(--ember)}
.cc-yes:hover{background:var(--ember-l);border-color:var(--ember-l)}
.cc-ghost{background:transparent;color:var(--dim);border-color:rgba(255,255,255,.22)}
.cc-ghost:hover{color:var(--txt);border-color:var(--txt)}
@media(max-width:560px){
  .cc{left:12px;right:12px;bottom:12px;width:auto}
  .cc-btn{flex:1 1 0}
}
@media(prefers-reduced-motion:reduce){.cc{animation:none}}

/* ── Problem pages: numbered steps + timeline ─────────────────────────
   Both are declared for light and dark bands explicitly. `.ctaband` is a
   LIGHT band despite the name — see the note on the form styles above. */
.steps{display:grid;gap:2px}
.step{display:grid;grid-template-columns:auto 1fr;gap:22px;align-items:start;
  padding:24px 0;border-top:1px solid var(--line)}
.step:last-child{border-bottom:1px solid var(--line)}
.step-n{font-family:'Fraunces',Georgia,serif;font-weight:600;font-size:15px;
  color:var(--ember);letter-spacing:.04em;padding-top:3px;min-width:26px}
.step h3{margin:0 0 8px;color:var(--txt)}
.step p{margin:0;font-size:16px;line-height:1.7;color:var(--dim)}
.band-light .step,.paper .step,.ctaband .step{border-color:var(--paper-line)}
.band-light .step h3,.paper .step h3,.ctaband .step h3{color:var(--onpaper)}
.band-light .step p,.paper .step p,.ctaband .step p{color:var(--onpaper-mute)}

.tl{display:grid;gap:2px}
.tl-row{display:grid;grid-template-columns:minmax(120px,170px) 1fr;gap:26px;
  padding:20px 0;border-top:1px solid var(--line);align-items:baseline}
.tl-row:last-child{border-bottom:1px solid var(--line)}
.tl-when{font-size:13px;font-weight:700;letter-spacing:.1em;text-transform:uppercase;
  color:var(--ember)}
.tl-what{font-size:16px;line-height:1.7;color:var(--dim)}
.band-light .tl-row,.paper .tl-row,.ctaband .tl-row{border-color:var(--paper-line)}
.band-light .tl-what,.paper .tl-what,.ctaband .tl-what{color:var(--onpaper-mute)}
@media(max-width:640px){
  .step{grid-template-columns:1fr;gap:10px}
  .step-n{padding-top:0}
  .tl-row{grid-template-columns:1fr;gap:6px}
}

/* ── Act 1 step counter ───────────────────────────────────────────────
   The three beats were three dots and nothing else, so a first-time
   visitor had no way to know the sequence had a length — or that the
   payoff was still two scrolls away. A visible "n of 3" is the cheapest
   possible fix for "I do not know if this is going anywhere". */
.act-steps{display:flex;align-items:center;justify-content:center;gap:10px;margin-top:22px}
.act-stepn{font-family:'Fraunces',Georgia,serif;font-weight:600;font-size:15px;
  color:var(--ember);min-width:10px;text-align:right;font-variant-numeric:tabular-nums}
.act-stepof{font-size:12px;letter-spacing:.12em;text-transform:uppercase;
  font-weight:600;color:#9aa0a6}

/* ── Act 1: the three beats are three different pictures ──────────────
   The results and the empty slot are hidden until their beat. Before
   this, everything rendered on frame one — including the "your business
   is not here" box — so the visitor saw the answer before the question
   and had no reason to keep scrolling. */
.serp .serp-res{
  opacity:.1;transform:translateY(10px);
  transition:opacity .55s cubic-bezier(.2,.7,.2,1),transform .55s cubic-bezier(.2,.7,.2,1)
}
.serp .serp-gap{
  opacity:0;transform:translateY(10px);
  transition:opacity .55s cubic-bezier(.2,.7,.2,1),transform .55s cubic-bezier(.2,.7,.2,1)
}
/* beat 1 — the competitors arrive, staggered so it reads as a page loading */
.serp.b1 .serp-res,
.serp.b2 .serp-res{opacity:1;transform:none}
.serp.b1 #r0,.serp.b2 #r0{transition-delay:.05s}
.serp.b1 #r1,.serp.b2 #r1{transition-delay:.15s}
.serp.b1 #r2,.serp.b2 #r2{transition-delay:.25s}
/* beat 2 — they recede, and the hole where you should be appears */
.serp.b2 .serp-res{opacity:.3}
.serp.b2 .serp-gap{opacity:1;transform:none;transition-delay:.2s}
@media(prefers-reduced-motion:reduce){
  .serp .serp-res,.serp .serp-gap{transition:opacity .2s;transform:none}
}

/* ── The path: six places one customer can be lost ────────────────────
   The homepage dramatised a single failure (search) across two full
   scroll acts, then listed the other five services as small cards. This
   section is the connective tissue: one customer's journey, the six
   points they can vanish at, and which service catches each. */
.journey{display:grid;gap:2px;max-width:940px;margin-inline:auto}
.jrow{display:grid;grid-template-columns:auto 1fr auto;gap:clamp(18px,3vw,38px);
  align-items:start;padding:26px 0;border-top:1px solid var(--line)}
.jrow:last-child{border-bottom:1px solid var(--line)}
.jnum{font-family:'Fraunces',Georgia,serif;font-weight:600;font-size:15px;
  color:var(--ember);padding-top:4px;font-variant-numeric:tabular-nums;min-width:26px}
.jbody h3{margin:0 0 8px;color:var(--txt)}
.jbody p{margin:0;font-size:16px;line-height:1.7;color:var(--dim);max-width:62ch}
.jfix{display:flex;flex-direction:column;align-items:flex-end;gap:8px;
  min-width:150px;text-align:right}
.jtag{font-size:11px;letter-spacing:.14em;text-transform:uppercase;font-weight:700;
  color:var(--mute)}
.jfix a{font-size:14.5px;font-weight:600;color:var(--ember);text-decoration:none;
  white-space:nowrap;transition:color .18s}
.jfix a:hover{color:var(--ember-l)}
@media(max-width:820px){
  .jrow{grid-template-columns:auto 1fr;gap:16px}
  .jfix{grid-column:2;align-items:flex-start;text-align:left;margin-top:12px}
}

/* The result below the slot stays visible now. Collapsing it was an
   earlier workaround for the clipping; the slide handles that properly,
   and keeping r2 on screen is better — it shows the list continuing past
   the hole rather than ending at it. */

/* ── The path: give each row its own arrival ──────────────────────────
   All six rows shared one reveal, so the section appeared in a single
   block and read as static. Staggering them makes the path feel walked
   rather than presented. */
.journey .jrow{transform:translateY(26px)}
.journey .jrow.in{transform:none}
.journey .jrow:nth-child(1){transition-delay:.00s}
.journey .jrow:nth-child(2){transition-delay:.07s}
.journey .jrow:nth-child(3){transition-delay:.14s}
.journey .jrow:nth-child(4){transition-delay:.21s}
.journey .jrow:nth-child(5){transition-delay:.28s}
.journey .jrow:nth-child(6){transition-delay:.35s}
.journey .jnum{transition:color .4s,transform .5s}
.journey .jrow.in .jnum{color:var(--ember)}
@media(prefers-reduced-motion:reduce){
  .journey .jrow{transform:none}
  .journey .jrow{transition-delay:0s!important}
  .serp #r2,.serp.b2 #r2{transition:none}
}

/* ── Act 1: the SERP slides inside the frame ──────────────────────────
   The results list is deliberately taller than the browser window. Rather
   than shrinking everything until it fitted — which made the payoff cramped
   and still clipped it — the frame stays put and the SERP moves inside it,
   the way a real page scrolls. Offsets are measured, not guessed: the slot
   sat 461px into a 636px frame, so centring it needs -195px. */
.serp{transition:transform .75s cubic-bezier(.22,.65,.24,1)}
.serp.b0{transform:translateY(0)}
.serp.b1{transform:translateY(-38px)}
.serp.b2{transform:translateY(-195px)}
.serp-gap{transition:transform .75s cubic-bezier(.22,.65,.24,1)}
.serp.b2 .serp-gap{transform:scale(1.03)}

/* ── Act 1 caption: one line at a time, animated in and out ───────────
   The reader should be able to read the line AND watch the screen, so the
   type carries its own motion rather than appearing all at once.

   The three lines share one absolutely-positioned slot inside .act-lines.
   That wrapper matters: when the <p>s were absolute children of .act-cap
   directly, the beat dots — a normal-flow sibling — ended up underneath the
   text and rendered straight through it. */
.act-cap{position:static;text-align:center;padding:0 var(--pad);pointer-events:none}
.act-lines{position:relative;min-height:clamp(132px,16vh,168px)}
.act-cap p{
  /* max-width:none is not redundant — the v1 .act-cap p rule further up sets
     max-width:20ch, which silently clamped these to well under the 26ch the
     longest line needs and pushed it to a third row that overhangs the slot
     and lands on the beat dots. */
  position:absolute;left:50%;top:50%;width:min(90vw,26ch);max-width:none;
  font-size:clamp(22px,3vw,38px);line-height:1.18;margin:0;
  transition:opacity .62s cubic-bezier(.22,.65,.24,1),
             transform .62s cubic-bezier(.22,.65,.24,1);
  will-change:opacity,transform
}
.act-cap p.is-future{opacity:0;transform:translate(-50%,calc(-50% + 30px)) scale(.94)}
.act-cap p.is-in    {opacity:1;transform:translate(-50%,-50%) scale(1)}
.act-cap p.is-past  {opacity:0;transform:translate(-50%,calc(-50% - 34px)) scale(1.07)}
.act-cap p.act-punch{color:var(--ember)}

@media(prefers-reduced-motion:reduce){
  .serp,.serp-gap,.act-cap p{transition:none}
  .serp.b1,.serp.b2{transform:translateY(-195px)}
  .act-cap p.is-past,.act-cap p.is-future{opacity:0}
}
