/* ── typefaces ──────────────────────────────────────────────────────────
   The typography follows Anthropic's own system: a text serif carries the
   voice, a grotesque handles the interface. Their faces — Styrene and
   Tiempos / Copernicus — are licensed from Klim and cannot be embedded, so
   these are the closest open equivalents:

     SF Pro Display / Text — the system faces on Apple platforms, named
     first in --font-display / --font. Instrument Sans is the self-hosted
     stand-in everywhere else; Source Serif 4 was removed with the serif
     scale it served, and its two woff2 files no longer ship.

   Self-hosted latin subsets, 126 KB for all three — no third-party request
   on the critical path. `swap`, so text paints immediately in the fallback
   and reflows once.

   Weights are held to 400 Regular, 500 Medium and 600 Semibold — the
   whole SF Pro scale is built from those three. */
/* Host Grotesk is the grotesque in the supplied reference. It is on Google
   Fonts under the OFL, so the real face is self-hosted here rather than
   approximated — variable 300-800, so 600 is a drawn instance and not a
   synthesised bold. */
@font-face {
  font-family: 'Host Grotesk';
  font-style: normal; font-weight: 300 800; font-display: swap;
  src: url('assets/fonts/host-grotesk-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+2000-206F, U+2122, U+2212;
}
/* The editorial serif, used only for italic emphasis phrases — never for
   body copy.

   WHY THIS ONE. Six candidates were rendered against the sans at the same
   size and measured for ink density, advance width and stroke contrast.
   Instrument Serif, which was here before, came out at 0.79 the visual
   weight of the sans and the narrowest advance of the set — beside a 500
   grotesque it read thin and cramped rather than as its equal. Bodoni Moda
   was worse at 0.56, the fragile fashion serif this must not be.

   DM Serif Display measures 0.98 against the sans: it carries the same
   visual weight, so the accent phrase stands beside the headline instead of
   shrinking away from it. Broad rather than condensed, real thick/thin
   contrast, sharp bracketed serifs, a genuine pronounced italic — and it
   ships one weight, 400, so there is no way to accidentally set it bold.

   SIL OFL, self-hosted; nothing leaves the origin at runtime. Only the
   italic is declared, since that is the only way it is ever used. */
@font-face {
  font-family: 'DM Serif Display';
  font-style: italic; font-weight: 400; font-display: swap;
  src: url('assets/fonts/dm-serif-display-italic-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+2000-206F, U+2122, U+2212;
}
@font-face {
  font-family: 'Instrument Sans';
  font-style: normal; font-weight: 400 700; font-display: swap;
  src: url('assets/fonts/instrument-sans-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+2000-206F, U+2122, U+2212;
}
/* Buttons and form controls do not inherit font-family from the document,
   so without this they fall back to the UA default — which is what had the
   burger's "Menu" / "Close" labels rendering in Arial. */
button, input, select, textarea { font-family: inherit; }

/* ── theme tokens ───────────────────────────────────────────────────── */
:root {
  /* WHITE. The ground was a warm ivory; it is plain white now, and the site
     ships in this one theme — the toggle, the account-menu radios and the
     FAQ night-mode control are all gone, and the boot script pins
     data-theme to light. The dark rules further down no longer match
     anything; they are left in place rather than torn out of two thousand
     lines, and cost nothing unmatched.

     Cards used to lift off the ivory by being lighter than it. On white
     there is nothing lighter to be, so separation moves to the hairline and
     the shadow, and --surface-2 keeps just enough tone to read as a
     recessed band without tinting the page. */
  --bg: #ffffff;
  --surface: #ffffff;        /* raised: the hairline does the lifting now */
  --surface-2: #f6f6f4;      /* recessed, barely */
  --ink: #1a1310;
  /* Body and caption text on light grounds is near-black, not grey. The
     ramp used to run #4a423b / #5d554e and, in the newer sections, into
     the #6E6E6E range; on request every one of those is taken down to
     near-black. Hierarchy is now carried by size, weight and space rather
     than by fading the colour out.

     Only the light theme moves. All four dark contexts — data-theme=dark
     and the nova, atlas and lab skins — redefine both tokens for their own
     grounds, so light-on-dark text is untouched. Checked before editing,
     because taking these to black there would have erased the text. */
  --muted: #1C1C1C;   /* near-black body copy, 16.9:1 on white */
  --faint: #262626;   /* near-black captions, 14.5:1 — still a step under --ink */
  --line: #e6e4de;
  --line-soft: #efedE8;
  --line-strong: #cfcbc0;
  /* The site was re-themed to the logo's red, but only --accent-a/-b were
     repointed — --brand kept the pre-rebrand orange, so every control
     still reading it (the Get Started button, the carousel dots, focus
     rings) stayed orange on a red page. One family now. */
  --brand: #ae0e20;
  --io-ground: #fafafa;                          /* the ground the cards sit on */
  --io-pill:   #f3f3f3;                          /* the pill starts a shade below it */
  --io-dot-b:  #4b0306;                          /* foot of the active dot's ramp */
  --io-grey-a: #574d46; --io-grey-b: #70645a;   /* 7.6:1 → 4.94:1 on white */
  --io-red-a:  #c01327; --io-red-b:  #8a0007;   /* 6.3:1 → 10.4:1 on white */
  --brand-2: #c31428;
  --brand-soft: rgba(174,14,32,.08);
  --brand-line: #e8b9be;
  --on-brand: #ffffff;
  --grid: rgba(60,48,32,.09);
  --grid-hi: rgba(60,48,32,.34);
  /* dot lattice — mobile/tablet only (see the hero background override) */
  --dot: rgba(60,48,32,.26);
  --hover: rgba(60,48,32,.055);
  --sh: 20,20,40;
  color-scheme: light;
}
:root[data-theme="dark"] {
  --bg: #141413;            /* Anthropic slate */
  --surface: #1f1e1d;
  --surface-2: #262624;
  --ink: #f6f3f1;
  --muted: #b0a49d;
  --faint: #988b83;   /* 5.58:1 on slate, 4.59:1 on surface-2 */
  --line: #35342f;
  --line-soft: #292825;
  --line-strong: #4a4842;
  --brand: #e2566b;               /* lifted for the dark ground: 5.4:1 */
  --io-ground: #191918;                          /* recessed from --surface so cards lift */
  --io-pill:   #141413;
  --io-dot-b:  #8a2740;
  --io-grey-a: #c4b8b1; --io-grey-b: #988b83;
  --io-red-a:  #f08698; --io-red-b:  #e2566b;
  --brand-2: #f08496;
  --brand-soft: rgba(226,86,107,.15);
  --brand-line: #7d2735;
  --on-brand: #14100c;
  --grid: rgba(255,255,255,.075);
  --grid-hi: rgba(255,255,255,.30);
  --dot: rgba(255,255,255,.26);
  --hover: rgba(255,255,255,.06);
  --sh: 0,0,0;
  color-scheme: dark;
  --accent-ink: #f2707f;   /* 5.9:1 on the dark card */
}

:root {
  --font: -apple-system, BlinkMacSystemFont, 'SF Pro Text', system-ui, 'Instrument Sans', 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
  /* display face: headline, section headings, the rotating term */
  /* SF PRO. Apple licenses SF Pro for its own platforms only — it cannot
     be self-hosted or fetched from a CDN, and doing so would breach both
     the licence and Rules §9. It IS present as a system face on macOS and
     iOS, so the stack names it first and resolves to the real thing there.
     Everywhere else the already self-hosted Instrument Sans stands in: a
     grotesque of the same proportions, so the scale below holds either
     way. Display and Text are separate stacks because SF Pro ships as two
     optical sizes — Display is drawn for large type, Text for small. */
  --font-display: -apple-system, BlinkMacSystemFont, 'SF Pro Display', system-ui, 'Instrument Sans', 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
  --ease: cubic-bezier(.22, 1, .36, 1);

  /* The feature-card glyphs. Lucide (ISC), self-hosted as masks like the
     rest of this set — the reference's own icon files are its brand
     assets, and these carry the same weight without borrowing them.
     Stroke is 2.4 rather than Lucide's 2 so they read solid at 34px,
     which is how the reference chips read. */
  --i-wy1: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round'><path d='M2 21a8 8 0 0 1 13.292-6' /> <circle cx='10' cy='8' r='5' /> <path d='m16 19 2 2 4-4' /></svg>");
  --i-wy2: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round'><path d='M8 2v3' /> <path d='M16 2v3' /> <rect x='3' y='3' width='18' height='18' rx='2' /> <path d='M3 9h18' /> <path d='m9 15 2 2 4-4' /></svg>");
  --i-wy3: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round'><path d='M3.85 8.62a4 4 0 0 1 4.78-4.77 4 4 0 0 1 6.74 0 4 4 0 0 1 4.78 4.78 4 4 0 0 1 0 6.74 4 4 0 0 1-4.77 4.78 4 4 0 0 1-6.75 0 4 4 0 0 1-4.78-4.77 4 4 0 0 1 0-6.76Z' /> <path d='m9 12 2 2 4-4' /></svg>");
  --i-wy4: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round'><path d='M16 7h6v6' /> <path d='m22 7-8.5 8.5-5-5L2 17' /></svg>");

  /* ── EDITORIAL TYPE SYSTEM ───────────────────────────────────────────
     One place where the face, scale, tracking, leading and ink of the
     editorial treatment are defined. The hero consumes it today; the rest
     of the site still runs on --font / --font-display above and migrates
     section by section, which is why both systems are declared side by
     side rather than one overwriting the other.

     --font-sans names the self-hosted grotesque FIRST, unlike --font,
     which is deliberately system-first. The two coexist: --font is for
     the parts of the site not yet migrated.

     Every size is a clamp, so desktop, tablet and mobile interpolate
     rather than snapping at a breakpoint. The min of each is the mobile
     figure and the max is the desktop figure. */
  --font-sans: 'Host Grotesk', 'Instrument Sans', -apple-system, BlinkMacSystemFont, system-ui, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
  --font-serif: 'DM Serif Display', ui-serif, Georgia, 'Times New Roman', Times, serif;

  /* A bare vw slope hits desktop and mobile but sags through the middle:
     at 4.6vw a 768px tablet computed 35px against a 48-58px target and just
     sat on the clamp floor. The px offset lifts the whole line so all three
     bands land where they should — 43 / 52 / 66 at 375 / 768 / 1440. */
  --text-display: clamp(38px, 30px + 2.7vw, 66px);  /* hero statement    */
  --text-h2:      clamp(30px, 26px + 1.7vw, 50px);  /* major statement   */
  --text-h3:      clamp(26px, 22px + 1.2vw, 38px);
  --text-lead:    clamp(18px, 15px + .5vw, 22px);   /* supporting line   */
  --text-body:    clamp(16px, 1.15vw, 17px);
  --text-ui:      clamp(16px, 1.25vw, 17.6px); /* button labels      */
  --text-small:   clamp(14px, 12px + .3vw, 16px); /* badges, micro labels */

  /* large type is tracked in, small type is not — the tighter the closer
     to display size, which is what keeps big headlines from looking loose */
  --tracking-display: -.032em;
  --tracking-tight:   -.025em;
  --tracking-body:    -.008em;
  --tracking-label:    .01em;

  --leading-display: 1.02;
  --leading-tight:   1.12;
  --leading-h3:      1.2;
  --leading-lead:    1.45;
  --leading-body:    1.6;
  --leading-ui:      1.3;

  /* near-black rather than black, and a warm-shifted secondary so text on
     the sunrise frames does not read as cold grey */
  --ink-primary:   #212121;
  /* The reference sets its subline in a light terracotta (#945B4D). That
     colour needs a near-white sky behind it — theirs has one; ours is a
     photograph, and measured on it that terracotta reads at 2.15:1 against
     a 4.5:1 requirement, which is not legible. Same warm family, taken deep
     enough to hold on the clouds: 4.8:1 at the worst frame. */
  --ink-secondary: #4e2c25;
  /* The accent is the reference's measured gradient, not a flat fill: a
     horizontal sweep that is brighter at both ends than in the middle, so
     the phrase reads as ink rather than as a highlighter. */
  /* Sampled off the supplied logo gradient — #ae0e20 at the top of the
     tile, #8f0914 at its darkest. The previous pair (#9b0410 / #7a0006)
     was a shade darker and more orange, so the brand mark and the site
     did not quite agree with each other. */
  --accent-a: #ae0e20;
  --accent-b: #8f0914;
  --ink-editorial: var(--accent-a);   /* flat fallback where clip is absent */

  /* The editorial accent for text on the PAGE, as opposed to on the hero.
     --accent-a is a deep crimson chosen against bright cloud; on a near-black
     panel it measures 1.75:1 and all but disappears. This one follows the
     theme: the same colour on light, a warm coral on dark. Anything printing
     the accent over page surfaces uses this, not --accent-a. */
  --accent-ink: var(--accent-a);

  /* contact-card glyphs (lucide: mail, clock, help-circle, plus) */
  --i-mail:  url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'><rect x='2' y='4' width='20' height='16' rx='2'/><path d='m22 7-8.97 5.7a1.94 1.94 0 0 1-2.06 0L2 7'/></svg>");
  --i-clock: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'><circle cx='12' cy='12' r='10'/><path d='M12 6v6l4 2'/></svg>");
  --i-help:  url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'><circle cx='12' cy='12' r='10'/><path d='M9.1 9a3 3 0 0 1 5.8 1c0 2-3 3-3 3'/><path d='M12 17h.01'/></svg>");
  --i-wa:    url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='black'><path d='M17.472 14.382c-.297-.149-1.758-.867-2.03-.967-.273-.099-.471-.148-.67.15-.197.297-.767.966-.94 1.164-.173.199-.347.223-.644.075-.297-.15-1.255-.463-2.39-1.475-.883-.788-1.48-1.761-1.653-2.059-.173-.297-.018-.458.13-.606.134-.133.297-.347.446-.52.149-.174.198-.298.298-.497.099-.198.05-.371-.025-.52-.075-.149-.669-1.612-.916-2.207-.242-.579-.487-.5-.669-.51-.173-.008-.371-.01-.57-.01-.198 0-.52.074-.792.372-.272.297-1.04 1.016-1.04 2.479 0 1.462 1.065 2.875 1.213 3.074.149.198 2.096 3.2 5.077 4.487.709.306 1.262.489 1.694.625.712.227 1.36.195 1.871.118.571-.085 1.758-.719 2.006-1.413.248-.694.248-1.289.173-1.413-.074-.124-.272-.198-.57-.347m-5.421 7.403h-.004a9.87 9.87 0 0 1-5.031-1.378l-.361-.214-3.741.982.998-3.648-.235-.374a9.86 9.86 0 0 1-1.51-5.26c.001-5.45 4.436-9.884 9.888-9.884 2.64 0 5.122 1.03 6.988 2.898a9.825 9.825 0 0 1 2.893 6.994c-.003 5.45-4.437 9.884-9.885 9.884m8.413-18.297A11.815 11.815 0 0 0 12.05 0C5.495 0 .16 5.335.157 11.892c0 2.096.547 4.142 1.588 5.945L.057 24l6.305-1.654a11.882 11.882 0 0 0 5.683 1.448h.005c6.554 0 11.89-5.335 11.893-11.893a11.821 11.821 0 0 0-3.48-8.413z'/></svg>");
  --i-call:  url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.9' stroke-linecap='round' stroke-linejoin='round'><path d='M22 16.9v3a2 2 0 0 1-2.2 2 19.8 19.8 0 0 1-8.6-3.1 19.5 19.5 0 0 1-6-6A19.8 19.8 0 0 1 2.1 4.2 2 2 0 0 1 4.1 2h3a2 2 0 0 1 2 1.7c.1 1 .3 1.9.6 2.8a2 2 0 0 1-.5 2.1L8 9.8a16 16 0 0 0 6 6l1.2-1.2a2 2 0 0 1 2.1-.5c.9.3 1.8.5 2.8.6a2 2 0 0 1 1.7 2z'/></svg>");
  --i-cam:   url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.9' stroke-linecap='round' stroke-linejoin='round'><rect x='2' y='6' width='13' height='12' rx='2.4'/><path d='M15 10.5l6-3.4v9.8l-6-3.4z'/></svg>");
  --i-tick:  url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round'><path d='M4 12.5l5.5 5.5L20 7'/></svg>");
  --i-clock2: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'><circle cx='12' cy='12' r='9'/><path d='M12 7v5.2l3.4 2'/></svg>");
  --i-warn:  url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'><path d='M12 3.4L22 20.6H2z'/><path d='M12 10v4.4'/><circle cx='12' cy='17.6' r='.2' fill='black' stroke-width='1.4'/></svg>");
  --i-plus:  url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.5' stroke-linecap='round'><path d='M12 5v14M5 12h14'/></svg>");

  /* glyphs the illustration components mask against */
  --i-chev: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'><path d='M6 9l6 6 6-6'/></svg>");
  --i-doc:  url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'><path d='M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z'/><path d='M14 2v6h6M9 13h6M9 17h6'/></svg>");
  --i-up:   url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round'><path d='M20 6L9 17l-5-5'/></svg>");
  --i-dn:   url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round'><path d='M12 8v5M12 16.5v.01'/><circle cx='12' cy='12' r='9'/></svg>");
  --i-user: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round'><circle cx='12' cy='8' r='3.6'/><path d='M4.5 20a7.5 7.5 0 0 1 15 0'/></svg>");
  --i-stack:url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linejoin='round'><path d='M12 2l9 5-9 5-9-5z'/><path d='M3 12l9 5 9-5M3 17l9 5 9-5'/></svg>");
  --i-cal:  url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round'><rect x='3' y='5' width='18' height='16' rx='2'/><path d='M3 10h18M8 3v4M16 3v4'/></svg>");
  --i-lab:  url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'><path d='M9 3v6L4 19a2 2 0 0 0 1.8 3h12.4A2 2 0 0 0 20 19L15 9V3'/><path d='M8 3h8'/></svg>");

  /* ── SECTION RHYTHM ────────────────────────────────────────────────
     Section padding STACKS: a section's bottom meets the next one's top,
     so the visible band between two sections is twice this. At the old
     108px every pair of .sec blocks was putting 216px of empty page
     between them, which is roughly double what a corporate site runs.

     56px a side = a 112px band at desktop, 72px on a phone. One number,
     one place — every full-width section reads from it. */
  --sec-y: clamp(36px, 4.2vw, 56px);

  /* ── PRODUCT-SECTION TOKENS ──────────────────────────────────────────
     The reference runs five near-identical darks (#212121 #242424 #383838
     #474747) and two reds one digit apart (#B1060F / #B10612). That is an
     accident of a design that grew, not a scale — consolidated to four
     steps and one red before anything was built on it, so the section has
     a palette rather than a collection.

     One shadow colour throughout, at 5%: the reference's dozen fractional
     variants are all the same recipe scaled, not separate decisions. */
  --pr-ground:  #ffffff;   /* the section and illustration ground */
  --pr-card:    #ffffff;
  --pr-chip:    #f2f2f2;
  --pr-rail:    #e7e7e7;
  --pr-dash:    #c4c4c4;
  --pr-wire:    #c8102a;   /* the connectors — a step brighter than the ink red */
  --pr-ink:     #212121;   /* headline, lead-ins */
  --pr-ink-2:   #3a3a3a;   /* illustration labels */
  --pr-ink-3:   #3e3e3e;   /* 10.8:1 on white */   /* secondary labels */
  --pr-body:    #474747;   /* 9.1:1 on white */   /* every paragraph */
  --pr-shadow:  0 4px 11px rgba(0, 0, 0, .05);
  --pr-shadow-s:0 1px 2px rgba(0, 0, 0, .05);
  --pr-bevel:   inset 0 3px 0 rgba(255, 255, 255, .35);

  /* colour is released only where it encodes something */
  --pr-ok:   #047857;
  --pr-warn: #c2410c;
  --pr-note: #7e22ce;
  --pr-due:  #be185d;
  --pr-idle: #6b7280;

  /* menu icons — masked so they inherit the brand colour (no icon library) */
  --i-arrow: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="none" stroke="black" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"><path d="M3 8h10M9 4l4 4-4 4"/></svg>');
  --i-board: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="none" stroke="black" stroke-width="1.4" stroke-linejoin="round"><rect x="2" y="3" width="3.4" height="10" rx="1"/><rect x="6.3" y="3" width="3.4" height="7" rx="1"/><rect x="10.6" y="3" width="3.4" height="10" rx="1"/></svg>');
  --i-star: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="none" stroke="black" stroke-width="1.4" stroke-linejoin="round"><path d="M8 2l1.8 3.8 4.2.6-3 2.9.7 4.1L8 11.5 4.3 13.4l.7-4.1-3-2.9 4.2-.6z"/></svg>');
  --i-book: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="none" stroke="black" stroke-width="1.4" stroke-linejoin="round"><path d="M4 2.5h6a1.5 1.5 0 0 1 1.5 1.5v9.5l-4.5-2-4.5 2V4A1.5 1.5 0 0 1 4 2.5z"/></svg>');
  --i-users: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="none" stroke="black" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round"><circle cx="6" cy="6" r="2.4"/><path d="M2 13.4a4 4 0 0 1 8 0"/><path d="M11 4.2a2.4 2.4 0 0 1 0 4.6"/><path d="M11.6 13.4a4 4 0 0 0-1.5-3.1"/></svg>');
  --i-chart: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="none" stroke="black" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round"><path d="M2.5 13.5h11"/><rect x="3.6" y="8" width="2.4" height="3.8" rx="0.6"/><rect x="7" y="4.6" width="2.4" height="7.2" rx="0.6"/><rect x="10.4" y="6.8" width="2.4" height="5" rx="0.6"/></svg>');
  --i-shield: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="none" stroke="black" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round"><path d="M8 2l5 2v4c0 3.2-2.1 5.4-5 6.4C5.1 13.4 3 11.2 3 8V4z"/><path d="M5.9 8l1.5 1.5 2.8-3"/></svg>');
  --i-bolt: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="none" stroke="black" stroke-width="1.4" stroke-linejoin="round"><path d="M9 2L4 9h3.5L7 14l5-7H8.5z"/></svg>');
  --i-id: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="none" stroke="black" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round"><rect x="2" y="3.5" width="12" height="9" rx="2"/><circle cx="6" cy="7.4" r="1.5"/><path d="M3.7 11.2a2.7 2.7 0 0 1 4.6 0"/><path d="M10.2 7h2.4M10.2 9.4h2.4"/></svg>');
  --i-clock: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="none" stroke="black" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round"><circle cx="8" cy="8" r="5.6"/><path d="M8 4.8V8l2.2 1.6"/></svg>');
  --i-cap: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="none" stroke="black" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round"><path d="M8 3l6 3-6 3-6-3z"/><path d="M4.5 8v3c0 1 1.6 2 3.5 2s3.5-1 3.5-2V8"/></svg>');
  --i-lab: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="none" stroke="black" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round"><path d="M6.5 2v4.2L3 12a1.4 1.4 0 0 0 1.2 2h7.6A1.4 1.4 0 0 0 13 12L9.5 6.2V2"/><path d="M5.6 2h4.8"/></svg>');
  --i-building: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="none" stroke="black" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="2.5" width="10" height="11" rx="1.5"/><path d="M6 5.6h1.4M6 8h1.4M6 10.4h1.4M8.8 5.6h1.4M8.8 8h1.4M8.8 10.4h1.4"/></svg>');
  --i-code: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="none" stroke="black" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round"><path d="M5.6 5L2.5 8l3.1 3"/><path d="M10.4 5L13.5 8l-3.1 3"/><path d="M9 3.2l-2 9.6"/></svg>');
  --i-doc: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="none" stroke="black" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round"><path d="M9 1.8H4.6A1.6 1.6 0 0 0 3 3.4v9.2a1.6 1.6 0 0 0 1.6 1.6h6.8a1.6 1.6 0 0 0 1.6-1.6V5.6z"/><path d="M9 1.8v3.8h4"/><path d="M5.6 8.6h4.8M5.6 11h3.2"/></svg>');
  --i-search: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="none" stroke="black" stroke-width="1.4" stroke-linecap="round"><circle cx="7" cy="7" r="4.4"/><path d="M10.3 10.3L13.6 13.6"/></svg>');
  --i-pen: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="none" stroke="black" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round"><path d="M11.2 2.4l2.4 2.4-8 8-3.1.7.7-3.1z"/><path d="M9.6 4l2.4 2.4"/></svg>');
  --i-award: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="none" stroke="black" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round"><circle cx="8" cy="6.2" r="3.8"/><path d="M5.8 9.4L5 14l3-1.6L11 14l-.8-4.6"/></svg>');
  --i-tools: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="none" stroke="black" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round"><path d="M9.8 5.2a2.6 2.6 0 0 1 3.4-3.4l-2 2 1.4 1.4 2-2a2.6 2.6 0 0 1-3.4 3.4L4.6 13.2a1.5 1.5 0 0 1-2.1-2.1z"/></svg>');
  --i-sun: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="none" stroke="black" stroke-width="1.4" stroke-linecap="round"><circle cx="8" cy="8" r="3"/><path d="M8 1.6v1.6M8 12.8v1.6M1.6 8h1.6M12.8 8h1.6M3.5 3.5l1.1 1.1M11.4 11.4l1.1 1.1M12.5 3.5l-1.1 1.1M4.6 11.4l-1.1 1.1"/></svg>');
  --i-moon: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="none" stroke="black" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round"><path d="M13.4 9.5A5.6 5.6 0 0 1 6.5 2.6a5.7 5.7 0 1 0 6.9 6.9z"/></svg>');
  --i-bell: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="none" stroke="black" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round"><path d="M4 6.6a4 4 0 0 1 8 0c0 3.2 1 4 1 4H3s1-.8 1-4z"/><path d="M6.6 13a1.6 1.6 0 0 0 2.8 0"/></svg>');
  --i-out: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="none" stroke="black" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round"><path d="M6.2 2.5H3.8A1.3 1.3 0 0 0 2.5 3.8v8.4a1.3 1.3 0 0 0 1.3 1.3h2.4"/><path d="M10.5 11.2L13.5 8l-3-3.2"/><path d="M13.5 8H6.2"/></svg>');
  --i-rocket: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="none" stroke="black" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round"><path d="M6.4 9.6C4.2 8.8 2.6 10 2 14c4-.6 5.2-2.2 4.4-4.4z"/><path d="M7.4 8.6l-1-1c.6-3.4 2.8-5.6 7.2-5.8-.2 4.4-2.4 6.6-5.8 7.2z"/><circle cx="10.4" cy="5.6" r="1"/></svg>');
}

* { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }
body {
  margin: 0; background: var(--bg); color: var(--ink);
  font-family: var(--font); -webkit-font-smoothing: antialiased;
  /* the Body rung is the document's baseline, so anything the role layer
     does not name still inherits a size that belongs to the scale */
  font-size: 17px; line-height: 1.53; letter-spacing: 0;
  overflow-x: hidden;
}

/* ── hero shell + interactive lattice ───────────────────────────────── */
/* Two stacked copies of the same grid, perfectly aligned:
   ::before is the faint baseline you always see;
   ::after is a brighter copy revealed only inside a spotlight that follows
   the pointer. Geometry never moves — only visibility changes. */
.hero {
  position: relative; overflow: hidden;
  padding-bottom: clamp(48px, 6vw, 88px);
  --mx: 50%; --my: 26%; --spot: 300px;
  background: var(--bg);
}
.hero::before,
.hero::after {
  content: ''; position: absolute; inset: 0; z-index: 0; pointer-events: none;
  background-size: 1cm 1cm;
  background-position: center top;
}
/* baseline — subtle, but genuinely visible */
.hero::before {
  background-image:
    linear-gradient(to right,  var(--grid) 1px, transparent 1px),
    linear-gradient(to bottom, var(--grid) 1px, transparent 1px);
  -webkit-mask-image: linear-gradient(to bottom, #000 0%, #000 58%, transparent 84%);
  mask-image: linear-gradient(to bottom, #000 0%, #000 58%, transparent 84%);
}
/* spotlight — the same black grid, just darker where the pointer is */
.hero::after {
  background-image:
    linear-gradient(to right,  var(--grid-hi) 1px, transparent 1px),
    linear-gradient(to bottom, var(--grid-hi) 1px, transparent 1px);
  -webkit-mask-image:
    radial-gradient(circle var(--spot) at var(--mx) var(--my),
      #000 0%, rgba(0,0,0,.72) 38%, rgba(0,0,0,.28) 62%, transparent 78%);
  mask-image:
    radial-gradient(circle var(--spot) at var(--mx) var(--my),
      #000 0%, rgba(0,0,0,.72) 38%, rgba(0,0,0,.28) 62%, transparent 78%);
}

/* ── mobile + tablet: video backdrop instead of the ruled grid ───────────
   The component's background layer: a looping muted video pinned behind the
   content with object-fit: cover. Hidden entirely above 1024px, where the
   ruled lattice and its pointer spotlight are untouched. */
.hero-bg { position: absolute; inset: 0; z-index: 0; display: none; overflow: hidden; }
.hero-bg video {
  position: absolute; inset: 0; display: block;
  width: 100%; height: 100%; object-fit: cover;
  /* the aurora lives on the left of the frame, and a tall hero crops `cover`
     horizontally — centred, the visible slice would be the empty side */
  object-position: left center;
}
/* the scrim is what makes the copy readable on both themes: the video is a
   full-strength image, and neither near-white nor near-black text survives on
   top of one unaided. Each theme veils toward its own page colour, strongest
   behind the headline and clearing by the workspace window. */
.hero-scrim {
  position: absolute; inset: 0; display: block; pointer-events: none;
  background:
    linear-gradient(180deg,
      rgba(255,255,255,0) 0%,
      rgba(255,255,255,.16) 28%,
      rgba(255,255,255,.58) 52%,
      rgba(255,255,255,.92) 72%,
      var(--bg) 88%);
}
:root[data-theme="dark"] .hero-scrim {
  background:
    linear-gradient(180deg,
      rgba(13,11,10,.05) 0%,
      rgba(13,11,10,.30) 28%,
      rgba(13,11,10,.68) 52%,
      rgba(13,11,10,.93) 72%,
      var(--bg) 88%);
}

@media (max-width: 1024px) {
  /* the ruled grid and its pointer spotlight give way to the video */
  .hero::before, .hero::after { display: none; }
  .hero-bg { display: block; }
}

/* text left, live workspace right — the window keeps its full scale and
   bleeds off the right edge rather than shrinking to fit the column */
.hero-inner {
  position: relative; z-index: 1;
  display: grid;
  grid-template-columns: minmax(0, clamp(400px, 26vw, 520px)) minmax(0, 1fr);
  /* the gap has to read as a deliberate column break, not a near-miss —
     it scales with the viewport instead of pinching shut on wide screens */
  align-items: center; column-gap: clamp(48px, 4vw, 110px);
  /* the workspace column stops short of the screen edge, never touches it */
  padding: clamp(32px, 4vw, 68px) var(--dash-edge) 0 var(--edge);
}

/* ── sticky header ──────────────────────────────────────────────────── */
/* ONE shell for the whole site. The header, the hero copy, the marquee, every
   section and the footer all resolve their left edge from these two values, so
   nothing on the page can drift out of alignment with anything else. */
:root {
  --hdr: 72px; --hdr-gap: 14px; --hdr-side: 16px; --shell: 1780px; --gutter: clamp(20px, 4vw, 48px);
  /* breathing room between the workspace window and the screen edge: 1–2cm
     (38–76px) across the whole desktop range */
  --dash-edge: clamp(40px, 3vw, 72px);
}
/* distance from the viewport edge to the shell's content edge */
.hero-inner { --edge: calc(max(0px, (100% - var(--shell)) / 2) + var(--gutter)); }
/* A floating, rounded bar — never a hard-edged box. It inches in and rounds
   further as you scroll rather than switching between two shapes, so the
   header reads as one object that settles. --hdr-gap drives both the inset
   here and the hero's pull-up below; the drawer breakpoint zeroes it, where a
   detached bar would just eat vertical space. */
.site-header {
  position: sticky; top: var(--hdr-gap); z-index: 60;
  width: calc(100% - var(--hdr-side) * 2);
  margin: var(--hdr-gap) auto 0; max-width: 1600px;
  /* opaque, always — a translucent bar lets the lattice read through the menu */
  background: var(--surface);
  border-radius: 20px;
  box-shadow: 0 6px 22px -12px rgba(var(--sh), .18);
  transition: max-width .32s var(--ease), top .32s var(--ease),
              border-color .28s, box-shadow .28s, border-radius .32s var(--ease);
}
/* condensed state — set by JS past the scroll threshold */
.site-header.scrolled {
  max-width: 1120px; border-radius: 24px;
  box-shadow: 0 0 0 1px var(--line), 0 14px 40px -12px rgba(var(--sh), .16);
}
/* The bar is inset by --hdr-side, which would push the brand that far right of
   every other left edge on the page. Its padding gives the inset back. Past
   shell + 2*side the bar stops being edge-limited, its own centring absorbs
   the inset, and the compensation has to stop or it over-corrects. */
.hdr-inner {
  display: flex; align-items: center; gap: 32px;
  max-width: var(--shell); margin: 0 auto;
  height: var(--hdr); padding: 0 calc(var(--gutter) - var(--hdr-side));
  transition: padding .32s var(--ease);
}
/* Once the viewport passes the shell, the page starts centring its content and
   the bar has to centre with it. It can only do that while its own content box
   is still at least a shell wide — so from that point the inset ramps in from
   zero rather than being a flat 16px, and the compensation above stops. Ramp
   and hand-off are both smooth; nothing steps. */
@media (min-width: 1520px) {
  :root { --hdr-side: min(16px, calc((100vw - var(--shell)) / 2)); }
  .hdr-inner { padding-inline: var(--gutter); }
}
.site-header.scrolled .hdr-inner { padding: 0 clamp(12px, 2vw, 20px); }
/* hero slides under the header so the lattice runs behind it — and past the
   float gap as well, so the bar sits ON the hero rather than above a strip
   of page background */
.hero {
  margin-top: calc((var(--hdr) + var(--hdr-gap)) * -1);
  padding-top: calc(var(--hdr) + var(--hdr-gap));
}

/* ── mobile toggle ──────────────────────────────────────────────────── */
.burger {
  display: none; margin-left: auto;
  width: 40px; height: 40px; border-radius: 10px;
  align-items: center; justify-content: center;
  border: 1px solid var(--line); background: var(--surface);
  color: var(--ink); cursor: pointer;
}
.menu-icon { width: 21px; height: 21px; transition: transform .3s ease-in-out; }
.menu-icon .mi-morph {
  stroke-dasharray: 12 63;
  transition: stroke-dasharray .3s ease-in-out, stroke-dashoffset .3s ease-in-out;
}
.burger[aria-expanded="true"] .menu-icon { transform: rotate(-45deg); }
.burger[aria-expanded="true"] .mi-morph { stroke-dasharray: 20 300; stroke-dashoffset: -32.42px; }

/* ── mobile panel ───────────────────────────────────────────────────── */
.mobile-panel {
  position: fixed; inset: var(--hdr) 0 0; z-index: 55;
  display: flex; flex-direction: column;
  background: var(--surface);
  -webkit-backdrop-filter: blur(12px); backdrop-filter: blur(12px);
  border-top: 1px solid var(--line);
  animation: mp-in .26s var(--ease) both;
}
.mobile-panel[hidden] { display: none; }
@keyframes mp-in { from { opacity: 0; transform: scale(.985); } }
.mp-scroll { flex: 1; overflow-y: auto; padding: 18px 20px 8px; -webkit-overflow-scrolling: touch; }
.mp-scroll h6 {
  margin: 18px 0 6px; font-size: 11px; font-weight: 700;
  letter-spacing: 0.08em; text-transform: uppercase; color: var(--faint);
}
.mp-scroll h6:first-child { margin-top: 0; }
/* ── mobile accordion ───────────────────────────────────────────────── */
.acc { border-bottom: 1px solid var(--line); }
.acc-t {
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  width: 100%; padding: 16px 4px; border: 0; background: none;
  font: inherit; font-family: var(--font-display);
  font-size: 18px; font-weight: 400; letter-spacing: 0; color: var(--ink);
  cursor: pointer; text-align: left;
}
.acc-t .chev { width: 14px; height: 14px; transition: transform .26s var(--ease); }
.acc-t[aria-expanded="true"] .chev { transform: rotate(180deg); }
/* grid-template-rows animates to auto-height without measuring in JS */
.acc-c {
  display: grid; grid-template-rows: 0fr;
  transition: grid-template-rows .3s var(--ease);
}
.acc-t[aria-expanded="true"] + .acc-c { grid-template-rows: 1fr; }
/* must be a single wrapper: 0fr sizes only the first row, so direct
   children beyond the first would stay visible when collapsed */
.acc-in { overflow: hidden; min-height: 0; }
.acc-c a {
  display: flex; align-items: center; gap: 12px;
  padding: 9px 4px; text-decoration: none; color: var(--ink);
}
.acc-c a b { display: block; font-size: 14px; font-weight: 500; }
.acc-c a em {
  display: block; font-style: normal; font-size: 12px;
  color: var(--muted); margin-top: 2px;
}
.mt {
  width: 38px; height: 38px; border-radius: 10px; flex: none;
  border: 1px solid var(--line); background: var(--surface-2);
}
.mt::after { inset: 10px; }
.mp-plain { padding-top: 8px; }
.mp-plain a {
  display: block; padding: 12px 4px; border-radius: 10px;
  color: var(--ink); text-decoration: none;
  font-family: var(--font-display);
  font-size: 18px; font-weight: 400; letter-spacing: 0;
}
.mp-scroll a:active { background: var(--hover); }
.mp-foot {
  flex: none; display: grid; gap: 10px;
  padding: 14px 20px calc(18px + env(safe-area-inset-bottom));
  border-top: 1px solid var(--line);
}
.mp-foot .btn { text-align: center; }

/* ── nav ────────────────────────────────────────────────────────────── */
.nav { display: flex; align-items: center; gap: 32px; padding: 22px clamp(20px, 5vw, 64px); }
/* The supplied lockup is a single flat ink throughout, so one alpha PNG masked
   and coloured by CSS covers every surface — orange on dark, white on the
   orange bar — instead of shipping and preloading two bitmaps. The <a> still
   carries real text for screen readers and search engines. */
:root { --logo: #fe3100; }               /* the lockup's own ink */
.brand {
  display: inline-flex; align-items: center; padding-block: 9px;   /* thumb-sized hit area */
  /* an <a> with no colour falls back to the UA's link blue — invisible here
     only because the label is sr-only, but wrong the moment it is not */
  text-decoration: none; color: var(--ink);
  font-size: 18px; font-weight: 600; letter-spacing: -0.01em;
}
/* if masking is unavailable, drop the graphic and let the wordmark show as
   text rather than leaving the header with no brand at all */
@supports not ((-webkit-mask-image: url('')) or (mask-image: url(''))) {
  .brand-mark { display: none; }
  .brand .sr-only, .ab-name .sr-only {
    position: static; width: auto; height: auto; margin: 0; clip: auto; overflow: visible;
  }
}
.brand-mark {
  display: block; width: 119px; height: 38px;   /* 3.12:1, the artwork's own */
  background: var(--logo-ink, var(--logo));
  -webkit-mask: url('assets/wordmark.webp') no-repeat center / contain;
  mask: url('assets/wordmark.webp') no-repeat center / contain;
}
/* Entries are separated by a hairline rule rather than by spacing alone.
   The rule sits on the item, not between items, so the row stays a single
   flex line — and the first one is suppressed so the group opens cleanly
   against the brand. */
.nav-links { display: flex; align-items: stretch; gap: 0; margin-left: 18px; }
.nav-links > .menu { display: flex; align-items: center; }
/* Named rather than `* + *`: .nav-pill is a decorative 0×0 element sitting
   first, and counting it would put a rule before the opening item. These
   selectors also outrank the `border: 0` in the shared item rule below. */
.nav-links > .menu + .menu,
.nav-links > .menu + a,
.nav-links > a + a,
.nav-links > a + .menu { border-left: 1px solid var(--line); }

.nav-links > a,
.menu-trigger {
  display: inline-flex; align-items: center; gap: 7px;
  padding: 8px 16px; margin: 0 4px; border-radius: 9px;
  color: var(--muted); text-decoration: none; font: inherit; font-size: 14px;
  background: none; border: 0; cursor: pointer;
  white-space: nowrap;
  transition: color .2s, background .2s;
}
.nav-links > a:hover,
.menu-trigger:hover,
.menu-trigger[aria-expanded="true"] { color: var(--ink); background: var(--hover); }

.chev { width: 12px; height: 12px; opacity: .55; transition: transform .28s var(--ease), opacity .2s; }
.menu-trigger:hover .chev { opacity: 1; }
.menu-trigger[aria-expanded="true"] .chev { transform: rotate(180deg); opacity: 1; }

/* A trigger whose menu has no items yet: shown, but it does not pretend to
   be operable. Remove `.is-empty` by giving the menu items in
   menu-data.mjs — build-menu.mjs then writes the panel and the aria. */
.menu-trigger.is-empty { cursor: default; }
.menu-trigger.is-empty:hover { background: none; color: var(--muted); }
.menu-trigger.is-empty:hover .chev { opacity: .55; }

/* ── mega menu ──────────────────────────────────────────────────────── */
.menu { position: relative; }
.menu-panel {
  position: absolute; top: calc(100% + 10px); left: 0; z-index: 40;
  width: max-content; max-width: min(560px, calc(100vw - 40px));
  padding: 18px; border-radius: 16px;
  background: var(--surface); border: 1px solid var(--line);
  box-shadow: 0 24px 60px rgba(var(--sh), .14), 0 2px 8px rgba(var(--sh), .05);
  opacity: 0; transform: translateY(-6px) scale(.985);
  transition: opacity .22s var(--ease), transform .22s var(--ease);
}
.menu-panel.wide { max-width: min(900px, calc(100vw - 40px)); }
.menu-panel.open { opacity: 1; transform: none; }
.menu-panel[hidden] { display: none; }
.panel-grid { display: grid; grid-template-columns: 2fr 1fr; gap: 20px; }
.panel-side { padding-left: 20px; border-left: 1px solid var(--line); }
/* integrity line — the promise the copy has to keep */
.panel-note {
  margin: 16px -18px -18px; padding: 13px 28px;
  border-top: 1px solid var(--line); background: var(--surface-2);
  border-radius: 0 0 16px 16px;
  font-size: 12px; color: var(--muted);
}
.panel-note strong { color: var(--ink); font-weight: 600; }
.menu-panel h6 {
  margin: 0 0 12px; padding-left: 10px;
  font-size: 11px; font-weight: 700; letter-spacing: 0.08em;
  text-transform: uppercase; color: var(--faint);
}
.menu-panel ul { list-style: none; margin: 0; padding: 0; display: grid; gap: 4px; }
.panel-main ul, .panel-two { grid-template-columns: 1fr 1fr; }
.menu-panel a {
  display: grid; grid-template-columns: auto 1fr; column-gap: 11px; row-gap: 4px;
  padding: 11px; border-radius: 11px; text-decoration: none;
  transition: background .18s;
}
.menu-panel a:hover, .menu-panel a:focus-visible { background: var(--hover); outline: none; }

/* ── featured grid cards ────────────────────────────────────────────── */
.gcards { display: grid; grid-template-columns: repeat(3, 1fr); gap: 12px; }
.menu-panel a.gcard {
  position: relative; isolation: isolate; overflow: hidden;
  display: flex; flex-direction: column; gap: 10px;
  min-height: 148px; padding: 16px 17px;
  border: 1px solid var(--line); border-radius: 10px; background: var(--surface);
  transition: border-color .15s;
}
.menu-panel a.gcard:hover { background: var(--surface); border-color: var(--line-strong); }
.gc-bg { position: absolute; inset: 0; z-index: -1; pointer-events: none; }
/* skewed lattice that lifts on hover */
.gc-bg::before {
  content: ''; position: absolute; inset: -25%;
  background-image:
    linear-gradient(to right,  var(--line) 1px, transparent 1px),
    linear-gradient(to bottom, var(--line) 1px, transparent 1px),
    /* a few filled cells, like the reference's random squares */
    linear-gradient(var(--hover), var(--hover)),
    linear-gradient(var(--hover), var(--hover)),
    linear-gradient(var(--hover), var(--hover));
  background-size: 30px 30px, 30px 30px, 29px 29px, 29px 29px, 29px 29px;
  background-position: 0 0, 0 0, 212px 61px, 272px 121px, 182px 151px;
  background-repeat: repeat, repeat, no-repeat, no-repeat, no-repeat;
  transform: skewY(-12deg) translateY(8px);
  transition: transform .18s ease-out;
  -webkit-mask-image: linear-gradient(225deg, #000, transparent);
  mask-image: linear-gradient(225deg, #000, transparent);
}
.menu-panel a.gcard:hover .gc-bg::before { transform: skewY(-12deg) translateY(0); }
/* brand-tinted bloom, in place of the reference's pink/blue conic */
.gc-bg::after {
  content: ''; position: absolute; inset: -10%;
  background: conic-gradient(var(--brand) 0deg, var(--brand-2) 117deg, var(--brand-2) 180deg, #5182fc 240deg, var(--brand) 360deg);
  filter: blur(50px); opacity: 0; transition: opacity .18s;
}
.menu-panel a.gcard:hover .gc-bg::after { opacity: .13; }
.gcard .mi { grid-row: auto; margin: 0; }
/* both must be block — as inline spans a short title lets the description
   ride up onto the same line */
.gc-t { display: block; font-size: 13px; font-weight: 600; color: var(--ink); }
.gc-d { display: block; margin-top: 5px; font-size: 12px; line-height: 1.4; color: var(--muted); font-style: normal; }
.gcard .gc-txt { display: block; }

/* ── small rows with a sliding arrow ────────────────────────────────── */
.menu-panel a.srow {
  display: flex; align-items: center; gap: 10px;
  padding: 10px 10px; border-radius: 9px;
}
.srow .mi { width: 22px; height: 22px; border-radius: 7px; }
.srow .mi::after { inset: 4px; }
.srow .s-t { font-size: 13px; color: var(--ink); }
.srow .arw-i {
  margin-left: auto; width: 14px; height: 14px; flex: none;
  opacity: 0; transform: translateX(-6px);
  transition: opacity .2s var(--ease), transform .2s var(--ease);
}
.menu-panel a.srow:hover .arw-i, .menu-panel a.srow:focus-visible .arw-i { opacity: .6; transform: none; }
.menu-panel b {
  grid-column: 2; align-self: center;
  font-size: 13px; font-weight: 600; color: var(--ink);
}
.menu-panel em {
  grid-column: 2; font-style: normal;
  font-size: 12px; line-height: 1.4; color: var(--muted);
}
.mi {
  grid-row: 1 / span 2; align-self: start; margin-top: 1px;
  width: 26px; height: 26px; border-radius: 8px; flex: none;
  background: var(--brand-soft);
  position: relative;
}
.mi::after {
  content: ''; position: absolute; inset: 5px;
  background: var(--brand);
  -webkit-mask-repeat: no-repeat; mask-repeat: no-repeat;
  -webkit-mask-size: contain; mask-size: contain;
  -webkit-mask-position: center; mask-position: center;
}
.mi-board::after  { -webkit-mask-image: var(--i-board);   mask-image: var(--i-board); }
.mi-star::after   { -webkit-mask-image: var(--i-star);    mask-image: var(--i-star); }
.mi-book::after   { -webkit-mask-image: var(--i-book);    mask-image: var(--i-book); }
.mi-users::after  { -webkit-mask-image: var(--i-users);   mask-image: var(--i-users); }
.mi-chart::after  { -webkit-mask-image: var(--i-chart);   mask-image: var(--i-chart); }
.mi-shield::after { -webkit-mask-image: var(--i-shield);  mask-image: var(--i-shield); }
.mi-bolt::after   { -webkit-mask-image: var(--i-bolt);    mask-image: var(--i-bolt); }
.mi-id::after     { -webkit-mask-image: var(--i-id);      mask-image: var(--i-id); }
.mi-clock::after  { -webkit-mask-image: var(--i-clock);   mask-image: var(--i-clock); }
.mi-cap::after    { -webkit-mask-image: var(--i-cap);     mask-image: var(--i-cap); }
.mi-lab::after    { -webkit-mask-image: var(--i-lab);     mask-image: var(--i-lab); }
.mi-building::after { -webkit-mask-image: var(--i-building); mask-image: var(--i-building); }
.mi-code::after   { -webkit-mask-image: var(--i-code);   mask-image: var(--i-code); }
.mi-doc::after    { -webkit-mask-image: var(--i-doc);    mask-image: var(--i-doc); }
.mi-search::after { -webkit-mask-image: var(--i-search); mask-image: var(--i-search); }
.mi-pen::after    { -webkit-mask-image: var(--i-pen);    mask-image: var(--i-pen); }
.mi-award::after  { -webkit-mask-image: var(--i-award);  mask-image: var(--i-award); }
.mi-tools::after  { -webkit-mask-image: var(--i-tools);  mask-image: var(--i-tools); }
.mi-rocket::after { -webkit-mask-image: var(--i-rocket); mask-image: var(--i-rocket); }
.nav-cta {
  margin-left: auto; padding: 9px 18px; border-radius: 999px;
  border: 1px solid var(--line); background: var(--surface);
  color: var(--ink); text-decoration: none; font-size: 14px;
  transition: background .2s, border-color .2s, color .2s;
}
.nav-cta:hover { background: var(--ink); color: var(--on-brand); border-color: var(--ink); }

/* ── copy ───────────────────────────────────────────────────────────── */
.hero-copy { text-align: left; padding: 0; }
/* ── hero type system ───────────────────────────────────────────────────
   Four tiers, and every text in the hero belongs to exactly one:
     1  display   SF Pro Display 600 — h1, stat figures, rotating term
     2  lede      SF Pro Text 400, 20/1.5 — the paragraph under the headline
     3  micro     SF Pro Text 500, 13px, uppercase, +0.6px — every label
     4  ui        SF Pro Text 600, 15px — buttons and the assurance line
   The micro tier is what makes the section read corporate: pill, eyebrow,
   stat captions and the marquee label all speak in the same small voice
   instead of four slightly different greys. */
.pill-note {
  display: inline-flex; align-items: center; gap: 9px;
  padding: 8px 16px 8px 13px; border-radius: 999px;
  border: 1px solid var(--line); background: var(--surface);
  backdrop-filter: blur(6px);
  font-size: 11px; font-weight: 600; line-height: 1.4;
  letter-spacing: 0.12em; text-transform: uppercase;
  color: var(--muted); text-decoration: none;
  transition: border-color .25s, transform .25s var(--ease);
}
.pill-note:hover { border-color: var(--brand-line); transform: translateY(-1px); }
.ping { position: relative; width: 7px; height: 7px; border-radius: 50%; background: var(--brand); flex: none; }
.ping::after {
  content: ''; position: absolute; inset: -4px; border-radius: 50%;
  border: 1.5px solid var(--brand); opacity: 0;
  animation: ping 2.4s var(--ease) infinite;
}
@keyframes ping { 0% { transform: scale(.5); opacity: .7; } 70%,100% { transform: scale(1.4); opacity: 0; } }
.arw { color: var(--brand); font-weight: 600; }

/* Size, weight and tracking here are inert — the SF Pro role layer owns
   all three with !important. What survives is the margin and the balance. */
.hero h1, .err-title, .hero-h1 {
  margin: 22px 0 0;
  font-family: var(--font-display);
  font-size: clamp(32px, 3.7vw, 52px); line-height: 1.12;
  letter-spacing: 0; font-weight: 400;
  text-wrap: balance;
}
/* keep the accent phrase whole — a hyphen break across lines reads as a typo */
.hero h1 em { font-style: italic; color: var(--brand); white-space: nowrap; }
/* ── rotating discipline ────────────────────────────────────────────────
   Sits above the workspace window and reads as its caption: a quiet lead-in,
   then the discipline itself at display size. Two lines rather than one, so
   the term always has the full column to itself and never fights the
   sentence for space. The word is typed in and out by hero-grid.js. */
.hero-right { display: flex; flex-direction: column; min-width: 0; }
.hero-rot {
  display: grid;                    /* live block and sizer share one cell */
  margin: 0 0 clamp(18px, 1.6vw, 26px);
}
.hero-rot > .hr-block { grid-area: 1 / 1; min-width: 0; }
.hr-sizer { visibility: hidden; }

/* A short brand tick marks the label. A full-width rule here would cut the
   hero in two right above the window — the seam this page is built to avoid. */
.hr-lead {
  display: block; position: relative; padding-top: 18px;
  font-size: 11px; font-weight: 600; line-height: 1.6;
  letter-spacing: 0.12em; text-transform: uppercase;
  color: var(--muted);
}
.hr-lead::before {
  content: ''; position: absolute; top: 0; left: 0;
  width: 34px; height: 2px; border-radius: 2px;
  background: linear-gradient(90deg, var(--brand), var(--brand-2));
}
.hr-term {
  display: block; margin-top: 6px;
  font-family: var(--font-display);
  font-size: clamp(26px, 2.8vw, 40px); line-height: 1.12;
  letter-spacing: 0; font-weight: 400;
}
/* the term carries the only gradient on the page — it earns it by being the
   one element that changes while you watch it */
.hr-word {
  /* a <b>, so it inherits 700 — but the display face is loaded at 400 only
     and the browser would smear a synthetic bold across the gradient fill */
  font-weight: 400;
  background: linear-gradient(96deg, var(--brand) 10%, var(--brand-2) 90%);
  -webkit-background-clip: text; background-clip: text;
  color: transparent;
}
.hr-caret {
  display: inline-block; width: .06em; height: .82em; margin-left: .07em;
  background: var(--brand-2); vertical-align: -.06em; border-radius: 1px;
  animation: hr-blink 1.05s steps(1) infinite;
}
@keyframes hr-blink { 0%, 49% { opacity: 1; } 50%, 100% { opacity: 0; } }

@media (prefers-reduced-motion: reduce) {
  .hr-caret { animation: none; opacity: .45; }
}

/* Anthropic sets running copy in the serif, not the grotesque — this is
   where the change is most visible. A serif needs a little more size and
   none of the negative tracking a sans wants at this scale. */
.sub {
  max-width: 52ch; margin: 22px 0 0; color: var(--muted);
  font-family: var(--font-display);
  font-size: clamp(16px, 1.65vw, 21px); line-height: 1.6;
  letter-spacing: 0;
}
.hero-cta { display: flex; gap: 12px; margin: 30px 0 0; flex-wrap: wrap; }
.btn {
  padding: 15px 30px; border-radius: 999px;
  font-size: 14px; font-weight: 500; letter-spacing: 0;
  text-decoration: none; transition: transform .2s var(--ease), border-color .2s, box-shadow .2s;
}
.btn:hover { transform: translateY(-1px); }
.btn-primary {
  background: var(--ink); color: var(--on-brand); font-weight: 500;
  box-shadow: 0 8px 22px rgba(var(--sh), .16);
}
.btn-ghost { border: 1px solid var(--line); color: var(--ink); background: var(--surface); backdrop-filter: blur(6px); }
.btn-ghost:hover { border-color: var(--line-strong); }

.assure {
  margin: 18px 0 0; max-width: 440px;
  font-size: 12px; line-height: 1.6; letter-spacing: 0; color: var(--faint);
}

/* ── stat row ───────────────────────────────────────────────────────── */
.stats {
  position: relative;
  list-style: none; display: grid; grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 16px; max-width: 520px; margin: clamp(26px, 3vw, 38px) 0 0;
  padding: clamp(24px, 2.8vw, 32px) 0 0;
}
/* fades out rather than stopping dead — the same hairline language as the
   eyebrow tick, so the hero has no hard rules anywhere in it */
.stats::before {
  content: ''; position: absolute; top: 0; left: 0; right: 0; height: 1px;
  background: linear-gradient(90deg, var(--line-strong), var(--line) 55%, transparent);
}
.stats li { display: flex; flex-direction: column; gap: 6px; min-width: 0; }
/* figures in the display face — a serif numeral reads considered where a
   grotesque one reads like a dashboard metric */
.stats b {
  font-family: var(--font-display);
  font-size: clamp(26px, 2.5vw, 32px); line-height: 1.12; font-weight: 400;
  letter-spacing: 0; color: var(--ink);
}
.stats span {
  font-size: 11px; font-weight: 600; line-height: 1.4;
  letter-spacing: 0.12em; text-transform: uppercase; color: var(--faint);
}

/* ── video ──────────────────────────────────────────────────────────── */
.hero-video {
  margin: clamp(40px, 5vw, 72px) auto 0;
  max-width: 1600px; padding: 0 clamp(16px, 2vw, 28px);
}
/* the video IS the workspace window, cropped — so it carries the window's own
   rounding and shadow, and the page grid runs right up to its edge */
.video-wrap { position: relative; }
.hero-video video {
  display: block; width: 100%; height: auto;
  border-radius: clamp(10px, 1.1vw, 18px);
  box-shadow: 0 24px 70px rgba(var(--sh), .12), 0 2px 8px rgba(var(--sh), .05);
}

.vid-toggle {
  position: absolute; right: 16px; bottom: 16px;
  display: inline-flex; align-items: center; gap: 7px;
  padding: 8px 14px 8px 11px; border-radius: 999px;
  border: 1px solid var(--line); background: rgba(255,255,255,.86);
  backdrop-filter: blur(8px);
  color: var(--ink); font: inherit; font-size: 12px; font-weight: 600;
  cursor: pointer; opacity: 0; transform: translateY(4px);
  transition: opacity .25s var(--ease), transform .25s var(--ease),
              border-color .2s, background .2s;
}
.video-wrap:hover .vid-toggle,
.vid-toggle:focus-visible { opacity: 1; transform: none; }
.vid-toggle:hover { border-color: var(--line-strong); background: var(--surface); }
.vid-toggle svg { width: 13px; height: 13px; fill: currentColor; flex: none; }
/* show whichever icon matches the *action* the button performs */
.vid-toggle .ic-play { display: none; }
.vid-toggle[aria-pressed="false"] .ic-pause { display: none; }
.vid-toggle[aria-pressed="false"] .ic-play { display: block; }
/* keep it visible while paused, so the control is discoverable */
.vid-toggle[aria-pressed="false"] { opacity: 1; transform: none; }

/* ── journal marquee ────────────────────────────────────────────────── */
/* lives inside the hero grid — span both columns, then sit on the shell */
.marquee {
  grid-column: 1 / -1;
  /* same content width as every .wrap section, so its right edge lines up too */
  margin: clamp(36px, 4vw, 60px) 0 0; max-width: calc(var(--shell) - 2 * var(--gutter));
  display: flex; align-items: center; gap: 22px;
}
.marquee-lbl {
  font-size: 11px; font-weight: 600; letter-spacing: 0.12em; text-transform: uppercase;
  color: var(--faint); white-space: nowrap; flex: none;
}
.marquee-track {
  flex: 1; min-width: 0; overflow: hidden;
  -webkit-mask-image: linear-gradient(to right, transparent, #000 8%, #000 92%, transparent);
  mask-image: linear-gradient(to right, transparent, #000 8%, #000 92%, transparent);
}
/* the run holds two identical sets; translating exactly one set width loops seamlessly */
.marquee-run { display: flex; width: max-content; animation: slide 30s linear infinite; }
.mq-set { display: flex; gap: 30px; padding-right: 30px; }
.marquee-track span { font-size: 13px; font-weight: 600; color: var(--faint); white-space: nowrap; }
@keyframes slide { to { transform: translateX(-50%); } }

/* the full nav (3 mega menus + 2 links + auth pair + theme) needs ~1000px of
   header before it starts wrapping onto two lines — hand over to the drawer
   well before that, which also puts iPad landscape on the drawer.
   At corporate sizing the six entries, the brand, the button pair and the
   theme toggle need ~1150px of content once the spacing clamps hit their
   lower bound — measured, not guessed. The handover sits at 1280. */
@media (max-width: 1280px) {
  /* the drawer is anchored to --hdr and measures from the viewport top, so the
     bar goes flush here — a floating one would leave a gap above the panel */
  :root { --hdr-gap: 0px; --hdr-side: 0px; }
  .site-header { width: 100%; max-width: none; border-radius: 0; box-shadow: none; }
  /* the drawer carries its own Sign in / Get Started, so the header pair goes too */
  .nav-links, .nav-cta, .nav-auth { display: none; }
  .burger { display: inline-flex; margin-left: auto; }
  .site-header.scrolled { max-width: none; border-radius: 0; }
  /* it is a full-width bar here, not a floating pill — so it keeps the shell
     gutter instead of the pill's tighter inset, which pushed the brand 20px
     left of every other element on the page */
  .site-header.scrolled .hdr-inner { padding: 0 var(--gutter); }
}
/* Below this the two-column split leaves the workspace under ~950px, which is
   where its five stages stop being readable. Stacking hands it the full shell
   instead — a 1400px window with room to breathe beats a cramped 800px one. */
@media (max-width: 1599px) {
  .hero-inner {
    grid-template-columns: minmax(0, 1fr);
    max-width: var(--shell); margin-inline: auto;
    padding: clamp(28px, 5vw, 56px) var(--gutter) 0;
    row-gap: clamp(34px, 5vw, 52px);
  }
  /* full shell available now — the prose keeps its measure, but the stat row
     spreads out instead of huddling in a 660px column */
  .hero-copy { max-width: none; }
  .sub, .assure { max-width: 660px; }
  .hero h1 { font-size: clamp(32px, 5.4vw, 52px); max-width: 20ch; }
  .stats { max-width: 880px; }
}

@media (max-width: 720px) {
  .hero-video { padding: 0 12px; }
  .stats { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 18px 26px; }
  /* stacking drops the track out of the row, so `flex: 1` no longer caps its
     width and it grows to its 2200px content — clipped by .hero, but the fade
     mask ends up off-screen. Pin it back to the container. */
  .marquee { flex-direction: column; align-items: stretch; gap: 12px; }
  .marquee-track { width: 100%; }
}

/* phones: the board is decorative at this size — keep it legible, not complete */
@media (max-width: 560px) {
  .hero h1 { font-size: clamp(26px, 8.4vw, 40px); }
  .hero-cta .btn { flex: 1 1 auto; text-align: center; }
}

@media (prefers-reduced-motion: reduce) {
  .ping::after, .marquee-run, .fp path { animation: none; }
}

/* ── mega panel (spans the header, columns of grouped links) ─────────── */
.hdr-inner { position: relative; }
.menu { position: static; }
.menu-panel.mega {
  left: 0; right: 0; width: auto; max-width: none;
  padding: 0; overflow: hidden;
}
.mega-head {
  display: flex; align-items: baseline; justify-content: space-between; gap: 16px;
  padding: 16px 22px 13px; border-bottom: 1px solid var(--line);
  background: var(--surface-2);
}
.mega-head h5 {
  margin: 0; font-size: 11px; font-weight: 700;
  letter-spacing: 0.12em; text-transform: uppercase; color: var(--faint);
}
.mega-all { font-size: 12px; font-weight: 600; color: var(--brand); text-decoration: none; white-space: nowrap; }
.mega-all:hover { text-decoration: underline; }

/* ── theme toggle ───────────────────────────────────────────────────── */

/* ── mobile sub-groups inside the accordion ─────────────────────────── */
.msub { padding: 6px 0 10px; }
.msub-t {
  display: flex; align-items: center; gap: 9px; margin: 6px 0 4px;
  font-size: 12px; font-weight: 700; color: var(--ink);
}
.msub-t .mi { width: 22px; height: 22px; border-radius: 7px; }
.msub-t .mi::after { inset: 5px; }
/* Rows here match the desktop panel: a label, a hairline, nothing else.
   The left indent that used to clear an icon box is gone with it. */
.msub-l { display: grid; }
.msub-l a {
  display: block; padding: 12px 2px;
  font-family: var(--font-display);
  font-size: 16px; font-weight: 400; line-height: 1.4; letter-spacing: 0;
  color: var(--ink); text-decoration: none;
}
.msub-l a + a { border-top: 1px solid var(--line); }
.msub-l a:active { color: var(--brand); }

/* the recorded demo is a light-background video; give it a frame in dark mode */
:root[data-theme="dark"] .hero-video video { border: 1px solid var(--line); }


/* the panel spans the two triggers rather than the whole header */

/* mobile: overview items + a link through to the section */
.m-feat {
  display: block !important; margin-top: 6px; padding: 12px 4px !important;
  font-size: 14px; font-weight: 600; color: var(--brand) !important;
}
@media (max-width: 980px) {
  .ov-grid { grid-template-columns: 1fr; }
  .ov-side { border-left: 0; border-top: 1px solid var(--line); }
}


.ov-grid { grid-template-columns: 1fr 268px; }
.menu-panel .ov-quick a.srow {
  display: flex; align-items: center; gap: 8px;
  padding: 9px 10px; border-radius: 8px;
  font-size: 13px; color: var(--muted); text-decoration: none;
}
.menu-panel .ov-quick a.srow:hover { color: var(--ink); background: var(--hover); }
.ov-quick .s-t { flex: 1; }
.ov-quick .ov-arw { margin-left: auto; }

/* the mega panel has no padding, so the note's negative margins would push it
   outside overflow:hidden and clip the text */
.menu-panel.mega .panel-note { margin: 0; border-radius: 0; }
.lr-d { -webkit-line-clamp: 3; }


/* footer bar */
.mm-foot {
  display: flex; align-items: center; justify-content: space-between; gap: 16px;
  padding: 16px 24px; border-top: 1px solid var(--line); background: var(--surface-2);
}
.menu-panel a.mm-cta {
  display: inline-block; padding: 13px 26px; border-radius: 999px;
  /* --on-brand, not #fff — see .nav-start below */
  background: var(--brand); color: var(--on-brand); text-decoration: none;
  font-size: 14px; font-weight: 600;
  transition: transform .2s var(--ease), box-shadow .2s;
}
.menu-panel a.mm-cta:hover { transform: translateY(-1px); box-shadow: 0 8px 22px -8px var(--brand); background: var(--brand); }
.menu-panel a.mm-all {
  display: inline-flex; align-items: center; gap: 8px; padding: 8px 4px;
  color: var(--ink); text-decoration: none; font-size: 14px; font-weight: 600;
}
.menu-panel a.mm-all span { color: var(--brand); transition: transform .2s var(--ease); display: inline-block; }
.menu-panel a.mm-all:hover span { transform: translateX(4px); }

@media (max-width: 980px) {
}

/* ── mega menu: titled columns of plain rows ──────────────────────────
   Rows carry a label and nothing else. The descriptions and icon boxes
   that used to sit here made the panel dense enough to read as clutter;
   the one-liners now appear on the hub pages, where there is room for
   them. Separation is a hairline rule per row rather than a hover card,
   which is what keeps the panel calm. */
.menu-panel.mega { width: max-content; max-width: calc(100vw - 32px); left: 0; right: auto; padding: 0; }
.mm-cols { display: flex; padding: 26px 0 22px; }
.mm-col { min-width: 190px; flex: 1; padding: 0 30px; }
.mm-col + .mm-col { border-left: 1px solid var(--line-soft); }
.mm-col h3 {
  margin: 0 0 6px; padding: 0 0 12px;
  font-size: 11px; font-weight: 700; color: var(--faint);
  text-transform: uppercase; letter-spacing: 0.12em;
}
.mm-col ul { list-style: none; margin: 0; padding: 0; }
.mm-col li + li { border-top: 1px solid var(--line); }
.menu-panel .mm-col a {
  display: block; padding: 12px 0; text-decoration: none;
  font-size: 14px; font-weight: 500; line-height: 1.4;
  color: var(--ink); white-space: nowrap;
  transition: color .16s var(--ease);
}
/* The row itself does not move or fill on hover — only the label shifts
   colour, which is what a list this quiet can carry without noise. */
.menu-panel .mm-col a:hover,
.menu-panel .mm-col a:focus-visible { color: var(--brand); outline: none; }
.menu-panel .mm-col a:focus-visible { text-decoration: underline; text-underline-offset: 4px; }
/* bordered square icon, as in the reference — tinted on hover, not filled blue */
.mm-ic {
  position: relative; width: 36px; height: 36px; flex: none;
  border: 1px solid var(--line); border-radius: 9px; background: var(--surface);
  transition: border-color .25s, background .25s;
}
.mm-ic::after {
  content: ''; position: absolute; inset: 9px; background: var(--ink);
  -webkit-mask-repeat: no-repeat; mask-repeat: no-repeat;
  -webkit-mask-size: contain; mask-size: contain;
  -webkit-mask-position: center; mask-position: center;
  transition: background .25s;
}
/* ── rich panel layouts ───────────────────────────────────────────────
   Used by the `grid` and `split` panel layouts. The `columns` layout above
   stays a plain list of labels; these two carry the icon tile and the
   one-line description, for menus whose items need explaining rather than
   just naming. */

/* The panel is `width: max-content`, which mis-measures a grid using `fr`
   columns — it resolves against the items' max-content and lands twice as
   wide as the content needs. These layouts state their width outright.
   The modifier is `layout-*`, not `mm-*`: the panel and its inner <ul>
   would otherwise share a class name and the panel would pick up the
   two-column grid meant for the list. */
.menu-panel.mega.layout-grid  { width: min(620px, calc(100vw - 32px)); }
.menu-panel.mega.layout-split { width: min(780px, calc(100vw - 32px)); }

.mm-grid {
  list-style: none; margin: 0; padding: 22px;
  display: grid; grid-template-columns: 1fr 1fr; gap: 4px;
}
.mm-split { display: grid; grid-template-columns: 1fr minmax(200px, .6fr); }
.mm-split .mm-card {
  width: auto; margin: 14px; padding: 10px;
  border: 1px solid var(--line); border-radius: 14px;
  background: var(--surface-2);
}
.mm-side { list-style: none; margin: 0; padding: 22px 22px 22px 4px; display: grid; gap: 2px; align-content: start; }

.menu-panel a.mm-rich {
  display: flex; align-items: center; gap: 12px;
  padding: 10px; border-radius: 11px; text-decoration: none;
  transition: background .16s var(--ease);
}
.menu-panel a.mm-rich:hover,
.menu-panel a.mm-rich:focus-visible { background: var(--hover); outline: none; }
.mm-rich .mm-ic { flex: none; }
.mm-rich:hover .mm-ic { border-color: var(--brand-line); background: var(--brand-soft); }
.mm-rich:hover .mm-ic::after { background: var(--brand); }

.mm-rt { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.mm-rt b {
  font-size: 14px; font-weight: 600; color: var(--ink); letter-spacing: 0;
}
.mm-rt em {
  font-style: normal; font-size: 12px; line-height: 1.4; color: var(--muted);
}

.menu-panel a.mm-compact {
  display: flex; align-items: center; gap: 10px;
  padding: 8px 10px; border-radius: 10px; text-decoration: none;
  font-size: 13px; font-weight: 500; color: var(--ink);
  transition: background .16s var(--ease), color .16s var(--ease);
}
.menu-panel a.mm-compact:hover,
.menu-panel a.mm-compact:focus-visible { background: var(--hover); color: var(--brand); outline: none; }
.mm-ic-sm { width: 22px; height: 22px; border-radius: 7px; }
.mm-ic-sm::after { inset: 4px; }

/* A panel is anchored to the left of its own trigger, so the rightmost
   menus would push a wide panel past the viewport. Those flip to
   right-anchored — the panel then grows leftward into space that exists. */
/* Position comes from --panel-x, set in nav.js so the panel centres on
   whichever trigger opened it and clamps inside the bar. The old
   right-anchor for the last two menus is no longer needed — clamping
   handles the edge case for every menu, not just those two. */
.menu-panel.mega { left: var(--panel-x, 0px); right: auto; }

@media (max-width: 1280px) {
  /* the panels never render at drawer widths, but keep them from being
     wider than the viewport if that ever changes */
  .menu-panel.mega.layout-grid,
  .menu-panel.mega.layout-split { width: min(420px, calc(100vw - 32px)); }
  .mm-grid { grid-template-columns: 1fr; }
  .mm-split { grid-template-columns: 1fr; }
}

.mm-ic.mi-board::after   { -webkit-mask-image: var(--i-board);   mask-image: var(--i-board); }
.mm-ic.mi-star::after    { -webkit-mask-image: var(--i-star);    mask-image: var(--i-star); }
.mm-ic.mi-book::after    { -webkit-mask-image: var(--i-book);    mask-image: var(--i-book); }
.mm-ic.mi-users::after   { -webkit-mask-image: var(--i-users);   mask-image: var(--i-users); }
.mm-ic.mi-chart::after   { -webkit-mask-image: var(--i-chart);   mask-image: var(--i-chart); }
.mm-ic.mi-shield::after  { -webkit-mask-image: var(--i-shield);  mask-image: var(--i-shield); }
.mm-ic.mi-bolt::after    { -webkit-mask-image: var(--i-bolt);    mask-image: var(--i-bolt); }
.mm-ic.mi-id::after      { -webkit-mask-image: var(--i-id);      mask-image: var(--i-id); }
.mm-ic.mi-clock::after   { -webkit-mask-image: var(--i-clock);   mask-image: var(--i-clock); }
.mm-ic.mi-cap::after     { -webkit-mask-image: var(--i-cap);     mask-image: var(--i-cap); }
.mm-ic.mi-lab::after     { -webkit-mask-image: var(--i-lab);     mask-image: var(--i-lab); }
.mm-ic.mi-code::after    { -webkit-mask-image: var(--i-code);    mask-image: var(--i-code); }
.mm-ic.mi-doc::after     { -webkit-mask-image: var(--i-doc);     mask-image: var(--i-doc); }
.mm-ic.mi-search::after  { -webkit-mask-image: var(--i-search);  mask-image: var(--i-search); }
.mm-ic.mi-pen::after     { -webkit-mask-image: var(--i-pen);     mask-image: var(--i-pen); }
.mm-ic.mi-award::after   { -webkit-mask-image: var(--i-award);   mask-image: var(--i-award); }
.mm-ic.mi-tools::after   { -webkit-mask-image: var(--i-tools);   mask-image: var(--i-tools); }
.mm-ic.mi-rocket::after  { -webkit-mask-image: var(--i-rocket);  mask-image: var(--i-rocket); }

.mm-foot {
  display: flex; align-items: center; justify-content: space-between; gap: 16px;
  padding: 14px 24px; border-top: 1px solid var(--line); background: var(--surface-2);
}
/* second .mm-cta block — this is the one that wins, so the fix belongs here too */
.menu-panel a.mm-cta {
  display: inline-block; padding: 12px 24px; border-radius: 999px;
  background: var(--brand); color: var(--on-brand); text-decoration: none;
  font-size: 14px; font-weight: 600;
  transition: transform .2s var(--ease), box-shadow .2s;
}
.menu-panel a.mm-cta:hover { transform: translateY(-1px); box-shadow: 0 8px 22px -8px var(--brand); }
.menu-panel a.mm-all {
  display: inline-flex; align-items: center; gap: 8px; padding: 8px 4px;
  color: var(--ink); text-decoration: none; font-size: 14px; font-weight: 600;
}
.menu-panel a.mm-all span { color: var(--brand); display: inline-block; transition: transform .2s var(--ease); }
.menu-panel a.mm-all:hover span { transform: translateX(4px); }

/* ── sliding hover pill behind the nav triggers ─────────────────────── */
.nav-links { position: relative; }
.nav-pill {
  position: absolute; left: 0; top: 0; z-index: -1;
  height: 0; width: 0; border-radius: 999px; background: var(--brand-soft);
  opacity: 0; pointer-events: none;
  transition: transform .32s var(--ease), width .32s var(--ease),
              height .32s var(--ease), opacity .2s;
}
.nav-links > a, .menu-trigger { position: relative; }
/* the pill replaces the per-item background so they can't double up */
.nav-links > a:hover, .menu-trigger:hover, .menu-trigger[aria-expanded="true"] { background: none; }

@media (max-width: 1100px) { .mm-cols { gap: 26px; } }
@media (max-width: 980px) {
  .mm-cols { flex-direction: column; gap: 18px; }
}

/* the note is a footnote, so it must sit below the CTA in the hierarchy */
.menu-panel.mega .panel-note {
  padding: 11px 24px; font-size: 11px; color: var(--muted);
  background: var(--surface); border-top: 1px solid var(--line-soft);
}
.menu-panel.mega .panel-note strong { color: var(--ink); font-weight: 600; }

/* ── header auth pair ───────────────────────────────────────────────── */
.nav-auth { margin-left: auto; display: flex; align-items: center; gap: 8px; }
.nav-signin {
  padding: 9px 14px; border-radius: 999px; color: var(--ink);
  text-decoration: none; font-size: 14px; font-weight: 500;
  transition: background .2s;
}
.nav-signin:hover { background: var(--hover); }
.nav-start {
  padding: 9px 18px; border-radius: 999px;
  /* not #fff: in dark the brand is a light orange, and white on it is 3.18:1.
     --on-brand is white in light and near-black in dark — correct on both. */
  background: var(--brand); color: var(--on-brand); text-decoration: none;
  font-size: 14px; font-weight: 600;
  transition: transform .2s var(--ease), box-shadow .2s;
}
.nav-start:hover { transform: translateY(-1px); box-shadow: 0 8px 20px -8px var(--brand); }
@media (max-width: 860px) { .nav-auth { display: none; } }

/* ── section scaffolding ────────────────────────────────────────────── */
.sec { padding: var(--sec-y) 0; }
/* Section tints used to be slabs with a 1px rule top and bottom, which is what
   made the page read as stacked blocks. They are now edgeless: the tint fades
   up from nothing and back to nothing, so one section becomes the next. */
.sec-alt { position: relative; }
/* The alternating tint is gone. Every other section used to paint a
   --surface-2 band, which on the old ivory read as a quiet rhythm; on white
   it reads as a stack of separate cards with seams between them. The page
   is one continuous white sheet now and the sections are told apart by
   their spacing and their headings, which is what should have been doing
   the work. */
/* same shell as the header and the hero — every section starts at the same x */
.wrap { max-width: var(--shell); margin: 0 auto; padding: 0 var(--gutter); }
.kicker {
  margin: 0 0 14px; font-size: 11px; font-weight: 700;
  letter-spacing: 0.12em; text-transform: uppercase; color: var(--brand);
}
.sec h2 {
  margin: 0; max-width: 18ch;               /* the shell is wide; the measure is not */
  font-family: var(--font-display);
  font-size: clamp(32px, 3.6vw, 52px); line-height: 1.12;
  letter-spacing: 0; font-weight: 400; color: var(--ink);
}
.cta-band h2 { max-width: none; }
/* section ledes speak in the same serif voice as the hero paragraph */
.lede {
  margin: 16px 0 0; max-width: 62ch;
  font-family: var(--font-display);
  font-size: clamp(16px, 1.45vw, 18px); line-height: 1.6;
  letter-spacing: 0; color: var(--muted);
}

/* ── service cards ──────────────────────────────────────────────────── */
.cards3 { display: grid; grid-template-columns: repeat(3, 1fr); gap: 20px; margin-top: clamp(36px, 4vw, 56px); }
.scard {
  display: flex; flex-direction: column;
  padding: 26px; border: 1px solid var(--line); border-radius: 16px;
  background: var(--surface); transition: border-color .25s, box-shadow .25s, transform .25s var(--ease);
}
.scard:hover { border-color: var(--line-strong); transform: translateY(-2px); box-shadow: 0 16px 40px -18px rgba(var(--sh), .28); }
.scard-ic, .feat-ic {
  position: relative; width: 42px; height: 42px; border-radius: 12px;
  background: var(--brand-soft); margin-bottom: 18px;
}
.scard-ic::after, .feat-ic::after {
  content: ''; position: absolute; inset: 11px; background: var(--brand);
  -webkit-mask-repeat: no-repeat; mask-repeat: no-repeat;
  -webkit-mask-size: contain; mask-size: contain;
  -webkit-mask-position: center; mask-position: center;
}
.scard h3, .feat h3 { margin: 0 0 10px; font-size: 18px; font-weight: 600; color: var(--ink); letter-spacing: 0; }
.scard p { margin: 0; font-size: 14px; line-height: 1.6; color: var(--muted); }
.ticks { list-style: none; margin: 18px 0 0; padding: 0; display: grid; gap: 9px; }
.ticks li { position: relative; padding-left: 24px; font-size: 13px; color: var(--muted); }
.ticks li::before {
  content: ''; position: absolute; left: 0; top: 4px; width: 15px; height: 15px;
  border-radius: 50%; background: var(--brand-soft);
}
.ticks li::after {
  content: ''; position: absolute; left: 4px; top: 8px; width: 7px; height: 4px;
  border-left: 1.6px solid var(--brand); border-bottom: 1.6px solid var(--brand);
  transform: rotate(-45deg);
}
.scard-go {
  margin-top: auto; padding-top: 22px; text-decoration: none;
  font-size: 14px; font-weight: 600; color: var(--brand);
}
.scard-go span { display: inline-block; transition: transform .2s var(--ease); }
.scard-go:hover span { transform: translateX(4px); }


/* ── feature grid ───────────────────────────────────────────────────── */
/* ── why: bento ─────────────────────────────────────────────────────────
   Six reasons at three weights rather than six identical boxes. The lead tile
   spans two rows and carries the integrity claim, because every other reason
   here depends on it. Each tile lights a spotlight border under the pointer,
   and the set staggers in on scroll so the eye is walked through it. */
.grid6 {
  display: grid; grid-template-columns: repeat(6, minmax(0, 1fr));
  gap: 14px; margin-top: clamp(36px, 4vw, 56px);
}
.feat {
  position: relative; isolation: isolate; overflow: hidden;
  display: flex; flex-direction: column;
  padding: clamp(20px, 2vw, 28px);
  border: 1px solid var(--line); border-radius: 18px;
  background: var(--surface);
  transition: transform .4s var(--ease), border-color .3s;
}
.feat:hover { transform: translateY(-3px); border-color: var(--line-strong); }

/* bento shape: 1 leads over two rows, 2 and 3 stack beside it, 4-6 sit below */
.feat:nth-child(1) { grid-column: span 3; grid-row: span 2; }
.feat:nth-child(2),
.feat:nth-child(3) { grid-column: span 3; }
.feat:nth-child(4),
.feat:nth-child(5),
.feat:nth-child(6) { grid-column: span 2; }

/* Spotlight border. The gradient is painted into the padding ring and the
   fill is punched back out — mask-composite must EXCLUDE here; intersect
   multiplies the two layers to zero alpha and paints nothing at all. */
.feat-edge {
  position: absolute; inset: 0; z-index: -1; pointer-events: none;
  border-radius: inherit; padding: 1px;
  background: radial-gradient(220px circle at var(--mx, 50%) var(--my, 50%),
              var(--brand), transparent 68%);
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor; mask-composite: exclude;
  opacity: 0; transition: opacity .35s var(--ease);
}
.feat:hover .feat-edge, .feat:focus-within .feat-edge { opacity: 1; }

/* the index, set large and faint in the display face */
.feat-n {
  position: absolute; top: clamp(12px, 1.2vw, 18px); right: clamp(14px, 1.4vw, 22px);
  font-family: var(--font-display); font-size: clamp(32px, 3vw, 40px);
  line-height: 1.12; color: var(--ink); opacity: .07;
  transition: opacity .35s var(--ease), color .35s;
}
.feat:hover .feat-n { opacity: .22; color: var(--brand); }

.feat-ic { width: 34px; height: 34px; margin-bottom: 16px; }
.feat-ic::after { inset: 9px; }
.feat h3 {
  margin: 0 0 8px; font-size: clamp(14px, 1.35vw, 18px); font-weight: 600;
  letter-spacing: 0; color: var(--ink);
}
.feat p { margin: 0; font-size: 14px; line-height: 1.6; color: var(--muted); }

/* the lead tile gets the room its claim needs */
.feat-lead { justify-content: flex-end; padding: clamp(24px, 2.6vw, 38px); }
.feat-lead .feat-ic { width: 44px; height: 44px; margin-bottom: 20px; }
.feat-lead .feat-ic::after { inset: 12px; }
.feat-lead h3 {
  font-family: var(--font-display); font-weight: 400;
  font-size: clamp(26px, 2.5vw, 32px); line-height: 1.12; letter-spacing: 0;
  margin-bottom: 12px;
}
.feat-lead p { font-size: clamp(14px, 1.2vw, 16px); line-height: 1.6; max-width: 44ch; }
.feat-lead .feat-n { font-size: clamp(40px, 4.4vw, 64px); }

/* staggered arrival, once */
.grid6 .feat { opacity: 0; }
.grid6.in .feat {
  animation: feat-in .6s var(--ease) both;
  animation-delay: calc(.07s * var(--i));
}
@keyframes feat-in {
  from { opacity: 0; transform: translateY(18px); }
  to   { opacity: 1; transform: none; }
}

@media (max-width: 980px) {
  .grid6 { grid-template-columns: repeat(4, minmax(0, 1fr)); }
  .feat:nth-child(1) { grid-column: span 4; grid-row: auto; }
  .feat:nth-child(2), .feat:nth-child(3),
  .feat:nth-child(4), .feat:nth-child(5), .feat:nth-child(6) { grid-column: span 2; }
}
@media (max-width: 620px) {
  .grid6 { grid-template-columns: minmax(0, 1fr); }
  .feat:nth-child(n) { grid-column: span 1; }
}
@media (prefers-reduced-motion: reduce) {
  .grid6 .feat { opacity: 1; }
  .grid6.in .feat { animation: none; }
  .feat, .feat-edge, .feat-n { transition: none; }
}

/* ── stat band ──────────────────────────────────────────────────────── */
.sec-band { position: relative; padding: clamp(40px, 5vw, 64px) 0; }
/* the wash is gone — the page is one ground from the hero to the footer */
.band { display: grid; grid-template-columns: repeat(4, 1fr); gap: 20px; text-align: center; }
.band b { display: block; font-size: clamp(32px, 3.4vw, 40px); font-weight: 600; letter-spacing: -0.01em; color: var(--ink); }
.band span { display: block; margin-top: 6px; font-size: 13px; color: var(--muted); }

/* ── quotes ─────────────────────────────────────────────────────────── */
/* ── review wall ────────────────────────────────────────────────────────
   Two rows drifting in opposite directions. Each row holds the same ten cards
   twice and translates by exactly one set width, so the seam never lands in
   view — the same loop the journal marquee uses. Pure CSS: nothing measures,
   nothing runs on the main thread. */
.rv-wall { margin-top: clamp(36px, 4vw, 56px); display: grid; gap: 10px; }
.rv-row {
  overflow: hidden;
  -webkit-mask-image: linear-gradient(to right, transparent, #000 6%, #000 94%, transparent);
  mask-image: linear-gradient(to right, transparent, #000 6%, #000 94%, transparent);
}
.rv-run { display: flex; width: max-content; animation: rv-drift 78s linear infinite; }
.rv-set { display: flex; gap: 10px; padding-right: 10px; }
/* reverse gives the second row its left-to-right travel off the same keyframe */
.rv-row[data-dir="r"] .rv-run { animation-direction: reverse; }
@keyframes rv-drift { to { transform: translateX(-50%); } }
/* let a reader stop a card they are part-way through */
.rv-row:hover .rv-run,
.rv-row:focus-within .rv-run { animation-play-state: paused; }

.rv {
  position: relative; overflow: hidden; isolation: isolate;
  flex: 0 0 auto; width: clamp(268px, 25vw, 340px);
  display: flex; flex-direction: column;
  margin: 0; padding: 18px;
  border: 1px dashed var(--line-strong);
}
/* GridPattern as a masked background rather than an <svg> per card — forty
   inline patterns would be forty DOM subtrees for one decorative texture, and
   masking lets it take the theme colour the way the menu icons do. */
.rv-grid {
  position: absolute; top: 0; left: 50%; width: 100%; height: 100%;
  margin: -8px 0 0 -80px; pointer-events: none; z-index: -1;
  background: var(--line-strong); opacity: .5;
  -webkit-mask-image:
    url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="25" height="25"><path d="M.5 25V.5H25" fill="none" stroke="black" stroke-dasharray="3"/></svg>'),
    radial-gradient(farthest-side at top, #000, transparent),
    linear-gradient(#000, transparent);
  mask-image:
    url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="25" height="25"><path d="M.5 25V.5H25" fill="none" stroke="black" stroke-dasharray="3"/></svg>'),
    radial-gradient(farthest-side at top, #000, transparent),
    linear-gradient(#000, transparent);
  -webkit-mask-composite: source-in; mask-composite: intersect;
  -webkit-mask-repeat: repeat, no-repeat, no-repeat; mask-repeat: repeat, no-repeat, no-repeat;
}
.rv blockquote { margin: 0 0 16px; }
.rv blockquote p {
  margin: 0; font-size: 14px; font-weight: 300; line-height: 1.6;
  letter-spacing: 0; color: var(--ink);
}
.rv figcaption { margin-top: auto; }
.rv b { display: block; font-size: 13px; font-weight: 600; color: var(--ink); }
.rv em {
  display: block; margin-top: 2px; font-style: normal;
  font-size: 11px; letter-spacing: 0; color: var(--muted);
}

/* The three rotated ellipses behind the section. They put a visible pink
   haze down the left of an otherwise continuous white page, which is
   exactly the kind of "different thing in between" the single ground is
   meant to remove. Off — the markup stays, so deleting this one rule
   brings them back. */
.voices-wash { display: none; }
.voices-wash-off { position: absolute; inset: 0; z-index: 0; overflow: hidden; pointer-events: none; }
.voices-wash i {
  position: absolute; top: 0; left: 0; height: 1280px; border-radius: 50%;
  transform: translateY(-350px) rotate(-45deg);
}
.voices-wash i:nth-child(1) { width: 560px; background: radial-gradient(68% 69% at 55% 31%, var(--brand-soft) 0, transparent 60%); }
.voices-wash i:nth-child(2) { width: 240px; transform: translate(5%, -50%) rotate(-45deg); background: radial-gradient(50% 50% at 50% 50%, var(--brand-soft) 0, transparent 80%); }
.voices-wash i:nth-child(3) { width: 240px; background: radial-gradient(50% 50% at 50% 50%, var(--brand-soft) 0, transparent 80%); }
#stories { position: relative; }
#stories .wrap { position: relative; z-index: 1; }

/* motion off: the rows stop and become a plain scroller the reader drives */
@media (prefers-reduced-motion: reduce) {
  .rv-run { animation: none; }
  .rv-row { overflow-x: auto; }
  .rv-row .rv-set[aria-hidden="true"] { display: none; }
}

/* ── faq ────────────────────────────────────────────────────────────── */
/* the questions keep a reading measure, but on the same left rail as every
   other section rather than centred in the shell */
/* ── FAQ intro capsule ──────────────────────────────────────────────────
   The reference's rotating beam, breathing ring, sweeping meter and running
   tick, at the site's tint. Everything here is decorative and aria-hidden. */
.fq-intro {
  position: relative; isolation: isolate; overflow: hidden;
  display: flex; align-items: center; gap: .85rem;
  width: 100%; max-width: 24rem; margin: 0 auto clamp(34px, 4vw, 56px);
  padding: .85rem 1.4rem; border-radius: 999px;
  border: 1px solid var(--line-strong); background: var(--surface-2);
  /* the reference sets .65rem (10.4px); this page holds an 11px floor, so the
     tracking gives back the width the extra half-pixel takes */
  font-size: 11px; text-transform: uppercase; letter-spacing: 0.12em;
  color: var(--muted);
  opacity: 0; transform: translate3d(0, 12px, 0); filter: blur(8px);
  animation: fqi-in .72s var(--ease) .1s forwards;
}
@keyframes fqi-in { to { opacity: 1; transform: none; filter: blur(0); } }

.fq-intro > span { position: relative; z-index: 1; }
.fqi-beam, .fqi-pulse { position: absolute; inset: -110%; z-index: 0; border-radius: 50%; pointer-events: none; }
.fqi-beam {
  background: conic-gradient(from 160deg, var(--brand-soft), transparent 32%,
              rgba(174, 14, 32, .13) 58%, transparent 78%, var(--brand-soft));
  animation: fqi-spin 18s linear infinite; opacity: .75;
}
@keyframes fqi-spin { to { transform: rotate(360deg); } }
.fqi-pulse {
  border: 1px solid currentColor; opacity: .22;
  animation: fqi-pulse 3.4s ease-out infinite;
}
@keyframes fqi-pulse {
  0%   { transform: scale(.7);  opacity: .5; }
  60%  { opacity: .08; }
  100% { transform: scale(1.25); opacity: 0; }
}
.fqi-label { font-weight: 600; letter-spacing: 0.12em; color: var(--ink); white-space: nowrap; }
.fqi-meter {
  flex: 1 1 auto; height: 1px; opacity: .7;
  background: linear-gradient(90deg, transparent, currentColor 35%, transparent 85%);
  transform: scaleX(0); transform-origin: left;
  animation: fqi-meter 5.8s ease-in-out infinite;
}
@keyframes fqi-meter {
  0%, 20%   { transform: scaleX(0); transform-origin: left; }
  45%, 60%  { transform: scaleX(1); transform-origin: left; }
  80%, 100% { transform: scaleX(0); transform-origin: right; }
}
.fqi-tick {
  width: .55rem; height: .55rem; border-radius: 999px; flex: none;
  background: var(--brand); box-shadow: 0 0 0 4px var(--brand-soft);
  animation: fqi-tick 3.2s ease-in-out infinite;
}
@keyframes fqi-tick {
  0%, 30% { transform: translateX(-6px); opacity: .4; }
  50%     { transform: translateX(2px);  opacity: 1; }
  100%    { transform: translateX(20px); opacity: 0; }
}

/* header row: copy left, mode control right — the reference's md:flex-row */
.fq-head { display: flex; flex-direction: column; gap: 26px; }
.fq-head .kicker { margin-top: 0; }
.fq-head .lede { margin-bottom: 0; }
@media (min-width: 820px) {
  .fq-head { flex-direction: row; align-items: flex-end; justify-content: space-between; gap: 40px; }
  .fq-mode { flex: none; margin-bottom: 4px; }
}

.fq-mode {
  display: inline-flex; align-items: center; gap: 12px;
  height: 44px; padding: 0 20px; border-radius: 999px;
  border: 1px solid var(--line-strong); background: var(--surface);
  font: inherit; font-size: 14px; font-weight: 500; color: var(--ink);
  cursor: pointer; transition: border-color .3s, background .5s, transform .2s var(--ease);
}
.fq-mode:hover { border-color: var(--brand-line); transform: translateY(-1px); }
.fq-mode:focus-visible { outline: 2px solid var(--brand); outline-offset: 3px; }
.fqm-dot { position: relative; display: flex; align-items: center; justify-content: center; width: 24px; height: 24px; }
.fqm-dot::before {
  content: ''; position: absolute; inset: 0; border-radius: 50%;
  border: 1px solid var(--line-strong); opacity: .55;
}
:root[data-theme="dark"] .fqm-dot::before { animation: fqm-breathe 2s ease-in-out infinite; }
@keyframes fqm-breathe { 50% { opacity: .18; } }
.fqm-dot i { width: 12px; height: 12px; border-radius: 50%; background: var(--ink); transition: background .5s; }

/* ── FAQ ────────────────────────────────────────────────────────────────
   Native port of the monochrome FAQ component. Carried over: the card stack
   with a pointer-tracked radial glow, the circular plus that rotates to a
   cross, the meta tag, the lift on hover, and the height transition. Left
   behind: its own theme toggle (this page already has one in the header, and
   a second control writing to a different storage key would fight it) and its
   slate palette. */
.faq {
  list-style: none; margin: clamp(30px, 3.5vw, 46px) 0 0; padding: 0;
  max-width: 900px; display: grid; gap: 12px;
}
.fq {
  position: relative; isolation: isolate; overflow: hidden;
  border: 1px solid var(--line); border-radius: 20px;
  background: var(--surface);
  transition: transform .4s var(--ease), border-color .3s, box-shadow .4s var(--ease);
}
.fq:hover, .fq:focus-within { transform: translateY(-2px); border-color: var(--line-strong); }
.fq[data-open="true"] { border-color: var(--brand-line); }

/* the glow follows the pointer; --fx/--fy are written by the handler and fall
   back to the card's centre so a keyboard user still gets the lit state */
.fq-glow {
  position: absolute; inset: 0; z-index: -1; pointer-events: none;
  opacity: 0; transition: opacity .45s var(--ease);
  background: radial-gradient(240px circle at var(--fx, 50%) var(--fy, 50%), var(--brand-soft), transparent 70%);
}
.fq:hover .fq-glow, .fq[data-open="true"] .fq-glow { opacity: 1; }

.fq h3 { margin: 0; font: inherit; }
.fq button {
  display: flex; align-items: flex-start; gap: clamp(14px, 1.6vw, 22px);
  width: 100%; padding: clamp(18px, 2vw, 26px) clamp(18px, 2.2vw, 30px);
  border: 0; background: none; cursor: pointer; font: inherit; text-align: left;
}
.fq button:focus-visible { outline: 2px solid var(--brand); outline-offset: -3px; border-radius: 20px; }

/* The original control — a 48px disc on --surface-2 with the old orange
   --brand — is gone; the ledger block below draws it. Only the <svg>
   sizing survives, for the markup that still ships an inline icon. */
.fq-ic svg { width: 19px; height: 19px; }

.fq-tx { display: flex; flex-direction: column; gap: 7px; min-width: 0; flex: 1; }
/* question left, tag hard right — the reference's sm:ml-auto */
.fq-row { display: flex; align-items: center; gap: 16px; }
@media (max-width: 620px) { .fq-row { flex-direction: column; align-items: flex-start; gap: 8px; } }
.fq-q {
  font-size: clamp(16px, 1.4vw, 18px); font-weight: 600; line-height: 1.4;
  letter-spacing: 0; color: var(--ink);
}
.fq-meta {
  margin-left: auto; flex: none;
  padding: 4px 11px; border: 1px solid var(--line); border-radius: 999px;
  font-size: 11px; font-weight: 600; letter-spacing: 0.12em;
  text-transform: uppercase; color: var(--muted);
}

/* grid-rows animates to auto height without measuring anything in JS */
.fq-p { display: grid; grid-template-rows: 0fr; transition: grid-template-rows .45s var(--ease); }
.fq[data-open="true"] .fq-p { grid-template-rows: 1fr; }
.fq-p > div { overflow: hidden; min-height: 0; }
.fq-p p {
  margin: 0; padding: 0 clamp(18px, 2.2vw, 30px) clamp(20px, 2.2vw, 28px)
                     calc(48px + clamp(18px, 2.2vw, 30px) + clamp(14px, 1.6vw, 22px));
  font-size: clamp(14px, 1.15vw, 16px); line-height: 1.6; color: var(--muted);
}
@media (max-width: 560px) {
  .fq-p p { padding-left: clamp(18px, 2.2vw, 30px); }
  .fq-meta { font-size: 11px; letter-spacing: 0.12em; }
}
@media (prefers-reduced-motion: reduce) {
  .fq, .fq-ic, .fq-ic svg, .fq-p, .fq-glow { transition: none; }
}


/* ── closing band ───────────────────────────────────────────────────── */
.cta-band { text-align: center; padding-bottom: clamp(48px, 6vw, 84px); }
.cta-band .lede { margin-inline: auto; }
.cta-band .hero-cta { margin-top: 30px; justify-content: center; }

/* ── footer ─────────────────────────────────────────────────────────── */
/* the footer's own ground is set further down; the hard rule here was the last
   visible seam on the page */
.foot { background: var(--surface-2); }
.foot-in { display: grid; grid-template-columns: 1.3fr 2.7fr; gap: 48px; padding-block: clamp(44px, 5vw, 64px); }
.foot-brand p { margin: 16px 0 0; font-size: 13px; line-height: 1.6; color: var(--muted); max-width: 300px; }
.foot-legal { font-size: 12px !important; color: var(--faint) !important; }
.foot-cols { display: grid; grid-template-columns: repeat(4, 1fr); gap: 28px; }
.foot-cols h4 {
  margin: 0 0 12px; font-size: 11px; font-weight: 700;
  letter-spacing: 0.08em; text-transform: uppercase; color: var(--muted);
}
.foot-cols a {
  display: block; font-size: 13px;
  /* padding, not margin — the gap between links has to be part of the hit area,
     otherwise every footer link is a 15px-tall target on a touchscreen */
  padding-block: 5px;
  color: var(--muted); text-decoration: none; transition: color .2s;
}
.foot-cols a:hover { color: var(--ink); }
.foot-bar {
  display: flex; align-items: center; justify-content: space-between; gap: 20px; flex-wrap: wrap;
  padding-block: 18px; border-top: 1px solid var(--line);
}
.foot-bar p { margin: 0; font-size: 12px; color: var(--faint); }
.foot-note { color: var(--muted) !important; }

@media (max-width: 960px) {
  .cards3 { grid-template-columns: 1fr 1fr; }   /* .grid6 sizes itself, see the bento block */
  .foot-in { grid-template-columns: 1fr; gap: 36px; }
  .foot-cols { grid-template-columns: 1fr 1fr; }
}
@media (max-width: 640px) {
  .cards3, .band { grid-template-columns: 1fr; }
  .band { text-align: left; }
}

/* section icon masks */
.scard-ic.mi-board::after, .feat-ic.mi-board::after { -webkit-mask-image: var(--i-board); mask-image: var(--i-board); }
.scard-ic.mi-search::after, .feat-ic.mi-search::after { -webkit-mask-image: var(--i-search); mask-image: var(--i-search); }
.scard-ic.mi-pen::after, .feat-ic.mi-pen::after { -webkit-mask-image: var(--i-pen); mask-image: var(--i-pen); }
.scard-ic.mi-users::after, .feat-ic.mi-users::after { -webkit-mask-image: var(--i-users); mask-image: var(--i-users); }
.scard-ic.mi-code::after, .feat-ic.mi-code::after { -webkit-mask-image: var(--i-code); mask-image: var(--i-code); }
.scard-ic.mi-doc::after, .feat-ic.mi-doc::after { -webkit-mask-image: var(--i-doc); mask-image: var(--i-doc); }
.scard-ic.mi-shield::after, .feat-ic.mi-shield::after { -webkit-mask-image: var(--i-shield); mask-image: var(--i-shield); }
.scard-ic.mi-chart::after, .feat-ic.mi-chart::after { -webkit-mask-image: var(--i-chart); mask-image: var(--i-chart); }
.scard-ic.mi-cap::after, .feat-ic.mi-cap::after { -webkit-mask-image: var(--i-cap); mask-image: var(--i-cap); }
.scard-ic.mi-tools::after, .feat-ic.mi-tools::after { -webkit-mask-image: var(--i-tools); mask-image: var(--i-tools); }
.scard-ic.mi-lab::after, .feat-ic.mi-lab::after { -webkit-mask-image: var(--i-lab); mask-image: var(--i-lab); }
.scard-ic.mi-rocket::after, .feat-ic.mi-rocket::after { -webkit-mask-image: var(--i-rocket); mask-image: var(--i-rocket); }
.scard-ic.mi-book::after, .feat-ic.mi-book::after { -webkit-mask-image: var(--i-book); mask-image: var(--i-book); }
.scard-ic.mi-star::after, .feat-ic.mi-star::after { -webkit-mask-image: var(--i-star); mask-image: var(--i-star); }
.scard-ic.mi-bolt::after, .feat-ic.mi-bolt::after { -webkit-mask-image: var(--i-bolt); mask-image: var(--i-bolt); }
.scard-ic.mi-id::after, .feat-ic.mi-id::after { -webkit-mask-image: var(--i-id); mask-image: var(--i-id); }
.scard-ic.mi-award::after, .feat-ic.mi-award::after { -webkit-mask-image: var(--i-award); mask-image: var(--i-award); }

/* ── touch targets ───────────────────────────────────────────────────────
   Placed at the end of the file on purpose: the base .foot-cols rule sits
   further up, and at equal specificity the later rule is the one that wins. */
@media (max-width: 1024px) {
  .foot-cols a { padding-block: 11px; }   /* ~37px row, thumb-sized */
}

/* ── animated border ─────────────────────────────────────────────────────
   Port of the button-border component. A 20px gradient square rides the
   button's outline via offset-path/offset-distance — the same two properties
   framer-motion was tweening, so CSS drives it directly and no library is
   needed. The overlay is masked to its own border ring so only that 2px band
   shows; everything inside stays the button.

   One deliberate change: the component composites `linear-gradient(transparent,
   transparent)` against black with `mask-composite: intersect`, which
   multiplies to zero alpha and paints nothing. The ring wants border-box MINUS
   padding-box, so both layers are opaque and the operator is `exclude`.

   Colour comes from --brand, so the comet is deep rust on light and the
   lighter orange on dark, matching the rest of the theme. */
 .burger { position: relative; }

.ab {
  position: absolute; inset: -1px;
  border: 2px solid transparent; border-radius: 12px;
  pointer-events: none; overflow: hidden;
  -webkit-mask-image: linear-gradient(#000, #000), linear-gradient(#000, #000);
          mask-image: linear-gradient(#000, #000), linear-gradient(#000, #000);
  -webkit-mask-clip: padding-box, border-box;
          mask-clip: padding-box, border-box;
  -webkit-mask-composite: xor;      /* legacy keyword — same result as exclude */
          mask-composite: exclude;
}
.ab i {
  position: absolute; width: 20px; aspect-ratio: 1;
  background: linear-gradient(90deg, transparent, var(--brand), var(--brand));
  offset-path: rect(0 auto auto 0 round 20px);
  animation: ab-run 5s linear infinite;
}
@keyframes ab-run { from { offset-distance: 0%; } to { offset-distance: 100%; } }

/* offset-path: rect() is recent; without it the square would sit in the corner,
   so fall back to a plain brand hairline rather than a stray dot */
@supports not (offset-path: rect(0 auto auto 0 round 2px)) {
  .ab i { display: none; }
  .ab { border-color: var(--brand-line); }
}
@media (prefers-reduced-motion: reduce) {
  .ab i { animation: none; }
}

/* ── copy sitting on the hero video ──────────────────────────────────────
   End of file on purpose: the base .sub / .assure rules are further up, and
   at equal specificity the later rule wins.

   Measured against the brightest part of the aurora, the muted greys fell to
   4.26:1 and 2.21:1 — under AA. Darkening the copy fixes that without
   thickening the veil, so the backdrop stays as visible as it was asked to be.
   Only the two lines that sit on the video change; everything lower is behind
   an opaque scrim and keeps the normal tokens. */
@media (max-width: 1024px) {
  .sub    { color: #35353f; }
  .assure { color: #45454f; }
  :root[data-theme="dark"] .sub    { color: #e2d9d3; }
  :root[data-theme="dark"] .assure { color: #c7bbb3; }
}

/* ── hero entrance ───────────────────────────────────────────────────────
   Port of the AcmeHero component's motion choreography: every element starts
   at opacity 0, offset 20px down, and fades up in a staggered cascade —
   headline 0.2s, copy 0.3s, buttons 0.4s, then the preview last at 0.6s from
   40px over 0.8s, exactly the delays and durations the component declares.

   framer-motion's `initial`/`animate` pair maps onto a keyframe with
   `animation-fill-mode: both`: `both` holds the from-state through the delay,
   which is what `initial` does, and holds the end-state afterwards. No
   library, and nothing here touches layout — only opacity and transform, so
   the entrance cannot shift the page as it plays. */

@keyframes hero-rise {
  from { opacity: 0; transform: translateY(20px); }
  to   { opacity: 1; transform: none; }
}
@keyframes hero-rise-far {
  from { opacity: 0; transform: translateY(40px); }
  to   { opacity: 1; transform: none; }
}

.hero-copy > *,
.hero-rot,
.dash-wrap,
.marquee { animation: hero-rise .5s var(--ease) both; }

.hero-copy > .pill-note { animation-delay: .05s; }
.hero-copy > h1         { animation-delay: .2s; }
.hero-copy > .sub       { animation-delay: .3s; }
.hero-copy > .hero-cta  { animation-delay: .4s; }
.hero-copy > .assure    { animation-delay: .46s; }
.hero-copy > .stats     { animation-delay: .52s; }

/* the preview arrives last, from further away and more slowly — the component's
   one deliberate departure from the 20px/0.5s rhythm above */
/* the label lands just before the window it captions */
.hero-rot  { animation-delay: .54s; }
.dash-wrap { animation: hero-rise-far .8s var(--ease) both; animation-delay: .6s; }
.marquee   { animation-delay: .78s; }

@media (prefers-reduced-motion: reduce) {
  .hero-copy > *, .hero-rot, .dash-wrap, .marquee { animation: none; }
}

/* ── desktop hero grid: dulled ───────────────────────────────────────────
   --grid and --grid-hi are read in exactly two places, .hero::before and
   .hero::after, and both are display:none at ≤1024 where the video backdrop
   takes over. Scoping to min-width 1025 anyway, so "desktop only" is enforced
   by the rule rather than by that coincidence holding true later.

   Baseline drops .08 → .034 and the pointer spotlight .34 → .13 — roughly
   2.4× fainter, so the lattice reads as a texture that answers the cursor
   rather than a drawn grid. */
@media (min-width: 1025px) {
  :root {
    --grid: rgba(10,10,16,.034);
    --grid-hi: rgba(10,10,16,.13);
  }
  :root[data-theme="dark"] {
    --grid: rgba(255,255,255,.032);
    --grid-hi: rgba(255,255,255,.12);
  }
}

/* ── who we are ──────────────────────────────────────────────────────────
   Port of AboutSection3. Kept: the clip-path banner, the stat cluster, the
   staggered blur-and-rise reveal (TimelineContent) and the per-word cut
   reveal on the headline (VerticalCutReveal).

   Dropped: the whole social-icon row, on request — those belong in the
   footer, and four external SVGs hotlinked from a demo host is not something
   this site should ship regardless.

   Retimed: the source staggers TimelineContent by `i * 0.4s` across 15
   elements, so the last one starts six seconds after the section appears, and
   its headline carries an additional 3s delay on top. That reads as broken
   rather than deliberate on a real page. Same choreography at 0.09s per step
   — the last element lands just under a second in.

   Realigned: the source right-aligns its closing column. Every other section
   here sits on one left rail, so that column is left-aligned too. */

.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

.about { position: relative; padding: var(--sec-y) 0; }


.ab-kicker {
  display: flex; align-items: center; gap: 10px; margin: 0 0 22px;
  font-size: 12px; font-weight: 700; letter-spacing: 0.12em;
  text-transform: uppercase; color: var(--muted);
}
.ab-star { color: var(--brand); font-size: 14px; animation: ab-spin 9s linear infinite; }
@keyframes ab-spin { to { transform: rotate(360deg); } }

.ab-fig { margin: 0; }
/* the <svg> only carries the clipPath definition now — it must not take space */
.ab-clipdef { position: absolute; width: 0; height: 0; overflow: hidden; }
/* clipPathUnits="objectBoundingBox" means the notch scales with the box, so one
   path fits every viewport without a second asset or a media query */
.ab-clip {
  position: relative; width: 100%; height: clamp(190px, 26vw, 360px);
  overflow: hidden; background: var(--surface-2);
  -webkit-clip-path: url(#ab-clip); clip-path: url(#ab-clip);
}
.ab-clip video {
  position: absolute; inset: 0; display: block;
  width: 100%; height: 100%; object-fit: cover;
}

/* ── stat cluster ───────────────────────────────────────────────────── */
.ab-stats {
  display: flex; align-items: flex-end; justify-content: space-between;
  gap: 24px; flex-wrap: wrap; padding: 20px 0 clamp(28px, 4vw, 48px);
}
.ab-small { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.ab-small p { margin: 0; font-size: 14px; color: var(--muted); }
.ab-small b, .ab-big b { color: var(--brand); font-weight: 700; }
.ab-sep { color: var(--line-strong); }
.ab-big { text-align: right; }
.ab-big p { margin: 0; }
.ab-big p:first-child {
  font-size: clamp(26px, 3.4vw, 40px); font-weight: 600; letter-spacing: -0.01em; line-height: 1.12;
}
.ab-big p:first-child span { color: var(--ink); text-transform: uppercase; font-size: .62em; letter-spacing: 0.08em; }
.ab-note { margin-top: 6px !important; font-size: 13px; color: var(--faint); }

/* ── body ───────────────────────────────────────────────────────────── */
.ab-body { display: grid; grid-template-columns: minmax(0, 2fr) minmax(0, 1fr); gap: clamp(32px, 5vw, 72px); }
.ab-h {
  margin: 0 0 clamp(24px, 3vw, 36px); max-width: 20ch;
  font-family: var(--font-display);
  font-size: clamp(32px, 3.8vw, 52px); line-height: 1.12;
  letter-spacing: 0; font-weight: 400; color: var(--ink);
}
.ab-cols { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: clamp(24px, 3vw, 40px); }
.ab-cols p { margin: 0; font-size: 14px; line-height: 1.6; color: var(--muted); }

.ab-side { display: flex; flex-direction: column; align-items: flex-start; }
.ab-name { margin: 0; }
.ab-name .brand-mark { width: 137px; height: 44px; }
.ab-role { margin: 8px 0 0; font-size: 13px; color: var(--muted); }
.ab-ask { margin: clamp(20px, 3vw, 30px) 0 18px; font-size: 14px; line-height: 1.6; font-weight: 500; color: var(--ink); }
.ab-cta {
  display: inline-flex; align-items: center; gap: 8px;
  padding: 12px 20px; border-radius: 10px; text-decoration: none;
  background: var(--ink); color: var(--on-brand);
  font-size: 14px; font-weight: 600;
  box-shadow: 0 10px 26px -12px rgba(var(--sh), .5);
  transition: gap .3s var(--ease), transform .2s var(--ease);
}
.ab-cta:hover { gap: 14px; transform: translateY(-1px); }
.ab-cta svg { width: 15px; height: 15px; flex: none; }

/* ── reveal (TimelineContent) ───────────────────────────────────────── */
.tl { opacity: 0; transform: translateY(-20px); filter: blur(10px); }
.about.in .tl {
  animation: tl-in .5s var(--ease) both;
  animation-delay: calc(var(--n, 0) * .09s);
}
@keyframes tl-in {
  from { opacity: 0; transform: translateY(-20px); filter: blur(10px); }
  to   { opacity: 1; transform: none; filter: blur(0); }
}

/* ── headline cut reveal (VerticalCutReveal) ────────────────────────── */
.ab-h .vc { display: inline-flex; overflow: hidden; vertical-align: bottom; }
.ab-h .vc > span { display: inline-block; transform: translateY(-110%); }
.about.in .ab-h .vc > span {
  animation: vc-in .62s cubic-bezier(.22, 1.08, .36, 1) both;
  animation-delay: calc(.15s + var(--w) * .06s);
}
@keyframes vc-in { to { transform: none; } }

@media (max-width: 860px) {
  .ab-stats { align-items: flex-start; }
  .ab-small { display: grid; gap: 4px; }
  .ab-sep { display: none; }        /* stacked, it strands at the line start */
  .ab-big { text-align: left; }
  .ab-body { grid-template-columns: minmax(0, 1fr); }
  .ab-cols { grid-template-columns: minmax(0, 1fr); }
  .ab-side { border-top: 1px solid var(--line); padding-top: clamp(24px, 5vw, 34px); }
}

@media (prefers-reduced-motion: reduce) {
  .tl, .about.in .tl { animation: none; opacity: 1; transform: none; filter: none; }
  .ab-h .vc > span, .about.in .ab-h .vc > span { animation: none; transform: none; }
  .ab-star { animation: none; }
}

/* ── sticky footer ───────────────────────────────────────────────────────
   Port of StickyFooter. The reveal is the component's: the <footer> is a
   fixed-height box carrying a clip-path, which makes it the containing block
   for the position:fixed layer inside it. That layer is pinned to the bottom
   of the viewport but painted only where the footer's box has scrolled into
   view, so the page appears to slide up off the footer rather than push it.

   Adapted: the source hard-codes 720px in three places (height, height, and
   the sticky top offset). One custom property drives all three here, because
   this footer holds less than the source's 54 links and 720px would leave a
   third of it empty. The radial decorations become three positioned blobs on
   --ink instead of Tailwind's --theme() colour function.

   Below 861px the mechanic is dropped entirely: the stacked footer is taller
   than any sensible fixed height, and a fixed layer that overflows its clip
   would strand the last links off-screen with no way to reach them. */

.foot {
  --foot-h: clamp(460px, 62vh, 620px);
  position: relative; width: 100%; height: var(--foot-h);
  clip-path: polygon(0 0, 100% 0, 100% 100%, 0 100%);
  background: var(--surface-2);
}
.foot-fixed { position: fixed; bottom: 0; left: 0; width: 100%; height: var(--foot-h); }
.foot-stick { position: sticky; top: calc(100vh - var(--foot-h)); height: 100%; overflow-y: auto; }
.foot-panel {
  position: relative; display: flex; flex-direction: column; justify-content: space-between;
  min-height: 100%; padding-block: clamp(30px, 4vw, 48px) 0;
  /* The hover-footer reference's background, and only its background —
     a radial wash centred high and wide (125% at 50% 10%), dark through
     the middle and lifting to a blue at the edges. It is layered OVER
     the panel's own vertical gradient rather than replacing it, so the
     footer still reads against whichever theme the page is in.

     The light theme gets a much weaker version: the reference's 40%
     black overlay is built for a near-black page and would simply
     darken a bone-coloured one until the links stopped reading. */
  background:
    radial-gradient(125% 125% at 50% 10%, rgba(15, 15, 17, .04) 50%, rgba(60, 162, 250, .10) 100%),
    linear-gradient(180deg, var(--bg), var(--surface-2) 22%);
}

/* the source's three rotated radial washes */
.foot-glow { position: absolute; inset: 0; z-index: 0; overflow: hidden; pointer-events: none; }
.foot-glow i {
  position: absolute; top: 0; left: 0; border-radius: 50%;
  transform-origin: top left; rotate: -45deg;
}
.foot-glow i:nth-child(1) {
  width: 560px; height: 1280px; translate: 0 -350px;
  background: radial-gradient(68% 69% at 55% 31%, rgba(var(--sh), .07) 0, rgba(var(--sh), .02) 50%, transparent 80%);
}
.foot-glow i:nth-child(2) {
  width: 240px; height: 1280px; translate: 5% -50%;
  background: radial-gradient(50% 50% at 50% 50%, rgba(var(--sh), .05) 0, rgba(var(--sh), .01) 80%, transparent 100%);
}
.foot-glow i:nth-child(3) {
  width: 240px; height: 1280px; translate: 0 -350px;
  background: radial-gradient(50% 50% at 50% 50%, rgba(var(--sh), .05) 0, rgba(var(--sh), .01) 80%, transparent 100%);
}
:root[data-theme="dark"] .foot-glow i:nth-child(1) {
  background: radial-gradient(68% 69% at 55% 31%, rgba(255,255,255,.05) 0, rgba(255,255,255,.015) 50%, transparent 80%);
}
:root[data-theme="dark"] .foot-glow i:nth-child(2),
:root[data-theme="dark"] .foot-glow i:nth-child(3) {
  background: radial-gradient(50% 50% at 50% 50%, rgba(255,255,255,.035) 0, rgba(255,255,255,.01) 80%, transparent 100%);
}

/* .foot-panel is a column flex container, and .wrap carries `margin: 0 auto`.
   Auto inline margins on a flex item cancel the default stretch, so the block
   shrank to its content and centred itself 25px off the page's left rail. */
.foot-in, .foot-bar { position: relative; z-index: 1; width: 100%; }

/* social row — the icons this footer was always meant to carry */
.foot-social { display: flex; gap: 8px; margin-top: 18px; }
.foot-social a {
  width: 34px; height: 34px; border-radius: 9px;
  display: inline-flex; align-items: center; justify-content: center;
  border: 1px solid var(--line); background: var(--surface);
  color: var(--muted); transition: color .2s, border-color .2s, transform .2s var(--ease);
}
.foot-social a:hover { color: var(--brand); border-color: var(--brand-line); transform: translateY(-1px); }
.foot-social svg { width: 17px; height: 17px; }

/* reveal, matching the component's blur/rise/stagger */
/* The stagger runs on load rather than on an intersection observer. It
   used to be gated behind a `.in` class added by about.js — which meant
   any page not loading that script (the whole /get-started/ page, every
   error page) shipped a footer that was present in the DOM and
   permanently invisible at opacity 0. Content must not depend on script
   to be seen, so the animation is now pure CSS; the footer sits below
   the fold, so it has finished long before anyone scrolls to it. */
/* The footer entrance animation was removed on request. `.fa` is kept as
   a hook (the markup still carries it and its --n order) but no longer
   animates, hides or transforms anything — the footer is simply there
   when the page is. */
.fa { opacity: 1; transform: none; filter: none; }

/* (A max-width:860px block used to undo the fixed-footer mechanism on
   small screens — the footer is now in normal flow at every width, so
   there is nothing left for it to undo.) */


/* ── WebGL deck ──────────────────────────────────────────────────────────
   The canvas is added by deck3d.js and only takes over once all four textures
   have uploaded — until then, and on any machine without WebGL, the CSS cards
   underneath are what renders. The <img> elements stay in the DOM permanently:
   they are the texture source, the alt text, and the no-GL fallback in one. */
/* the canvas is deliberately larger than the deck so cards can fly past its
   edges — which means it must not exist until GL actually owns the deck, or it
   overflows the page on any layout where the deck is full width */
.deck-gl { display: none; }
.deck.gl .deck-gl { display: block; position: absolute; inset: 0; width: 100%; height: 100%; }
.deck.gl .dcard { opacity: 0 !important; pointer-events: none; }

/* ── WebGL deck ──────────────────────────────────────────────────────────
   The canvas is added by deck3d.js and only takes over once all four textures
   have uploaded — until then, and on any machine without WebGL, the CSS cards
   underneath are what renders. The <img> elements stay in the DOM permanently:
   they are the texture source, the alt text, and the no-GL fallback in one. */
/* the canvas is deliberately larger than the deck so cards can fly past its
   edges — which means it must not exist until GL actually owns the deck, or it
   overflows the page on any layout where the deck is full width */
.deck-gl { display: none; }
.deck.gl .deck-gl { display: block; position: absolute; inset: 0; width: 100%; height: 100%; }
.deck.gl .dcard { opacity: 0 !important; pointer-events: none; }

/* ── stage section ───────────────────────────────────────────────────────
   Full-bleed and committed dark in both themes. This is the one place on the
   site that stops being a document and becomes a scene, so it sets its own
   colour rather than inheriting: pale rust on near-white had no depth to it,
   and the 3D needs black to read against.

   Four elements per frame. Everything is sized off the viewport so the
   composition holds from a laptop to a 5K display. */

.story {
  --ink-3d: #f4efec;
  --dim-3d: #ab9f97;
  position: relative;
  background: #080706;
  color: var(--ink-3d);
}
/* the scene sits behind everything, edge to edge */
.deck { position: absolute; inset: 0; z-index: 0; }
.dcard {
  position: absolute; inset: 0; margin: 0; opacity: 0;
  transition: opacity .8s var(--ease);
}
.dcard[data-pos="0"] { opacity: .5; }
.dcard img { width: 100%; height: 100%; object-fit: cover; display: block; }


.st-stack { position: relative; min-height: 46vh; }

.st.on { opacity: 1; pointer-events: auto; }

.st-ix span { color: var(--brand-2); margin-right: 16px; }


/* type arrives heavy and slow — the single biggest difference between motion
   that feels expensive and motion that feels like a toy */
.st > * { opacity: 0; transform: translate3d(0, 34px, 0); }
.st.on > *:nth-child(1) { transition-delay: .06s }
.st.on > *:nth-child(2) { transition-delay: .14s }
.st.on > *:nth-child(3) { transition-delay: .24s }

/* one progress line replaces the dots and the counter it used to sit beside */

/* ── the six-stage sequence, at every width ──────────────────────────
   This used to be a desktop-only piece: below 861px the whole thing was
   flattened to a plain stacked list — the panel unpinned, the stages all
   shown at once with no cross-fade, the card deck reduced to static
   images and the progress rail hidden. story.js and deck3d.js carried
   matching 861px gates.

   All three gates are lifted, so a phone and a tablet get the same
   pinned, scroll-driven sequence a desktop does. What changes below 861px
   is the composition: there is no room for two columns, so the deck goes
   full-bleed — the photograph fills the whole pinned panel — and the copy
   is set over it. The rest of that layout is further down, after the base
   .st / .deck rules, which are declared BELOW this block and would
   otherwise win on source order at equal specificity. */
@media (max-width: 860px) {
  /* svh, not vh: vh is the toolbar-expanded height on mobile, which would
     push the bottom of each pinned stage out of sight */
  .story-panel { height: 100svh; }

  .story-grid {
    grid-template-columns: minmax(0, 1fr);
    align-content: center;
  }
  .story-rail { inset-inline: var(--gutter); }
}

@media (prefers-reduced-motion: reduce) {
  .st, .st > *, .dcard { transition: none; }
}

/* ── stage section ───────────────────────────────────────────────────────
   Rejoined to the page. This used to set its own near-black canvas, which
   made the site read as two different websites with a hard seam between
   them. It now inherits the theme like every other section: the drama comes
   from one large contained scene panel, not from inverting the page. */

.story { position: relative; background: transparent; }
.story-panel {
  position: sticky; top: 0; height: 100vh; overflow: hidden;
  display: grid; align-items: center;
}
/* One column at every width. The two-column split — copy left, a 4/3 card
   right — is gone: the photograph is the panel now, and the type is set over
   it. `position: static` is load-bearing, not tidying: .deck is a child of
   this grid, so while it was `relative` the deck's `inset: 0` resolved to
   the grid's own box. Static hands the deck to .story-panel, which is
   `sticky` and therefore the containing block. */
.story-grid {
  position: static;
  width: 100%; max-width: var(--shell); margin-inline: auto;
  padding-inline: var(--gutter);
  display: grid; align-items: center;
  grid-template-columns: minmax(0, 1fr);
}

/* z-index belongs here rather than on .story-grid — raising the grid would
   lift the photograph with the type, since .deck is inside it. .deck is
   z-index 0 and follows .st-stack in the DOM, which is enough to paint the
   WebGL canvas straight over the copy. story.js sets the height, per stage. */
.st-stack {
  position: relative; z-index: 1;
  /* a floor, not the layout: story.js sets an explicit height per stage. This
     only matters if the script never runs, where it keeps the panel from
     collapsing to nothing — .st is absolute and contributes no height. */
  min-height: clamp(240px, 30vh, 380px);
  transition: height .5s var(--ease);
}
.st {
  position: absolute; inset: 0; display: flex; flex-direction: column; justify-content: center;
  opacity: 0; pointer-events: none; transition: opacity .9s var(--ease);
}
.st.on { opacity: 1; pointer-events: auto; }

/* Over a photograph the type cannot take its colour from the theme — the
   ground is the same dark image in light mode and dark mode alike, so these
   are fixed light values rather than var(--ink) / var(--muted). Families and
   sizes still come from the role layer; only colour and shadow are set here. */
.st-ix {
  margin: 0 0 clamp(16px, 1.8vw, 26px);
  font-size: clamp(11px, .85vw, 13px); font-weight: 700;
  letter-spacing: 0.12em; text-transform: uppercase;
  color: rgba(255, 255, 255, .68);
}
.st-ix span { color: var(--brand-2); margin-right: 14px; }

/* Size, weight and tracking are inert here too; the role layer sets them. */
.st h2 {
  font-family: var(--font-display);
  margin: 0; max-width: 15ch;
  font-size: clamp(32px, 4.7vw, 64px); line-height: 1.12;
  letter-spacing: 0; font-weight: 400; color: #fff;
  text-shadow: 0 1px 26px rgba(0, 0, 0, .5), 0 1px 3px rgba(0, 0, 0, .34);
}
.st-d {
  margin: clamp(20px, 2.4vw, 30px) 0 0; max-width: 46ch;
  font-size: clamp(14px, 1.15vw, 16px); line-height: 1.6;
  color: rgba(255, 255, 255, .88);
  text-shadow: 0 1px 18px rgba(0, 0, 0, .55);
}
.st-note { color: rgba(255, 255, 255, .72); }

/* The publication caveat. Inherits colour and sizes in em, so it reads as a
   quieter aside in both the 3D stage and the stacked one without needing a
   value for either. */
.st-note {
  display: block; margin-top: .8em;
  font-size: .87em; line-height: 1.6; opacity: .74;
}

.st > * { opacity: 0; transform: translate3d(0, 28px, 0); }
.st.on > * {
  opacity: 1; transform: none;
  transition: opacity 1s cubic-bezier(.16,1,.3,1), transform 1.1s cubic-bezier(.16,1,.3,1);
}
.st.on > *:nth-child(1) { transition-delay: .06s }
.st.on > *:nth-child(2) { transition-delay: .14s }
.st.on > *:nth-child(3) { transition-delay: .24s }

/* The scene IS the panel — edge to edge, no card, no radius, no shadow. It
   was a contained 4/3 panel beside the copy; now the copy sits on it. */
.deck {
  position: absolute; inset: 0; z-index: 0;
  width: 100%; height: 100%; aspect-ratio: auto; max-height: none;
  border-radius: 0; overflow: hidden; box-shadow: none;
  background: #0b0a09;
}

/* The scrim. Weighted to the edges rather than laid flat over the whole
   frame: the top carries the floating header and the bottom the progress
   rail, while the middle needs only enough to hold contrast, so the
   photograph still reads as a photograph. Five stops, not two — a long
   two-stop ramp bands visibly on an OLED phone. Wide screens layer a
   second, horizontal pass over this; see the 861px block. */
.deck::after {
  content: ''; position: absolute; inset: 0; z-index: 1; pointer-events: none;
  background:
    linear-gradient(to bottom,
      rgba(6, 5, 4, .80) 0%,
      rgba(6, 5, 4, .58) 22%,
      rgba(6, 5, 4, .52) 50%,
      rgba(6, 5, 4, .58) 78%,
      rgba(6, 5, 4, .80) 100%);
}
.dcard { position: absolute; inset: 0; margin: 0; opacity: 0; transition: opacity .8s var(--ease); }
.dcard[data-pos="0"] { opacity: 1; }
.dcard img { width: 100%; height: 100%; object-fit: cover; display: block; }

/* progress reads as a hairline under the copy, not a bar across the page */
.story-rail {
  position: absolute; left: var(--gutter); bottom: clamp(28px, 5vh, 56px); z-index: 3;
  width: clamp(160px, 22vw, 300px); height: 2px; border-radius: 2px;
  background: var(--line);
  right: 0;
}
/* the track is var(--line), which disappears on a photograph — the fill
   keeps its brand gradient */
.story-rail { background: rgba(255, 255, 255, .24); }
.story-rail i {
  display: block; height: 100%; width: 100%; border-radius: 2px;
  transform-origin: left center; transform: scaleX(var(--fill, 0));
  background: linear-gradient(90deg, var(--brand), var(--brand-2));
  transition: transform .25s linear;
}

/* (A second max-width:860px block flattened the sequence here as well —
   panel static, stages all visible, deck static, rail hidden. It sat
   AFTER the first one and so won, which is why lifting only the earlier
   gate changed nothing. Removed with it; the narrow-screen sizing now
   lives in the block further up.) */
/* ── the copy, on wide screens ───────────────────────────────────────
   The layout is the same everywhere now; what changes with width is where
   the type sits on the frame and how hard the scrim has to work.

   Wide, the copy occupies the left of the image rather than the whole of
   it, so the scrim gets a second, horizontal pass — dark under the type,
   nearly clear over the right of the photograph. Two stacked alpha layers
   composite, so each is set lower than the effect it produces: .62 and .16
   read as .68 where they overlap. */
@media (min-width: 861px) {
  .story-grid { padding-block: clamp(80px, 12vh, 140px); }
  .st-stack { max-width: min(52%, 720px); }
  /* 15ch was a measure for a 581px column beside a card; on a 1425px frame
     it read as a cramped block, and every stage heading broke a line early.
     22ch is still a comfortable measure and lets the longest of them —
     "Start with a conversation," — hold its own line. */
  .st h2 { max-width: 22ch; }
  .deck::after {
    background:
      linear-gradient(to right,
        rgba(6, 5, 4, .62) 0%,
        rgba(6, 5, 4, .40) 40%,
        rgba(6, 5, 4, .12) 70%,
        rgba(6, 5, 4, .08) 100%),
      linear-gradient(to bottom,
        rgba(6, 5, 4, .42) 0%,
        rgba(6, 5, 4, .16) 25%,
        rgba(6, 5, 4, .16) 75%,
        rgba(6, 5, 4, .42) 100%);
  }
}

/* ── the copy, on narrow screens ─────────────────────────────────────
   There is no room to set the type beside anything, so it spans the frame
   and keeps the base vertical-only scrim. */
@media (max-width: 860px) {
  .story-grid { padding-block: clamp(64px, 11svh, 104px); }
  .st h2 { max-width: 18ch; }
  .st-d { max-width: 40ch; }
}

/* Short panels only. On a 320x640 phone the longest stage's copy is 554px of
   the 640, and the rail's 32px inset is more than the slack that leaves — the
   caveat's last line crossed it by 17px. Every taller phone has room, so this
   is scoped by height rather than paid for everywhere. */
@media (max-width: 560px) and (max-height: 700px) {
  .story-rail { bottom: calc(10px + env(safe-area-inset-bottom, 0px)); }
}

@media (prefers-reduced-motion: reduce) { .st, .st > *, .dcard { transition: none; } }

/* ── mobile navigation ───────────────────────────────────────────────────
   This was a port of the Sterling Gate menu: an overlay, three coloured
   backdrop panels sliding in at a stagger, five ambient SVG shapes blooming
   behind them, and rows rising with a 10deg rotation. The component ships
   five flat links and the motion is sized for that. This drawer carries five
   sections and 37 items, so the same sequence ran for about 970ms before the
   last row settled, over a surface the reader had already decided to use.

   What is left is the part that does work: a dimmed page behind, one surface
   arriving from the edge it is anchored to, and rows fading up with it. The
   entrance now finishes in under half the time and nothing in it is
   decoration. nav.js drives the timing; the geometry is here. */

/* The drawer used to stop at the bar: `inset: var(--hdr) 0 0` left the
   header showing above it, so a menu opened on a phone was a panel under
   a strip of the page it was covering. It takes the whole viewport now.

   That hides the burger along with the rest of the header, so the drawer
   carries its own way out — .mp-close below, sitting where the burger was
   so the control does not appear to move when the panel arrives. */
.mobile-nav { position: fixed; inset: 0; z-index: 55; }
.mobile-nav[hidden] { display: none; }

/* placed to land on the burger's own centre: the bar is --hdr tall with
   16px of side padding and the burger is 44 square, so 44 square inset by
   16 and (--hdr - 44) / 2 puts the cross exactly where the plus was */
.mp-close {
  position: absolute; z-index: 3;
  top: calc((var(--hdr) - 44px) / 2); right: 16px;
  width: 44px; height: 44px; padding: 0;
  display: grid; place-items: center;
  border-radius: 12px; border: 1px solid var(--line);
  background: var(--surface); color: var(--ink); cursor: pointer;
  transition: border-color .2s, color .2s, background .2s;
}
.mp-close svg { width: 20px; height: 20px; }
.mp-close:hover, .mp-close:focus-visible {
  border-color: var(--line-strong); background: var(--surface-2);
}
/* the list clears the button, and keeps clearing it as it scrolls under */
.mobile-panel .mp-scroll { padding-top: calc(var(--hdr) + 10px); }
.mn-overlay {
  position: absolute; inset: 0; opacity: 0;
  background: rgba(var(--sh), .34);
  -webkit-backdrop-filter: blur(3px); backdrop-filter: blur(3px);
}

/* the panel is the surface now, so it carries the ground the layers used to */
.mobile-panel {
  position: absolute; inset: 0; z-index: 1;
  display: flex; flex-direction: column;
  background: var(--surface); border-top: 0; animation: none;
  overflow: hidden;
  box-shadow: -1px 0 0 var(--line);
}

.mp-scroll { position: relative; z-index: 2; }

/* only the two properties the entrance actually animates */
.mp-scroll > *, .mp-foot > * { will-change: transform, opacity; }

/* the one address the footer and contact page already publish, so the
   drawer is not a dead end when nothing in the menu is what was wanted */
.mp-mail {
  text-align: center; text-decoration: none;
  font-size: 13px; color: var(--muted);
}
.mp-mail:hover, .mp-mail:active { color: var(--ink); }

/* ── the Menu / Close button ─────────────────────────────────────────── */
.burger {
  width: auto; gap: 10px; padding: 0 12px 0 14px;
  overflow: hidden;
}
.bg-tx {
  position: relative; display: block; height: 17px; overflow: hidden;
  font-size: 13px; font-weight: 600; letter-spacing: 0;
}
.bg-tx em {
  display: block; height: 17px; line-height: 17px; font-style: normal;
  transition: transform .5s var(--ease);
}
.burger[aria-expanded="true"] .bg-tx em { transform: translateY(-100%); }
.bg-ic { display: block; width: 12px; height: 12px; color: var(--ink); transition: transform .55s var(--ease); }
.bg-ic svg { width: 100%; height: 100%; display: block; }
.burger[aria-expanded="true"] .bg-ic { transform: rotate(315deg); }

@media (prefers-reduced-motion: reduce) {
  .bg-tx em, .bg-ic { transition: none; }
}

/* The label stays at every width. It used to be dropped below 420px because
   the header's 32px flex gap left no room for it — tightening the gap and the
   button's own padding buys back more than the word costs, so "Menu" survives
   down to 320. */
@media (max-width: 420px) {
  .hdr-inner { gap: 12px; }
  .burger { padding: 0 10px 0 12px; gap: 8px; }
  .bg-tx { font-size: 12px; }
}

/* ══ light-mode brand header ═══════════════════════════════════════════
   The bar itself carries the brand in light mode. Scoped with :not(dark)
   rather than [data-theme="light"] so it still applies before the pre-paint
   script runs, and appended last so it wins at equal specificity against the
   header block above.

   Not a flat fill: a shallow gradient across the bar keeps a 1600px-wide
   block of saturated colour from going dead in the middle. Dark mode is
   untouched — the bar stays near-black there, where an orange band would
   glare. */
/* :not(.on-hero) on every selector below. Over the cloud hero the bar goes
   clear and its type flips to near-black (see the hero-header block at the
   end of this file). This block is the reason it could not: scoped through
   :root:not(...) it outranks a plain .site-header.on-hero, so it kept
   repainting the orange ground and the white type straight back over it.
   Excluding the state here is the fix — raising the other block's
   specificity to out-shout this one would only start an arms race. */
:root:not([data-theme="dark"]) .site-header:not(.on-hero) {
  background: linear-gradient(102deg, #8f0914 0%, #ae0e20 44%, #c31428 100%);
  color: #fff;
}
:root:not([data-theme="dark"]) .site-header:not(.on-hero).scrolled {
  box-shadow: 0 0 0 1px rgba(255, 255, 255, .18),
              0 12px 34px -10px rgba(140, 48, 10, .45);
}

/* Every rule below is scoped to .site-header. The brand lockup also appears in
   the footer — unscoped, knocking it out to white made the footer logo
   invisible on its white panel. */
/* wordmark, and the badge inverts so it stays legible on its own ground */
:root:not([data-theme="dark"]) .site-header:not(.on-hero) .brand { color: #fff; }
:root:not([data-theme="dark"]) .site-header:not(.on-hero) .brand-mark { --logo-ink: #fff; }

/* nav: .88 white rather than pure, so the active item still has somewhere
   brighter to go — and it clears 4.5:1 on this ground either way */
:root:not([data-theme="dark"]) .site-header:not(.on-hero) .nav-links > a,
:root:not([data-theme="dark"]) .site-header:not(.on-hero) .menu-trigger { color: rgba(255, 255, 255, .88); }
:root:not([data-theme="dark"]) .site-header:not(.on-hero) .nav-links > a:hover,
:root:not([data-theme="dark"]) .site-header:not(.on-hero) .menu-trigger:hover,
:root:not([data-theme="dark"]) .site-header:not(.on-hero) .menu-trigger[aria-expanded="true"] {
  color: #fff; background: rgba(255, 255, 255, .14);
}
/* --line is a light-ground token; on the orange bar it reads as a hard white
   rule. Translucent white sits at the weight a separator should have. */
:root:not([data-theme="dark"]) .site-header:not(.on-hero) .nav-links > .menu + .menu,
:root:not([data-theme="dark"]) .site-header:not(.on-hero) .nav-links > .menu + a,
:root:not([data-theme="dark"]) .site-header:not(.on-hero) .nav-links > a + a,
:root:not([data-theme="dark"]) .site-header:not(.on-hero) .nav-links > a + .menu {
  border-left-color: rgba(255, 255, 255, .22);
}
/* the empty-trigger override has to lose to the header's own colour */
:root:not([data-theme="dark"]) .site-header:not(.on-hero) .menu-trigger.is-empty:hover {
  color: rgba(255, 255, 255, .88); background: none;
}

/* auth: the primary CTA inverts to white, which makes it the highest-contrast
   element on the bar — where a primary action belongs */
:root:not([data-theme="dark"]) .site-header:not(.on-hero) .nav-signin { color: #fff; }
:root:not([data-theme="dark"]) .site-header:not(.on-hero) .nav-signin:hover { background: rgba(255, 255, 255, .14); }
:root:not([data-theme="dark"]) .site-header:not(.on-hero) .nav-start {
  background: #fff; color: var(--brand);
}
:root:not([data-theme="dark"]) .site-header:not(.on-hero) .nav-start:hover {
  box-shadow: 0 8px 20px -8px rgba(0, 0, 0, .45);
}

/* icon buttons */
:root:not([data-theme="dark"]) .site-header:not(.on-hero) .burger {
  border-color: rgba(255, 255, 255, .3);
  background: rgba(255, 255, 255, .08);
  color: #fff;
}
:root:not([data-theme="dark"]) .site-header:not(.on-hero) .burger:hover {
  border-color: rgba(255, 255, 255, .76);
  background: rgba(255, 255, 255, .16);
}

/* ══ comparison table ══════════════════════════════════════════════════
   Laid out to the reference: centred heading and standfirst, right-aligned
   row labels over a lattice block, a bloom beneath it, light spilling below,
   a lattice strip in every column gutter, and our column as one solid block.

   The table carries explicit ARIA roles because the stacking breakpoint sets
   display:block on table elements, which silently strips their implicit roles
   — a screen reader would otherwise stop announcing "row, GitHub, No". */
.sec h2.cmp-h {
  /* .sec h2 is (0,1,1) and caps the measure at 18ch, which pins a centred line
     to the left — this has to out-specify it, not merely come later */
  margin: 0; max-width: none; text-align: center;
  font-family: var(--font-display); font-weight: 400;
  font-size: clamp(32px, 4.4vw, 64px); line-height: 1.12; letter-spacing: 0;
  color: var(--ink);
}
.cmp-sub {
  margin: 14px auto 0; max-width: 62ch; text-align: center;
  font-size: clamp(14px, 1.2vw, 16px); line-height: 1.6; color: var(--muted);
}

/* geometry shared by the table and the backdrop, so the strips land exactly in
   the gutters rather than being nudged into place by eye */
.cmp-stage {
  --lblw: 39%;                 /* label block */
  --colw: 15.25%;              /* each verdict column */
  position: relative; margin-top: clamp(38px, 4.5vw, 68px);
}
.cmp-fx { position: absolute; inset: -6px 0 -8px; pointer-events: none; overflow: hidden; z-index: 0; }
.cmp-fx i { position: absolute; display: block; }

/* bloom sits UNDER the lattice — in the reference the grid lines read over it */
.cf-bloom {
  top: 0; bottom: 0; left: 0; width: var(--lblw);
  background:
    radial-gradient(58% 44% at 62% 44%, rgba(120, 88, 196, .30) 0, transparent 70%),
    radial-gradient(46% 40% at 90% 88%, rgba(226, 120, 70, .30) 0, transparent 72%);
}
.cf-grid, .cf-strip {
  background-image:
    linear-gradient(to right, var(--cmp-line) 1px, transparent 1px),
    linear-gradient(to bottom, var(--cmp-line) 1px, transparent 1px);
  background-size: 26px 26px;
}
.cf-grid { top: 0; bottom: 0; left: 0; width: var(--lblw); }
/* light spilling out from under the block */
.cf-glow {
  left: 8%; width: 30%; height: 190px; bottom: -100px;
  background: radial-gradient(50% 50% at 50% 24%, rgba(255, 236, 224, .16) 0, transparent 70%);
  filter: blur(20px);
}
/* one strip per gutter, positioned off the same column arithmetic as the table */
.cf-strip {
  top: 0; bottom: 0; width: 30px;
  left: calc(var(--lblw) + var(--colw) * (var(--at) - 1));
  -webkit-mask-image: linear-gradient(to bottom, transparent, #000 7%, #000 92%, transparent);
  mask-image: linear-gradient(to bottom, transparent, #000 7%, #000 92%, transparent);
}
:root { --cmp-line: rgba(20, 19, 16, .16); }
:root[data-theme="dark"] { --cmp-line: rgba(255, 255, 255, .14); }

.cmp { position: relative; z-index: 1; width: 100%; table-layout: fixed; border-collapse: separate; border-spacing: 0; }
.cmp .ch-void { width: var(--lblw); }
.cmp thead th { width: var(--colw); }

/* ── column headers ───────────────────────────────────────────────────── */
.cmp thead th.ch { padding: 0 6px 22px; vertical-align: bottom; text-align: center; }
.ch-in { display: inline-flex; align-items: center; gap: 8px; }
.ch-logo { width: 22px; height: 22px; flex: none; }
.ch-logo svg { width: 100%; height: 100%; display: block; }
.lg-gh svg { fill: var(--ink); }
.lg-tu { color: var(--ink); }
.lg-us { color: var(--on-brand); }
.ch-t { font-size: 14px; font-weight: 600; letter-spacing: 0; color: var(--ink); white-space: nowrap; }
/* our column: header block, then the body block, as two pieces like the reference */
.cmp thead th.cmp-us {
  padding: 16px 6px; vertical-align: middle;
  background: var(--brand); border-radius: 4px;
}
.cmp thead th.cmp-us .ch-t { color: var(--on-brand); }
/* the mark sits on the brand block: white plate, brand letter — currentColor
   alone would paint it near-black on orange in the dark theme */
.cmp thead th.cmp-us .lg-us { color: #fff; }

/* ── rows ─────────────────────────────────────────────────────────────── */
.cmp tbody th {
  padding: 30px 30px 30px 0;
  font-size: clamp(13px, 1.15vw, 16px); font-weight: 400; line-height: 1.4;
  color: var(--ink); text-align: right; vertical-align: middle;   /* right-aligned, as the reference */
}
.cmp td { padding: 30px 8px; text-align: center; vertical-align: middle; }
.cmp td.cmp-us { background: var(--brand); }
/* Parts the header block from the body block. font-size/line-height are zeroed
   because an empty cell still generates a line box, which propped the row open
   to 30px regardless of the height asked for. */
.cmp tbody tr.cmp-gap td {
  padding: 0; height: 6px; background: none;
}
.cmp tbody tr:nth-child(2) td.cmp-us { border-radius: 4px 4px 0 0; }
.cmp tbody tr:last-child  td.cmp-us { border-radius: 0 0 4px 4px; }

/* only cells that carry a verdict get a disc — unscoped, this also generated a
   30px mark inside the spacer row and held it open regardless of its height */
.cmp td[data-v]::before {
  content: ''; display: inline-block; width: 30px; height: 30px;
  border-radius: 50%; vertical-align: middle;
  background-repeat: no-repeat; background-position: center; background-size: 14px 14px;
}
.cmp td[data-v="y"]::before {
  background-color: rgba(45, 138, 86, .17);
  background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 12 12"><path d="M2 6.3l2.7 2.7L10 3.4" fill="none" stroke="%233ecf8e" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round"/></svg>');
}
.cmp td[data-v="n"]::before {
  background-color: rgba(190, 55, 48, .17);
  background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 12 12"><path d="M3.3 3.3l5.4 5.4M8.7 3.3l-5.4 5.4" fill="none" stroke="%23f2635a" stroke-width="1.7" stroke-linecap="round"/></svg>');
}
/* our ticks: a light disc on the brand block, as in the reference */
.cmp td.cmp-us[data-v="y"]::before {
  background-color: rgba(255, 255, 255, .26);
  background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 12 12"><path d="M2 6.3l2.7 2.7L10 3.4" fill="none" stroke="%23ffffff" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round"/></svg>');
}

/* the label block gives ground before anything is allowed to crowd */
@media (max-width: 1080px) {
  .cmp-stage { --lblw: 34%; --colw: 22%; }
  .cmp thead th.ch { padding-bottom: 18px; }
  .ch-t { font-size: 13px; }
  .ch-logo { width: 19px; height: 19px; }
  .cmp tbody th, .cmp td { padding-top: 24px; padding-bottom: 24px; }
  .cmp tbody th { padding-right: 20px; }
}
@media (max-width: 880px) {
  .ch-in { flex-direction: column; gap: 6px; }
  .ch-t { font-size: 11px; white-space: normal; text-align: center; }
}

/* ── narrow: still one matrix, never split into per-row cards ───────────
   The table stays a table at every width. Below 640 the column names drop to
   their logos — the marks and the row labels are what carry the meaning, and
   the names cost ~90px each that the verdict columns need. Nothing scrolls
   sideways and nothing is cut off. */
@media (max-width: 880px) {
  .cmp-stage { --lblw: 40%; --colw: 15%; }
  .cmp tbody th { padding-right: 12px; }
  .cmp td { padding-inline: 4px; }
}
@media (max-width: 640px) {
  .cmp-stage { --lblw: 44%; --colw: 14%; }
  .cmp thead th.ch { padding: 0 2px 14px; }
  .ch-in { flex-direction: column; gap: 0; }
  .ch-t { display: none; }                 /* the logo identifies the column */
  .ch-logo { width: 22px; height: 22px; }
  .cmp thead th.cmp-us { padding: 12px 2px; }
  .cmp tbody th {
    padding: 16px 10px 16px 0; font-size: 12px; line-height: 1.4;
    overflow-wrap: anywhere;
  }
  .cmp td { padding: 16px 2px; }
  .cmp td[data-v]::before { width: 24px; height: 24px; background-size: 11px 11px; }
}
@media (max-width: 400px) {
  .cmp-stage { --lblw: 46%; --colw: 13.5%; }
  .ch-logo { width: 19px; height: 19px; }
  .cmp tbody th { font-size: 11px; padding-right: 8px; }
  .cmp td[data-v]::before { width: 21px; height: 21px; background-size: 10px 10px; }
}

/* ── closing the comparison: what each audience walks away with ─────────
   Reference layout — a standing headline on the left, two coloured cards to
   its right, each with a banded header and icon rows. The cards keep their own
   tint in both themes (they are objects on the page, not surfaces), so their
   ink is fixed dark rather than following --ink. Tints stay in the warm family
   instead of the reference's purple/yellow. */
.conf {
  display: grid; align-items: start;
  grid-template-columns: minmax(0, .84fr) minmax(0, 1fr) minmax(0, 1fr);
  gap: clamp(18px, 2.2vw, 32px);
  margin-top: clamp(48px, 6vw, 88px);
}
/* The two cards are wrapped so they can become one scroll track on a
   phone. Everywhere else the wrapper must not exist as far as the grid is
   concerned — display:contents hands its children straight to the grid,
   so the three-column and two-column layouts are untouched. */
.conf-pair { display: contents; }
.conf-lede { padding-top: 6px; }
.conf-lede h3 {
  margin: 0; font-family: var(--font-display); font-weight: 400;
  font-size: clamp(32px, 3.4vw, 52px); line-height: 1.12; letter-spacing: 0;
  color: var(--ink);
}
.conf-lede p { margin: 14px 0 0; font-size: 14px; line-height: 1.6; color: var(--muted); }

/* .conf sets align-items:start so the lede sits at the top of the row
   rather than being stretched down it. That applied to the cards too, so
   each one ended at its own last line and the research card — which runs
   a line longer in two of its three items — finished 43px below the
   other. Only the cards opt back into stretching; the lede stays put. */
.conf-card { --ink-c: #1a1310; border-radius: 4px; overflow: hidden; align-self: stretch; }
.cc-a { background: #f6dccb; }
.cc-a > header { background: #eec5a8; }
.cc-b { background: #f8e7bd; }
.cc-b > header { background: #f2d48f; }

.conf-card > header {
  padding: 18px 22px; text-align: center;
  display: flex; flex-direction: column; gap: 3px;
}
.conf-card > header b {
  font-size: clamp(14px, 1.4vw, 18px); font-weight: 700;
  letter-spacing: 0; color: var(--ink-c);
}
.conf-card > header span {
  font-size: 11px; font-weight: 600; letter-spacing: 0.12em;
  text-transform: uppercase; color: rgba(26, 19, 16, .74);   /* .62 gave 4.3:1 on the peach chip, just under AA */
}
.conf-card ul {
  list-style: none; margin: 0; padding: clamp(20px, 2vw, 26px) clamp(18px, 1.8vw, 24px);
  display: grid; gap: clamp(20px, 2vw, 26px);
}
.conf-card li { display: grid; grid-template-columns: auto 1fr; gap: 14px; align-items: start; }
.ci { width: 26px; height: 26px; flex: none; color: var(--ink-c); margin-top: 1px; }
.ci svg { width: 100%; height: 100%; display: block; }
.conf-card b {
  display: block; font-size: 14px; font-weight: 700; line-height: 1.4;
  letter-spacing: 0; color: var(--ink-c);
}
.conf-card li p {
  margin: 7px 0 0; font-size: 13px; line-height: 1.6;
  color: rgba(26, 19, 16, .74);
}

/* the headline steps above the pair before the cards get squeezed */
@media (max-width: 1000px) {
  .conf { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
  .conf-lede { grid-column: 1 / -1; padding-top: 0; }
  .conf-lede h3 { font-size: clamp(26px, 4.6vw, 40px); }
  .conf-lede br { display: none; }
}
@media (max-width: 640px) {
  .conf { grid-template-columns: minmax(0, 1fr); gap: 18px; }

  /* Side by side and swiped, rather than stacked. Two cards this tall put
     the second one entirely below the fold when they stack, and a reader
     who came for "whichever side of the work you are on" needs to see
     that there are two sides without scrolling past the first.

     Same track as the other two carousels on this page: snap to the
     card, no scrollbar, and out through the page gutter so a card can
     sit at the edge with its neighbour showing. 86% leaves that sliver
     of the second card visible, which is the only thing that tells
     anyone it swipes. */
  .conf-pair {
    display: flex; gap: 12px;
    margin-inline: calc(var(--gutter) * -1);
    padding-inline: var(--gutter);
    overflow-x: auto; overflow-y: hidden;
    scroll-snap-type: x mandatory;
    scroll-behavior: smooth;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
  }
  .conf-pair::-webkit-scrollbar { display: none; }

  .conf-card {
    flex: 0 0 86%;
    scroll-snap-align: center;
    margin-inline: 0;
    border-radius: 14px;
  }
}


/* the footer has room for the lockup at a readable size — the strapline under
   the wordmark is only legible from about 40px up */
.foot .brand-mark { width: 147px; height: 47px; }
@media (max-width: 1024px) { .brand-mark { width: 109px; height: 35px; } }
@media (max-width: 380px)  { .brand-mark { width: 97px; height: 31px; } }

/* ── dark mode: "Who we are" and "How we are different" sit flat ────────
   Both paint a --surface-2 wash (#1c1815) over the #0d0b0a body, which lifts
   them off the page in dark mode where every other section is flat. The wash
   stays in light mode, where it reads as a quiet alternation rather than a
   different colour. */
:root[data-theme="dark"] .about::before,
:root[data-theme="dark"] #compare::before { background: none; }



/* ── touch header: the burger sits hard against the right edge ──────────
   The push used to live on `.theme-btn { margin-left: auto }`. When the
   theme switcher was deleted, so was the only thing holding the row open —
   `display: none` and a removed element both contribute no margin, so the
   burger fell back against the logo and sat at x90 in a 375px bar.

   It carries the auto margin itself now. It is the last item in the row,
   which is where that margin belongs regardless. */
@media (max-width: 1280px) {
  .site-header .burger { margin-left: auto; }
}

/* A `@media (max-width:860px) { .deck { display:none } }` rule lived here.
   It was correct while the story flattened below 861px: story.js re-homed
   the six cards into the text stages and left .deck in the flow as an empty
   near-black 16/10 block. The sequence no longer flattens at any width, so
   the deck holds its cards again and the rule is gone. */

/* ── header UI: reference look, original bar animation ────────────────
   The look is the supplied reference — 56px bar, ghost rows, outline +
   solid button pair. The *movement* is the one this site had before: a
   floating bar that condenses as you scroll.

   At rest it sits inset from the viewport edges, with a gap above it, and
   spans up to 1600px. Past the scroll threshold it contracts to 1120px,
   rounds off further and lifts on a stronger shadow. Same `.scrolled`
   class nav.js has always toggled — no JS changed.

   One deliberate deviation from the reference: it constrains its bar to
   max-w-5xl (1024px) because its demo column is max-w-3xl. This page runs
   on a 1520px --shell and every left edge resolves from it, so the inner
   keeps --shell. A 1024px bar would sit the wordmark well right of the
   headline beneath it. */

:root { --hdr: 64px; --hdr-gap: 16px; --hdr-side: 16px; }

/* header-2's choreography: the bar begins flush against the top edge and
   *detaches* on scroll — dropping --hdr-gap away from the edge as it
   narrows. Previously it floated at both ends and only the width moved,
   which read as a resize rather than as lifting off. `top` is the sticky
   offset, so it only takes effect once the bar is actually stuck; at the
   top of the document the bar simply sits at y=0.

     at rest   top: 0        max-width: 1600px   radius 16
     scrolled  top: 16px     max-width: 1180px   radius 20 + glass + shadow */
.site-header {
  top: 0;
  width: calc(100% - var(--hdr-side) * 2);
  margin: 0 auto;
  max-width: 1600px;
  border-radius: 16px;
  border-bottom: 0;
  box-shadow: 0 6px 22px -12px rgba(var(--sh), .18);
  transition: max-width .32s var(--ease), top .32s var(--ease),
              border-radius .32s var(--ease), background .28s var(--ease),
              box-shadow .28s var(--ease), backdrop-filter .28s var(--ease);
}

/* the condense. 1120px was sized for the old three-entry nav; six entries
   plus the brand, the button pair and the theme toggle measure 1110px, and
   ten pixels of slack is not enough to survive a font-rendering difference.
   1180 keeps the movement obvious and the row on one line. */
.site-header.scrolled {
  top: var(--hdr-gap);
  /* the serif nav is wider than the grotesque it replaced: the row measures
     1237px at the top of the spacing clamps, so the condensed bar has to
     clear that plus its own 44px of padding */
  max-width: 1300px;
  border-radius: 20px;
  background: rgba(255, 255, 255, .95);
  box-shadow: 0 0 0 1px var(--line), 0 14px 40px -12px rgba(var(--sh), .16);
}
:root[data-theme="dark"] .site-header {
  background: var(--surface);
  box-shadow: 0 0 0 1px rgba(255, 255, 255, .05), 0 6px 22px -12px rgba(0, 0, 0, .6);
}
:root[data-theme="dark"] .site-header.scrolled {
  background: rgba(13, 11, 10, .95);
  box-shadow: 0 0 0 1px var(--line), 0 16px 44px -14px rgba(0, 0, 0, .8);
}

@supports (backdrop-filter: blur(2px)) {
  .site-header.scrolled {
    background: rgba(255, 255, 255, .62);
    backdrop-filter: blur(16px) saturate(180%);
    -webkit-backdrop-filter: blur(16px) saturate(180%);
  }
  :root[data-theme="dark"] .site-header.scrolled { background: rgba(13, 11, 10, .62); }
}

/* The bar is inset by --hdr-side, which would push the brand that far right
   of every other left edge on the page. Its padding gives the inset back.
   Past shell + 2*side the bar stops being edge-limited, its own centring
   absorbs the inset, and the compensation has to stop or it over-corrects. */
.hdr-inner { gap: clamp(20px, 2vw, 30px); padding: 0 calc(var(--gutter) - var(--hdr-side)); }
@media (min-width: 1520px) {
  :root { --hdr-side: min(16px, calc((100vw - var(--shell)) / 2)); }
  .hdr-inner { padding-inline: var(--gutter); }
}
/* condensed, the bar is its own container — the page rail no longer
   applies, and header-2 tightens the nav padding here (px-4 → md:px-2) */
.site-header.scrolled .hdr-inner { padding: 0 clamp(14px, 1.6vw, 22px); }

/* h-14 on touch, md:h-12 on desktop — the shorter bar is what makes the
   detached state read as a pill rather than a slab */
@media (min-width: 1281px) { :root { --hdr: 76px; } }
.hdr-inner { transition: height .32s var(--ease), padding .32s var(--ease); }

/* ── rows ───────────────────────────────────────────────────────────
   The reference has no rules between entries — items are ghost buttons
   that only show ground on hover. The dividers added from the earlier
   reference are dropped here. */
.nav-links { align-items: center; margin-left: 0; gap: 4px; }
.nav-links > .menu + .menu,
.nav-links > .menu + a,
.nav-links > a + a,
.nav-links > a + .menu { border-left: 0; }

.nav-links > a,
.menu-trigger {
  height: 40px; padding: 0 clamp(13px, 1.15vw, 18px); margin: 0; gap: 6px;
  border-radius: 9px;
  /* the nav labels are the header's voice, not its controls, so they take
     the serif — 400 because that is the only weight loaded; 500 here would
     have the browser synthesise a bold */
  font-family: var(--font-display);
  font-size: 16px; font-weight: 400; letter-spacing: 0;
  color: var(--muted);
}
.nav-pill { border-radius: 8px; background: var(--hover); }
.chev { width: 12px; height: 12px; margin-left: -1px; opacity: .6; }

/* the wordmark gets the same ghost treatment as a row */
.site-header .brand {
  padding: 6px 8px; border-radius: 8px;
  transition: background .18s var(--ease);
}
.site-header .brand:hover { background: var(--hover); }

/* ── controls ───────────────────────────────────────────────────────
   An outline button and a solid one, both rounded-md, matching the
   reference's Sign In / Get Started pair. */
.nav-auth { gap: 10px; flex: none; }
.nav-links { flex: none; }
.site-header .nav-signin {
  display: inline-flex; align-items: center; height: 40px; padding: 0 18px;
  white-space: nowrap;
  border: 1px solid var(--line-strong); border-radius: 8px;
  background: var(--surface); color: var(--ink);
  font-size: 14px; font-weight: 500;
  transition: background .18s var(--ease), border-color .18s var(--ease);
}
.site-header .nav-signin:hover { background: var(--hover); border-color: var(--line-strong); }

.site-header .nav-start {
  display: inline-flex; align-items: center; height: 40px; padding: 0 20px;
  white-space: nowrap;
  border-radius: 9px; font-size: 14px; font-weight: 500;
  box-shadow: none;
}
.site-header .nav-start:hover { transform: none; opacity: .9; box-shadow: none; }

.site-header .burger {
  height: 40px; border-radius: 9px;
  border: 1px solid var(--line-strong); background: var(--surface);
  color: var(--ink); box-shadow: none;
}
/* The burger keeps its "Menu" / "Close" label — it is not an icon-only
   control. Sizing it as a square (as the reference's icon Button does)
   collapses the label, which is the one thing that made the touch header
   readable. */
.site-header .burger { width: auto; gap: 10px; padding: 0 14px 0 16px; }
 .site-header .burger:hover { background: var(--hover); }

/* ── light mode: the orange bar ───────────────────────────────────────
   The reference's neutral, transparent bar is kept in dark mode, where an
   orange band would glare. In light mode the bar is orange again — solid at
   all times, because a saturated ground cannot also be transparent.

   The reference's scroll transition survives in a different form: at rest
   the orange is opaque, and once scrolled it goes very slightly
   translucent with the same blur, so page content ghosts through the bar
   as it passes under. Same .scrolled class, same JS.

   Not a flat fill: a shallow gradient across a bar this wide keeps a block
   of saturated colour from going dead in the middle. */
:root:not([data-theme="dark"]) .site-header {
  background:
    radial-gradient(120% 200% at 6% -60%, rgba(255, 255, 255, .15) 0%, rgba(255, 255, 255, 0) 52%),
    linear-gradient(102deg, #8f0914 0%, #ae0e20 44%, #c31428 100%);
  color: #fff;
  border-bottom-color: transparent;
}
/* Restated rather than left to .site-header.scrolled: the rule above is
   (0,3,0) and would outrank that (0,2,0) block. */
:root:not([data-theme="dark"]) .site-header.scrolled {
  color: #fff;
  border-bottom-color: rgba(255, 255, 255, .20);
  box-shadow: 0 10px 30px -14px rgba(140, 48, 10, .55);
}
@supports (backdrop-filter: blur(2px)) {
  :root:not([data-theme="dark"]) .site-header.scrolled {
    background:
      radial-gradient(120% 200% at 6% -60%, rgba(255, 255, 255, .15) 0%, rgba(255, 255, 255, 0) 52%),
      linear-gradient(102deg, rgba(143, 9, 20, .93) 0%, rgba(174, 14, 32, .93) 44%, rgba(195, 20, 40, .93) 100%);
  }
}

/* the lockup knocks out to white again — its own rust would disappear into
   this ground */
:root:not([data-theme="dark"]) .site-header .brand { color: #fff; }
:root:not([data-theme="dark"]) .site-header .brand-mark { --logo-ink: #fff; }

/* rows: .88 white rather than pure, so the hovered item still has somewhere
   brighter to go — and it clears 4.5:1 on this ground either way */
:root:not([data-theme="dark"]) .site-header .nav-links > a,
:root:not([data-theme="dark"]) .site-header .menu-trigger { color: rgba(255, 255, 255, .88); }
:root:not([data-theme="dark"]) .site-header .nav-links > a:hover,
:root:not([data-theme="dark"]) .site-header .menu-trigger:hover,
:root:not([data-theme="dark"]) .site-header .menu-trigger[aria-expanded="true"] {
  color: #fff; background: none;
}
:root:not([data-theme="dark"]) .site-header .nav-pill { background: rgba(255, 255, 255, .16); }

/* controls keep the reference's outline + solid pairing; on orange the
   solid one inverts to white, which makes it the brightest element on the
   bar — where a primary action belongs */
:root:not([data-theme="dark"]) .site-header .nav-signin {
  color: #fff;
  background: rgba(255, 255, 255, .10);
  border-color: rgba(255, 255, 255, .30);
}
:root:not([data-theme="dark"]) .site-header .nav-signin:hover {
  background: rgba(255, 255, 255, .18); border-color: rgba(255, 255, 255, .42);
}
:root:not([data-theme="dark"]) .site-header .nav-start {
  background: #fff; color: var(--brand); box-shadow: none;
}
:root:not([data-theme="dark"]) .site-header .nav-start:hover { opacity: 1; background: rgba(255, 255, 255, .92); }

:root:not([data-theme="dark"]) .site-header .burger {
  border-color: rgba(255, 255, 255, .28);
  background: rgba(255, 255, 255, .10);
  color: #fff;
  box-shadow: none;
}
:root:not([data-theme="dark"]) .site-header .burger:hover {
  border-color: rgba(255, 255, 255, .42); background: rgba(255, 255, 255, .18);
}

/* At drawer widths the bar goes flush: the mobile panel is anchored to
   --hdr and measures from the viewport top, so a floating bar would leave a
   gap above the panel. */
@media (max-width: 1280px) {
  :root { --hdr-gap: 0px; --hdr-side: 0px; }
  .site-header, .site-header.scrolled {
    width: 100%; max-width: none; border-radius: 0; margin: 0; top: 0;
  }
  .hdr-inner, .site-header.scrolled .hdr-inner { padding-inline: 16px; }
}

/* ── panel: feature layout ────────────────────────────────────────────
   Ported from the navigation-menu reference. A bordered region of cards on
   the left, a narrower column of bordered rows on the right.

   The reference's card sits on an SVG <pattern> of grid lines that shifts
   up on hover, over a conic-gradient glow. Both are rebuilt here without
   an asset: the grid is two repeating linear-gradients, the glow a conic
   in brand tones rather than the reference's pink/purple/blue, which would
   be the only out-of-theme colour on the site. */

.menu-panel.mega.layout-feature { width: min(940px, calc(100vw - 32px)); }

.mm-feature { display: grid; grid-template-columns: 1fr minmax(250px, .4fr); }
.mm-cards {
  list-style: none; margin: 0; padding: 18px;
  display: grid; grid-template-columns: repeat(3, 1fr); gap: 12px;
  border-right: 1px solid var(--line);
}
.mm-aside { list-style: none; margin: 0; padding: 18px; display: grid; gap: 8px; align-content: start; }

.menu-panel a.mm-gcard {
  position: relative; isolation: isolate; overflow: hidden;
  display: flex; flex-direction: column; gap: 12px; height: 100%;
  padding: 16px 18px 18px; text-decoration: none;
  border: 1px solid var(--line); border-radius: 10px;
  background: var(--surface);
  transition: border-color .16s var(--ease);
}
.menu-panel a.mm-gcard:hover,
.menu-panel a.mm-gcard:focus-visible { border-color: var(--brand-line); outline: none; }

.mm-gc-fx { position: absolute; inset: 0; z-index: -1; }
.mm-gc-fx::before {
  content: ''; position: absolute; inset: -25%;
  background-image:
    linear-gradient(to right, var(--line) 1px, transparent 1px),
    linear-gradient(to bottom, var(--line) 1px, transparent 1px);
  background-size: 30px 30px;
  transform: skewY(-12deg) translateY(8px);
  -webkit-mask-image: linear-gradient(225deg, #000, transparent);
  mask-image: linear-gradient(225deg, #000, transparent);
  transition: transform .15s ease-out;
}
.mm-gcard:hover .mm-gc-fx::before { transform: skewY(-12deg) translateY(0); }
.mm-gc-fx::after {
  content: ''; position: absolute; inset: -10%;
  background: conic-gradient(var(--brand) 0deg, var(--brand-2) 117deg,
              var(--brand) 180deg, var(--brand-2) 240deg, var(--brand) 360deg);
  filter: blur(50px); opacity: 0;
  transition: opacity .15s ease-out;
}
.mm-gcard:hover .mm-gc-fx::after { opacity: .1; }

.mm-gcard .mm-ic { position: relative; }
.mm-gc-t { position: relative; display: block; }
.mm-gc-t b { display: block; font-size: 14px; font-weight: 600; color: var(--ink); }
.mm-gc-t em {
  display: block; margin-top: 6px; font-style: normal;
  font-size: 12px; line-height: 1.4; color: var(--muted);
}

.menu-panel a.mm-large {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  padding: 11px 14px; text-decoration: none;
  border: 1px solid var(--line); border-radius: 9px;
  background: var(--surface);
  transition: border-color .16s var(--ease), background .16s var(--ease);
}
.menu-panel a.mm-large:hover,
.menu-panel a.mm-large:focus-visible {
  border-color: var(--brand-line); background: var(--surface-2); outline: none;
}
.mm-lg-t b { display: block; font-size: 13px; font-weight: 600; color: var(--ink); line-height: 1.25; }
.mm-lg-t em {
  display: -webkit-box; margin-top: 3px; font-style: normal;
  line-height: 1.4; color: var(--muted);
  /* two lines rather than one clipped one — these are sentences, and a
     single-line clamp was cutting them mid-word */
  -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.mm-large .mm-ic { flex: none; }
.mm-large:hover .mm-ic::after { background: var(--brand); }

/* NavSmallItem's arrow, sliding in on hover — applied to the compact rows
   the split layout already uses */
.menu-panel a.mm-compact::after {
  content: ''; margin-left: auto; width: 14px; height: 14px; flex: none;
  background: currentColor; opacity: 0; transform: translateX(-8px);
  -webkit-mask: var(--i-arrow) center / contain no-repeat;
  mask: var(--i-arrow) center / contain no-repeat;
  transition: opacity .18s var(--ease), transform .18s var(--ease);
}
.menu-panel a.mm-compact:hover::after,
.menu-panel a.mm-compact:focus-visible::after { opacity: .7; transform: translateX(0); }

@media (max-width: 1280px) {
  .menu-panel.mega.layout-feature { width: min(440px, calc(100vw - 32px)); }
  .mm-feature { grid-template-columns: 1fr; }
  .mm-cards { grid-template-columns: 1fr; border-right: 0; border-bottom: 1px solid var(--line); }
}

/* ── panel typography ─────────────────────────────────────────────────
   The dropdowns speak in the same system as the rest of the site: the
   grotesque for labels and interface, the serif for the descriptive line
   under a card title. Sizes lifted a little to match the taller bar. */
.menu-panel .mm-col a {
  font-family: var(--font-display);
  font-size: 16px; font-weight: 400; letter-spacing: 0; padding: 13px 0;
}
.mm-col h3 { font-size: 11px; letter-spacing: 0.12em; }

.mm-gc-t b { font-size: 14px; font-weight: 600; letter-spacing: 0; }
.mm-gc-t em {
  font-family: var(--font-display); font-style: normal;
  font-size: 13px; line-height: 1.4; letter-spacing: 0; color: var(--muted);
}
.mm-rt b { font-size: 14px; }
.mm-rt em {
  font-family: var(--font-display); font-size: 13px; line-height: 1.4; letter-spacing: 0;
}
.mm-lg-t b { font-size: 14px; }
.mm-lg-t em { font-family: var(--font-display); font-size: 13px; letter-spacing: 0; }
.menu-panel a.mm-compact { font-size: 14px; padding: 10px 12px; }

/* a lone column of cards fills the panel and needs no dividing border */
.mm-feature-solo { grid-template-columns: 1fr; }
.mm-feature-solo .mm-cards { border-right: 0; }

/* ── reading copy takes the serif ─────────────────────────────────────
   Anthropic's split: the serif carries anything you read in sentences,
   the grotesque carries anything you operate or scan. This is the rule
   that puts the whole body of the site on the system rather than only the
   headings — everything below the exception list is running copy.

   Weight stays 400 throughout: the serif is loaded at one weight, so any
   <strong> inside prose is handled by colour, not by a synthesised bold. */
p, .st-d, .tl, .scard p, .ab-note, .ab-ask, .cmp-sub, .foot-note,
.assure, .st-note, .rv p, .feat p, .ci + div p {
  font-family: var(--font-display);
  letter-spacing: 0;
}

/* exceptions — interface, micro type and anything already deliberate */
.dash p, .dash .st-d,
:is(#faq, #cfq) p, .fq-q, .fq-a, .fq-meta,
.panel-note, .msub-t, .ab-role, .foot-legal,
.stats span, .band span, .kicker, .ab-kicker,
.pill-note, .mm-col a, .sr-only {
  font-family: var(--font);
}
/* …but the panel description lines were put on the serif on purpose */
.mm-gc-t em, .mm-rt em, .mm-lg-t em { font-family: var(--font-display); }

/* prose emphasis: the serif has no bold to reach for, so <strong> earns
   its weight from colour instead of a smeared synthetic face */
p strong, .tl strong, .st-d strong {
  font-weight: 400; color: var(--ink);
}

/* ── weights the loaded faces cannot actually render ──────────────────
   Everything below was asking for a synthetic bold. Two kinds of fix:
   labels go back to the grotesque (they are interface, not reading copy),
   and prose emphasis drops to 400, where the serif takes colour instead. */

/* interface labels: grotesque, where a real bold exists */
.st-ix, .st-ix span, .ab-cta { font-family: var(--font); }

/* the sans is loaded 400–700; 800 was being synthesised */
.d-mark, .d-bell i { font-weight: 700; }

/* prose emphasis in the serif — colour, not weight */
p b, .tl b, .tl span, .st-d b,
.ab-ask, .hr-lead { font-weight: 400; }
p b, .tl b, .st-d b { color: var(--ink); }

/* ── the type roles, enforced ─────────────────────────────────────────
   A section-by-section audit found the same headings set at 40, 49, 52, 63
   and 64px, stat figures in the sans in one place and the serif in another,
   uppercase serif where a label belonged, and prose at weight 300 the face
   cannot render. Every text on the site is now exactly one of the roles
   below and nothing else.

     h1        serif 52/400  1.12  -0.01em
     h2        serif 40/400  1.12  -0.01em
     h3        serif 26/400  1.25
     title     sans  18/600  1.25       card and item headings
     figure    serif 40/400  1.12       stat numerals
     prose     serif 16/400  1.6
     ui        sans  14/500  1.4
     micro     sans  11/600  1.4  0.08em uppercase   every label

   This block sits last on purpose: it is the backstop that keeps a local
   rule from reintroducing a size that is not on the scale. */

/* headings — one size each, everywhere */
.hero h1 {
  font-size: clamp(40px, 4.4vw, 52px); line-height: 1.12; letter-spacing: -0.01em;
}
.sec h2, .sec h2.cmp-h, .story .st h2, .fq-head h2, .cta-band h2, .about h2 {
  font-family: var(--font-display); font-weight: 400;
  font-size: clamp(32px, 3.4vw, 40px); line-height: 1.12; letter-spacing: -0.01em;
}
.conf-lede h3, .feat h3, .grid6 .feat.feat-lead h3 {
  font-family: var(--font-display); font-weight: 400;
  font-size: clamp(21px, 2vw, 26px); line-height: 1.25; letter-spacing: 0;
}

/* card and item headings share one title role */
.scard h3, .conf-card header b, .feat-lead b {
  font-family: var(--font); font-weight: 600;
  font-size: 18px; line-height: 1.25; letter-spacing: 0;
}

/* stat figures: the band was in the sans while the hero was in the serif */
.band b, .stats b {
  font-family: var(--font-display); font-weight: 400;
  font-size: clamp(32px, 3vw, 40px); line-height: 1.12; letter-spacing: -0.01em;
}

/* labels are always the grotesque — an uppercase serif is not a role */
.hr-lead, .ab-big .tl span {
  font-family: var(--font); font-weight: 600;
  font-size: 11px; line-height: 1.4; letter-spacing: 0.08em; text-transform: uppercase;
}

/* prose never drops below the loaded weight */
.rv blockquote p, .rv p { font-weight: 400; }

/* the FAQ lede had a local rule keeping it in the grotesque */
.fq-head .lede, .fq-intro .lede { font-family: var(--font-display); letter-spacing: 0; }

/* ── about: the four content blocks ──────────────────────────────────
   Layout only — every piece of type in here is one of the twelve roles,
   assigned in the role layer below. */
.abx { margin-top: clamp(56px, 7vw, 96px); }
.abx .kicker { margin-bottom: 12px; }
.abx-t { margin: 0 0 clamp(22px, 2.6vw, 34px); max-width: 26ch; color: var(--ink); }

.abx-grid2 { display: grid; grid-template-columns: 1fr 1fr; gap: 18px; align-items: start; }
.abx-grid2 > div > p + p { margin-top: 14px; }
.abx-values, .abx-areas {
  display: grid; grid-template-columns: repeat(3, 1fr); gap: 18px;
  margin-top: 18px;
}

.abx-card {
  padding: 22px 24px; border: 1px solid var(--line); border-radius: 14px;
  background: var(--surface);
  transition: border-color .25s;
}
.abx-card:hover { border-color: var(--brand-line); }
.abx-card h3 { margin: 0 0 8px; color: var(--ink); }
.abx-card p { margin: 0; color: var(--muted); }
.abx-tall { background: var(--surface-2); }

.abx-chips { list-style: none; margin: 18px 0 0; padding: 0; display: flex; flex-wrap: wrap; gap: 10px; }
.abx-chips li {
  padding: 8px 16px; border: 1px solid var(--line); border-radius: 999px;
  background: var(--surface); color: var(--muted); white-space: nowrap;
}
.abx-lead { margin: clamp(26px, 3vw, 40px) 0 0; }
.abx-ticks { margin-top: 4px; align-self: center; }
.abx-ahead { max-width: 68ch; margin: clamp(26px, 3vw, 40px) 0 0; color: var(--muted); }

@media (max-width: 900px) {
  .abx-grid2, .abx-values, .abx-areas { grid-template-columns: 1fr; }
}
@media (min-width: 641px) and (max-width: 900px) {
  .abx-values, .abx-areas { grid-template-columns: 1fr 1fr; }
}

/* ── the split block ──────────────────────────────────────────────────
   Opt-in, set by the section builders. The heading leaves the top of the
   block and takes a rail on the left, holding position while the matter
   beside it scrolls past; the content then runs the rest of the width
   instead of down a centred measure.

   Blog posts and the legal pages use these same blocks and do not get this:
   they are read straight through, and a rail that pins a heading beside a
   contract clause helps nobody. */
@media (min-width: 1000px) {
  .abx-split { display: grid; grid-template-columns: minmax(0, 20rem) minmax(0, 1fr);
               gap: clamp(28px, 3.6vw, 64px); align-items: start; }
  .abx-split .abx-rail { position: sticky; top: 104px; }
  /* the heading no longer needs to clear the matter below it */
  .abx-split .abx-t { margin-bottom: 14px; }
  /* three cards in a column that is now ~60% of the shell would be thin */
  .abx-split .abx-areas, .abx-split .abx-values { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
.abx-rule {
  display: block; width: 54px; height: 3px; border-radius: 3px;
  background: linear-gradient(to right, var(--brand), rgba(174, 14, 32, 0));
}
@media (max-width: 999px) { .abx-split .abx-rule { margin-bottom: clamp(18px, 2.4vw, 26px); } }
.abx-b > :first-child { margin-top: 0; }

/* Blog posts run down a 720px column because that is a reading measure, not
   an accident. The rail needs its own 20rem beside that, so the column only
   widens where both fit — the matter still lands near 750px, and below
   1200px a post stays exactly as it was. */
@media (min-width: 1000px) and (max-width: 1199.98px) {
  .blp .abx-split { display: block; }
  .blp .abx-split .abx-rail { position: static; }
  .blp .abx-split .abx-rule { margin-bottom: clamp(18px, 2.4vw, 26px); }
}
@media (min-width: 1200px) {
  .blp .ba-body.ba-col { width: min(1040px, 100% - 2 * var(--gutter)); }
}

/* about page: the title block above the four anchored sections */
.abpage .abpage-h1 { margin: 10px 0 0; max-width: 22ch; }
.abpage > .wrap > .lede { margin-top: 16px; }
.abpage .abx[id] { scroll-margin-top: calc(var(--hdr) + var(--hdr-gap) + 24px); }

/* about hub + breadcrumb */
.abpage-crumb { display: flex; gap: 8px; align-items: center; margin: 0 0 18px; }
.abpage-crumb a { color: var(--faint); text-decoration: none; }
.abpage-crumb a:hover { color: var(--brand); text-decoration: underline; text-underline-offset: 3px; }
.abpage-crumb span[aria-current] { color: var(--ink); }
.abpage-crumb span[aria-hidden] { color: var(--faint); }
.abx-first { margin-top: clamp(34px, 4vw, 56px); }
.abx-hub { grid-template-columns: 1fr 1fr; }
.abx-link { position: relative; display: block; text-decoration: none; padding-right: 52px; }
.abx-go {
  position: absolute; right: 22px; top: 24px; color: var(--brand);
  opacity: 0; transform: translateX(-4px); transition: opacity .2s var(--ease), transform .2s var(--ease);
}
.abx-link:hover .abx-go { opacity: 1; transform: none; }
@media (max-width: 700px) { .abx-hub { grid-template-columns: 1fr; } }

/* about pages: back control, media banner, contact band */
.abpage-back {
  display: inline-flex; align-items: center; gap: 8px;
  margin: 0 0 18px; color: var(--faint); text-decoration: none;
  transition: color .2s;
}
.abpage-back svg { width: 15px; height: 15px; }
.abpage-back:hover { color: var(--brand); }

.abpage-media {
  margin: clamp(26px, 3.4vw, 44px) 0 0; border-radius: 20px; overflow: hidden;
  border: 1px solid var(--line); background: var(--surface-2);
  aspect-ratio: 16 / 6.5;
}
.abpage-media img, .abpage-media video {
  width: 100%; height: 100%; object-fit: cover; display: block;
}

/* ═══ THE CLOSING CALL TO ACTION ═════════════════════════════════════════
   One block, sitting on the footer, identical on every page — it used to
   be two: a "Start with a conversation" band on the landing page and a
   "Talk to us before you commit a semester" band everywhere else, each
   with its own copy, its own alignment and a pair of buttons.

   Built to the supplied reference: centred, a two-tone headline whose
   second clause is the display serif in the accent, one standfirst line,
   and a single dark control. The tinted wash behind it is gone with all
   the other bands.

   The button goes to /contact/#talk — a real form. The old pair pointed
   at "#" and a mailto. */
/* The six-question recap that closes every page, immediately above the
   call to action. It reuses .faq-list/.faq-item wholesale — the Help
   Centre's <details> styling — so there is exactly one FAQ look on the
   site and nothing here to keep in step with it. Only the band around it
   is new: the section padding, the heading rhythm, and the link out. */
/* Bottom padding is carried by default and only removed when the call to
   action follows and supplies its own. Written the other way round first —
   no bottom padding, on the assumption the CTA is always next — which left
   the questions sitting flush against the footer on /schedule-call/, the
   one page that closes with the FAQ alone. The safe direction is spacing
   that is present unless something else provides it. */
.abx-faq {
  position: relative;
  padding: clamp(44px, 5.2vw, 76px) 0 clamp(40px, 4.8vw, 68px);
}
.abx-faq:has(+ .abx-cta) { padding-bottom: 0; }
/* ── the closing questions, to the supplied reference ─────────────────
   A centred header — pill, two-tone headline, one standfirst — over
   SEPARATED cards, each with a circled plus on the right. The tag column
   and the chevron are gone: they competed with the question for the same
   line, and the question is the only thing anyone scans.

   Everything is scoped to .abx-faq. The Help Centre runs the same
   .faq-list/.faq-item markup as a dense, zebra-striped list with a tag
   filter, and that is right for a page whose whole job is the full set.
   Scoping is what lets the two look different on purpose rather than one
   quietly restyling the other. */
.abx-faq-head {
  text-align: center; max-width: 46rem;
  margin: 0 auto clamp(26px, 3vw, 40px);
}
.abx-faq-pill {
  display: inline-block; padding: 7px 17px; border-radius: 999px;
  background: var(--surface-2); border: 0;
  font-size: 11px; font-weight: 600; letter-spacing: 0.12em;
  text-transform: uppercase; color: var(--muted);
}
.abx-faq-h { margin: clamp(14px, 1.6vw, 20px) 0 0; color: var(--ink); text-wrap: balance; }
/* the reference leads with the first word in the display serif, not the
   last clause — it reads as a title rather than a sentence with a flourish */
.abx-faq-h em {
  font-family: var(--font-serif); font-style: italic; font-weight: 400;
  color: var(--brand);
}
.abx-faq-sub { margin: 12px 0 0; color: var(--muted); }

.abx-faq .faq-list {
  display: grid; gap: clamp(10px, 1.1vw, 14px);
  max-width: 62rem; margin-inline: auto;
}
/* No stroke anywhere. The cards are separated by their own ground and a
   soft shadow, and OPEN is said with colour rather than a red outline —
   a border round an expanded panel draws a box the eye has to read past
   to reach the answer inside it.

   The open tint runs light at the top to faintly recessed at the foot, so
   the panel settles into the page as it expands instead of sitting on it. */
.abx-faq .faq-item,
.abx-faq .faq-item:nth-child(odd) {   /* beats the shared zebra */
  background: var(--surface);
  border: 0;
  border-radius: clamp(14px, 1.5vw, 19px);
  box-shadow: 0 1px 2px rgba(var(--sh), .05), 0 12px 24px -20px rgba(var(--sh), .4);
  transition: background .25s var(--ease), box-shadow .25s var(--ease);
}
.abx-faq .faq-item:hover {
  box-shadow: 0 2px 4px rgba(var(--sh), .07), 0 18px 32px -22px rgba(var(--sh), .5);
}
.abx-faq .faq-item[open] {
  background: linear-gradient(180deg, var(--surface) 0%, var(--surface-2) 100%);
  box-shadow: 0 1px 2px rgba(var(--sh), .06), 0 16px 30px -22px rgba(var(--sh), .45);
}

/* A shadow does nothing on a near-black ground, so with the strokes gone
   the cards had 1.11:1 of separation from the page on the error pages —
   present, but barely. The panel is LIFTED rather than outlined: same
   borderless look, actually visible. */
:root[data-theme="dark"] .abx-faq .faq-item,
:root[data-theme="dark"] .abx-faq .faq-item:nth-child(odd) { background: #262625; }
:root[data-theme="dark"] .abx-faq .faq-item[open] {
  background: linear-gradient(180deg, #2b2b29 0%, #333331 100%);
}

.abx-faq .faq-item summary {
  display: flex; align-items: center; justify-content: space-between;
  gap: clamp(16px, 2vw, 28px);
  padding: clamp(19px, 2.1vw, 26px) clamp(20px, 2.3vw, 30px);
  cursor: pointer; list-style: none;
}
.abx-faq .faq-item summary::-webkit-details-marker { display: none; }
.abx-faq .faq-item summary::after { content: none; }   /* the shared chevron */
.abx-faq .faq-q { font-weight: 600; color: var(--ink); text-wrap: pretty; }
.abx-faq .faq-q { min-width: 0; }
/* The Help Centre lets its summary wrap under 620px so the tag can drop to
   a second line. There is no tag here, so wrapping only pushed the plus
   below the question and left it stranded on its own line. */
@media (max-width: 620px) {
  .abx-faq .faq-item summary { flex-wrap: nowrap; gap: 14px; }
}
.abx-faq .faq-item summary:focus-visible {
  outline: 2px solid var(--brand); outline-offset: -4px;
  border-radius: clamp(14px, 1.5vw, 19px);
}

/* A circled plus that loses its upright stroke on open, so the control
   states what it will do rather than which way it points. */
.abx-faq .faq-plus {
  position: relative; flex: none; width: 23px; height: 23px;
  border-radius: 50%; border: 1.5px solid var(--brand);
  transition: transform .3s var(--ease), background .2s var(--ease);
}
.abx-faq .faq-plus::before,
.abx-faq .faq-plus::after {
  content: ""; position: absolute; left: 5px; right: 5px; top: 50%;
  height: 1.5px; margin-top: -0.75px; background: var(--brand); border-radius: 1px;
  transition: transform .3s var(--ease), opacity .2s var(--ease);
}
.abx-faq .faq-plus::after { transform: rotate(90deg); }
.abx-faq .faq-item[open] .faq-plus { transform: rotate(180deg); }
.abx-faq .faq-item[open] .faq-plus::after { opacity: 0; transform: rotate(0deg); }
.abx-faq .faq-item:hover .faq-plus { background: var(--brand-soft); }

.abx-faq .faq-a {
  padding: 0 clamp(20px, 2.3vw, 30px) clamp(20px, 2.2vw, 27px);
}
.abx-faq .faq-a p { max-width: 68ch; }

@media (prefers-reduced-motion: reduce) {
  .abx-faq .faq-plus, .abx-faq .faq-plus::before, .abx-faq .faq-plus::after { transition: none; }
}

.abx-faq-more { margin: clamp(18px, 2vw, 26px) 0 0; }
.abx-faq-more a {
  display: inline-flex; align-items: center; gap: 8px;
  color: var(--muted); text-decoration: none;
  border-bottom: 1px solid var(--line-strong);
  padding-bottom: 2px;
  transition: color .25s var(--ease), border-color .25s var(--ease);
}
.abx-faq-more a::after { content: '→'; transition: transform .25s var(--ease); }
.abx-faq-more a:hover { color: var(--ink); border-color: var(--brand-line); }
.abx-faq-more a:hover::after { transform: translateX(3px); }
.abx-faq-more a:focus-visible { outline: 2px solid var(--brand); outline-offset: 3px; }

/* The pair reads as one closing movement, so the call to action loses the
   top half of its padding when it follows the questions. */
.abx-faq + .abx-cta { padding-top: clamp(30px, 3.4vw, 52px); }

/* /schedule-call/ is a centred, narrow page: its header caps at 48rem and
   the form is narrower still. The questions arrived with the wide,
   left-aligned column every other page uses, so they ran nearly twice the
   width of the form above them and started well to its left — the block
   read as belonging to a different page. Here it takes the page's own
   column and centring so the two line up. */
.sch-page ~ .abx-faq .wrap { max-width: 48rem; }
.sch-page ~ .abx-faq .faq-list { max-width: none; }
.sch-page ~ .abx-faq .kicker,
.sch-page ~ .abx-faq h2,
.sch-page ~ .abx-faq .abx-faq-more { text-align: center; }
.sch-page ~ .abx-faq .abx-faq-more a { justify-content: center; }

.abx-cta { position: relative; padding: clamp(48px, 6vw, 84px) 0; }

.close-cta {
  display: flex; flex-direction: column; align-items: center;
  text-align: center; gap: 14px;
  max-width: 56rem;
}
.close-cta h2 { margin: 0; color: var(--ink); text-wrap: balance; }
.close-cta h2 em {
  font-family: var(--font-serif); font-style: italic; font-weight: 400;
  color: var(--accent-a);
}
.close-cta .lede { margin: 0; max-width: 44rem; color: var(--muted); }

/* the control: a dark, slightly glossy slab. The gradient runs light at
   the top to dark at the foot, with a hairline inner highlight on the
   top edge — that is what gives it the moulded look in the reference
   rather than reading as a flat black rectangle. */
.cta-pill {
  display: inline-flex; align-items: center; justify-content: center;
  margin-top: clamp(10px, 1.4vw, 18px);
  padding: clamp(17px, 1.7vw, 22px) clamp(42px, 4.8vw, 68px);
  border: 1px solid #0b0b0b;
  border-radius: clamp(15px, 1.5vw, 19px);
  background: linear-gradient(180deg, #4c4c4c 0%, #2a2a2a 45%, #141414 100%);
  color: #fff;
  text-decoration: none;
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, .2),
    inset 0 -1px 0 rgba(0, 0, 0, .5),
    0 1px 2px rgba(0, 0, 0, .28),
    0 10px 26px -12px rgba(0, 0, 0, .5);
  transition: transform .2s var(--ease), box-shadow .25s var(--ease), filter .25s;
}
.cta-pill:hover {
  transform: translateY(-1px);
  filter: brightness(1.14);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, .24),
    inset 0 -1px 0 rgba(0, 0, 0, .5),
    0 2px 4px rgba(0, 0, 0, .28),
    0 16px 32px -14px rgba(0, 0, 0, .55);
}
.cta-pill:active { transform: translateY(0); filter: brightness(1); }
.cta-pill:focus-visible { outline: 2px solid var(--accent-a); outline-offset: 4px; }

@media (prefers-reduced-motion: reduce) { .cta-pill { transition: none; } }

/* services: numbered sequence + the cross-link rail at the foot of a page */
.svc-steps { list-style: none; margin: 0; padding: 0; counter-reset: svcstep; display: grid; }
.svc-steps li {
  counter-increment: svcstep; position: relative;
  display: flex; gap: 16px; padding: 0 0 20px;
}
.svc-steps li::before {
  content: counter(svcstep, decimal-leading-zero);
  flex: none; width: 32px; height: 32px; display: grid; place-items: center;
  border: 1px solid var(--brand-line); border-radius: 50%;
  background: var(--brand-soft); color: var(--brand);
  font-variant-numeric: tabular-nums;
}
/* the rail joining the steps, stopping short of the last */
.svc-steps li:not(:last-child)::after {
  content: ''; position: absolute; left: 16px; top: 32px; bottom: 4px;
  width: 1px; background: var(--line-strong);
}
.svc-steps li:last-child { padding-bottom: 0; }
.svc-steps p { margin: 0; padding-top: 5px; max-width: 62ch; color: var(--muted); }

.abx-note {
  margin-top: clamp(30px, 3.4vw, 44px); padding: 20px 22px;
  border: 1px solid var(--line); border-left: 2px solid var(--brand);
  border-radius: 4px 14px 14px 4px; background: var(--surface-2);
}
.abx-note p { margin: 0; color: var(--muted); }

/* ══ THE RAIL AT THE FOOT OF EVERY PAGE ═══════════════════════════════
   Where to go next, on 23 pages. It used to be bare text links in three
   columns under a hairline: nothing bounded them, so titles of different
   lengths read as a ragged block rather than as three choices.

   Now each one is a card with its own colour under a dark panel. The
   markup did not change — it is still a <nav>, a <ul> and an <a> per
   destination, with the name in <b> and the tag in <em> — so all 23
   pages take this without being rebuilt, and nothing here can drift out
   of step with the generator.

   NOT WEBGL. The design this follows runs a Warp shader per card off a
   React library. Six live WebGL contexts is a lot to ask of a features
   grid; eight at the foot of every page on the site is a different
   proposition, and Rules §7 names animation libraries for simple
   effects specifically. The flow is two layers of radial gradient
   turning against each other at different speeds and directions, which
   is compositor-only — no repaint, no main thread, no context. */
.svc-more { margin-top: clamp(48px, 6vw, 80px); padding-top: 30px; border-top: 1px solid var(--line); }
/* On most pages the rail sits inside .wrap and inherits the page rail.
   On the rest it is a direct child of <body> and has always run the
   full width of the window — which read as slightly loose when these
   were bare text links, and reads as cards butting into the edge of
   the screen now. This gives those the same rail .wrap would have, and
   does not match when one is already there, so nothing double-pads. */
body > .svc-more { max-width: var(--shell); margin-inline: auto; padding-inline: var(--gutter); }
.svc-more ul {
  list-style: none; margin: 16px 0 0; padding: 0;
  display: grid; grid-template-columns: repeat(3, 1fr); gap: clamp(12px, 1.3vw, 18px);
}

/* the card: the colour lives here, the panel sits on top of it */
.svc-more li {
  position: relative; border-radius: 18px; overflow: hidden; isolation: isolate;
  background: hsl(var(--sm-h, 300) 62% 15%);
}
/* two gradient layers, turning opposite ways at speeds that do not
   divide into each other, so the pattern never visibly repeats */
.svc-more li::before,
.svc-more li::after {
  content: ""; position: absolute; inset: -45%; z-index: 0; pointer-events: none;
}
.svc-more li::before {
  background:
    radial-gradient(42% 52% at 24% 30%, hsl(var(--sm-h, 300) 100% 64% / .72), transparent 70%),
    radial-gradient(46% 58% at 76% 36%, hsl(calc(var(--sm-h, 300) + 38) 100% 50% / .66), transparent 72%);
}
.svc-more li::after {
  background:
    radial-gradient(54% 62% at 52% 92%, hsl(calc(var(--sm-h, 300) - 28) 96% 44% / .74), transparent 74%),
    radial-gradient(38% 46% at 12% 80%, hsl(calc(var(--sm-h, 300) + 16) 92% 58% / .50), transparent 70%);
}
@media (prefers-reduced-motion: no-preference) {
  .svc-more li::before { animation: sm-turn 34s linear infinite; }
  .svc-more li::after  { animation: sm-turn 47s linear infinite reverse; }
}
@keyframes sm-turn { to { transform: rotate(1turn); } }
/* The rail lives at the foot of the page, so for most of a visit these
   are sixteen composited layers turning where nobody can see them.
   Chrome often throttles that on its own; this does not rely on it. */
.svc-more.is-idle li::before,
.svc-more.is-idle li::after { animation-play-state: paused; }

/* the panel. The reference uses a flat 80% black with a white hairline
   and no blur; a backdrop-filter here would be eight live blurs at the
   foot of every page to soften a gradient that is already behind glass. */
.svc-more a {
  position: relative; z-index: 1; height: 100%;
  display: flex; flex-direction: column;
  min-height: 148px; padding: 22px; border-radius: inherit;
  background: rgba(8, 8, 10, .70); border: 1px solid rgba(255, 255, 255, .16);
  text-decoration: none;
  transition: background .2s var(--ease), border-color .2s var(--ease);
}
.svc-more a:hover { background: rgba(8, 8, 10, .54); border-color: rgba(255, 255, 255, .32); }
.svc-more a:focus-visible { outline: 2px solid #FFFFFF; outline-offset: 2px; }
.svc-more b { display: block; color: #FFFFFF; }
.svc-more em { display: block; margin-top: 6px; font-style: normal; color: rgba(255, 255, 255, .72); }

/* the chevron, drawn from two borders rather than carried as an asset.
   margin-top:auto puts it on the floor of the card whatever the tag
   above it wraps to, which is what keeps a row of them on one line. */
.svc-more a::after {
  content: ""; align-self: flex-end; margin-top: auto;
  width: 9px; height: 9px; margin-right: 3px;
  border: 2px solid rgba(255, 255, 255, .55); border-width: 2px 2px 0 0;
  transform: rotate(45deg);
  transition: border-color .2s var(--ease), transform .2s var(--ease);
}
.svc-more a:hover::after { border-color: #FFFFFF; transform: rotate(45deg) translate(2px, -2px); }

/* one hue per card, cycling. The reference rotates six; so does this. */
.svc-more li:nth-child(6n + 1) { --sm-h: 300; }
.svc-more li:nth-child(6n + 2) { --sm-h: 185; }
.svc-more li:nth-child(6n + 3) { --sm-h: 128; }
.svc-more li:nth-child(6n + 4) { --sm-h:  42; }
.svc-more li:nth-child(6n + 5) { --sm-h: 262; }
.svc-more li:nth-child(6n + 6) { --sm-h: 340; }

/* the one page on a white ground. Same six hues, same cards, but the
   panel is inverted: a black card at the foot of a white page reads as
   a hole in it. The colour is dialled back to a wash rather than
   restated, so the two variants cannot drift apart. */
:root[data-skin="lab"][data-lab="light"] .svc-more li { background: hsl(var(--sm-h, 300) 64% 96%); }
:root[data-skin="lab"][data-lab="light"] .svc-more li::before,
:root[data-skin="lab"][data-lab="light"] .svc-more li::after { opacity: .48; }
:root[data-skin="lab"][data-lab="light"] .svc-more a {
  background: rgba(255, 255, 255, .80); border-color: rgba(26, 19, 16, .12);
}
:root[data-skin="lab"][data-lab="light"] .svc-more a:hover {
  background: rgba(255, 255, 255, .64); border-color: rgba(26, 19, 16, .24);
}
:root[data-skin="lab"][data-lab="light"] .svc-more b  { color: var(--lb-ink); }
:root[data-skin="lab"][data-lab="light"] .svc-more em { color: var(--lb-w52); }
:root[data-skin="lab"][data-lab="light"] .svc-more a::after { border-color: rgba(26, 19, 16, .42); }
:root[data-skin="lab"][data-lab="light"] .svc-more a:hover::after { border-color: var(--lb-ink); }
:root[data-skin="lab"][data-lab="light"] .svc-more a:focus-visible { outline-color: var(--lb-ink); }

@media (max-width: 900px) { .svc-more ul { grid-template-columns: 1fr 1fr; } }
@media (max-width: 640px) {
  .svc-more ul { grid-template-columns: 1fr; }
  .svc-more a { min-height: 0; }
}

/* catalogue rows, and the honest empty state a catalogue shows while its
   entries are still being written */
.cat { list-style: none; margin: 0; padding: 0; display: grid; gap: 2px; }
.cat-row {
  display: grid; grid-template-columns: 1fr auto; gap: 4px 18px;
  padding: 14px 16px; margin: 0 -16px; border-radius: 12px;
  align-items: baseline;
}
.cat-row:nth-child(odd) { background: var(--surface-2); }
.cat-row b { color: var(--ink); }
.cat-row .cat-meta { color: var(--brand); white-space: nowrap; }
.cat-row p { grid-column: 1 / -1; margin: 0; color: var(--muted); }
.cat-empty {
  margin: 0; padding: 18px 20px;
  border: 1px dashed var(--line-strong); border-radius: 14px;
  background: var(--surface-2); color: var(--muted); max-width: 68ch;
}
@media (max-width: 560px) {
  .cat-row { grid-template-columns: 1fr; }
  .cat-row .cat-meta { white-space: normal; }
}

/* ── project titles: filter + rows ───────────────────────────────────
   Every row ships visible in the HTML; titles.js only hides non-matches,
   so the list is complete for a crawler and for anyone without scripting. */
.ttl-filters, .ttl-levels { display: flex; flex-wrap: wrap; gap: 8px; margin: 18px 0 0; }
.ttl-levels { margin-top: 10px; }

.ttl-chip, .ttl-lvl {
  display: inline-flex; align-items: center; gap: 8px;
  padding: 8px 14px; border-radius: 999px; cursor: pointer;
  border: 1px solid var(--line); background: var(--surface); color: var(--muted);
  transition: border-color .16s var(--ease), background .16s var(--ease), color .16s var(--ease);
}
.ttl-chip span {
  padding: 1px 7px; border-radius: 999px;
  background: var(--surface-2); color: var(--faint);
}
.ttl-chip:hover, .ttl-lvl:hover { border-color: var(--brand-line); color: var(--ink); }
.ttl-chip.is-on, .ttl-lvl.is-on {
  border-color: var(--brand); background: var(--brand); color: var(--on-brand);
}
.ttl-chip.is-on span { background: rgba(255,255,255,.22); color: var(--on-brand); }

.ttl-count { margin: 18px 0 0; color: var(--faint); }

.ttl-list { list-style: none; margin: 10px 0 0; padding: 0; display: grid; gap: 2px; }
.ttl-row {
  display: grid; grid-template-columns: 1fr auto; gap: 4px 20px;
  align-items: baseline; padding: 16px 18px; margin: 0 -18px; border-radius: 12px;
}
.ttl-row:nth-child(odd) { background: var(--surface-2); }
.ttl-row b { color: var(--ink); }
.ttl-row .ttl-meta { color: var(--brand); white-space: nowrap; }
.ttl-row p { grid-column: 1 / -1; margin: 0; color: var(--muted); max-width: 78ch; }
.ttl-row[hidden] { display: none; }

.ttl-none {
  margin: 16px 0 0; padding: 18px 20px;
  border: 1px dashed var(--line-strong); border-radius: 14px;
  background: var(--surface-2); color: var(--muted);
}

@media (max-width: 640px) {
  .ttl-row { grid-template-columns: 1fr; }
  .ttl-row .ttl-meta { white-space: normal; }
}

/* ── help centre: <details> questions ─────────────────────────────────
   Open/close needs no script; faq-filter.js only narrows by tag. */
.faq-list { display: grid; gap: 2px; margin-top: 10px; }
.faq-item { border-radius: 12px; }
.faq-item[hidden] { display: none; }
.faq-item:nth-child(odd) { background: var(--surface-2); }
.faq-item summary {
  display: flex; align-items: baseline; justify-content: space-between; gap: 20px;
  padding: 16px 18px; cursor: pointer; list-style: none;
}
.faq-item summary::-webkit-details-marker { display: none; }
.faq-item summary::after {
  content: ''; flex: none; width: 9px; height: 9px; margin-left: auto;
  border-right: 1.6px solid var(--brand); border-bottom: 1.6px solid var(--brand);
  transform: rotate(45deg) translateY(-2px);
  transition: transform .2s var(--ease);
}
.faq-item[open] summary::after { transform: rotate(225deg) translateY(-2px); }
.faq-q { color: var(--ink); }
.faq-tag { color: var(--brand); white-space: nowrap; margin-left: auto; }
.faq-a { padding: 0 18px 18px; }
.faq-a p { margin: 0; color: var(--muted); max-width: 78ch; }
.faq-a p + p { margin-top: 10px; }

/* the landing page's link through to the full set */
.fq-all { margin: clamp(22px, 2.4vw, 32px) 0 0; }
.fq-all a {
  display: inline-flex; align-items: center; gap: 8px;
  color: var(--brand); text-decoration: none;
}
.fq-all a span { transition: transform .18s var(--ease); }
.fq-all a:hover span { transform: translateX(3px); }

@media (max-width: 640px) {
  .faq-item summary { flex-wrap: wrap; gap: 6px 12px; }
  .faq-tag { white-space: normal; margin-left: 0; }
}

/* ── menu panel: ambient layer ────────────────────────────────────────
   Three stacked effects behind the panel content, all below it on z and
   all pointer-transparent:

     1  a slow-drifting aurora in brand tones — the ambient motion
     2  a fine grid, so the drift has something to move against
     3  a spotlight tracking the pointer, driven by --mx/--my from nav.js

   Every one is switched off under prefers-reduced-motion; the panel keeps
   its ground and border, so nothing depends on the animation running. */

.menu-panel.mega { position: absolute; isolation: isolate; }
.menu-panel.mega::before,
.menu-panel.mega::after {
  content: ''; position: absolute; inset: 0; z-index: -1;
  pointer-events: none; border-radius: inherit;
}

/* 1 + 2 — the aurora, over a grid */
.menu-panel.mega::before {
  background-image:
    radial-gradient(40% 64% at 16% -4%, color-mix(in oklab, var(--brand) 42%, transparent) 0%, transparent 70%),
    radial-gradient(34% 58% at 84% 10%, color-mix(in oklab, var(--brand-2) 38%, transparent) 0%, transparent 72%),
    radial-gradient(48% 74% at 52% 112%, color-mix(in oklab, var(--brand) 30%, transparent) 0%, transparent 70%),
    linear-gradient(to right, var(--line-soft) 1px, transparent 1px),
    linear-gradient(to bottom, var(--line-soft) 1px, transparent 1px);
  background-size: 140% 140%, 140% 140%, 140% 140%, 28px 28px, 28px 28px;
  opacity: .62;
  animation: mm-drift 18s ease-in-out infinite alternate;
}
@keyframes mm-drift {
  0%   { background-position: 0% 0%,    100% 0%,  50% 100%, 0 0, 0 0; }
  50%  { background-position: 34% 26%,  62% 34%,  26% 74%,  0 0, 0 0; }
  100% { background-position: 12% 40%, 100% 10%,  74% 92%,  0 0, 0 0; }
}

/* 3 — the spotlight */
.menu-panel.mega::after {
  background: radial-gradient(46% 62% at var(--mx, 50%) var(--my, 0%),
              color-mix(in oklab, var(--brand) 30%, transparent) 0%, transparent 68%);
  opacity: 0;
  transition: opacity .3s var(--ease);
}
.menu-panel.mega.open::after { opacity: 1; }

/* the panel's own arrival: a touch of lift and blur clearing */
/* transform is animated from nav.js so the slide-between-triggers and the
   arrival lift are one motion; a transition here would fight it */
.menu-panel.mega {
  transform: translateY(-8px) scale(.985);
  filter: blur(2px);
  transition: opacity .26s var(--ease), transform .3s var(--ease),
              filter .26s var(--ease);
}
.menu-panel.mega.open { transform: none; filter: none; }
.menu-panel.mega.is-morphing { transition: opacity .26s var(--ease), filter .26s var(--ease); }

/* the sliding trigger pill gains a soft brand halo */
.nav-pill {
  box-shadow: 0 0 0 1px color-mix(in oklab, var(--brand) 22%, transparent),
              0 6px 18px -8px color-mix(in oklab, var(--brand) 55%, transparent);
}
:root:not([data-theme="dark"]) .site-header .nav-pill {
  box-shadow: 0 0 0 1px rgba(255, 255, 255, .30),
              0 6px 18px -8px rgba(0, 0, 0, .35);
}

/* the columns' dividing rules fade in with the panel rather than snapping */
.mm-cards, .mm-feature, .mm-col + .mm-col { transition: border-color .3s var(--ease); }

@media (prefers-reduced-motion: reduce) {
  .menu-panel.mega { transform: none; filter: none; transition: opacity .01s; }
  .menu-panel.mega::before { animation: none; }
  .menu-panel.mega::after { display: none; }
}


/* The cards sat on opaque --surface, which covered the ambient layer
   entirely. Translucent instead, so the aurora and the pointer spotlight
   read through them — with a blur so text stays crisp over the movement. */
.menu-panel.mega a.mm-gcard,
.menu-panel.mega a.mm-large {
  background: color-mix(in oklab, var(--surface) 74%, transparent);
  -webkit-backdrop-filter: blur(6px);
  backdrop-filter: blur(6px);
}
.menu-panel.mega a.mm-gcard:hover,
.menu-panel.mega a.mm-large:hover {
  background: color-mix(in oklab, var(--surface) 88%, transparent);
}
/* the column card in the split layout, same reasoning */
.menu-panel.mega .mm-card { background: color-mix(in oklab, var(--surface-2) 70%, transparent); }

/* Every text row needs a plate. Measured: --muted on the bare aurora at its
   hotspot is 3.78:1, under the 4.5 floor; over a 74% surface plate it is
   5.08:1. The `grid` and `split` layouts had rows sitting straight on the
   panel, so they get the same treatment the feature cards already had —
   which also makes all four layouts consistent. */
.menu-panel.mega a.mm-rich,
.menu-panel.mega a.mm-compact,
.menu-panel.mega .mm-foot,
.menu-panel.mega .panel-note {
  background: color-mix(in oklab, var(--surface) 78%, transparent);
  -webkit-backdrop-filter: blur(6px);
  backdrop-filter: blur(6px);
  border-radius: 11px;
}
.menu-panel.mega a.mm-rich:hover,
.menu-panel.mega a.mm-compact:hover {
  background: color-mix(in oklab, var(--surface) 92%, transparent);
}
/* the footer strip spans the panel, so it squares off against the edges */
.menu-panel.mega .mm-foot { border-radius: 0; }
.menu-panel.mega .panel-note { border-radius: 0; }

/* and the panel's own ground gives the aurora more to tint */
.menu-panel.mega { background: color-mix(in oklab, var(--surface) 88%, transparent); }
@supports (backdrop-filter: blur(2px)) {
  .menu-panel.mega {
    background: color-mix(in oklab, var(--surface) 78%, transparent);
    -webkit-backdrop-filter: blur(20px) saturate(150%);
    backdrop-filter: blur(20px) saturate(150%);
  }
}

/* ── the menu indicator ──────────────────────────────────────────────
   NavigationMenuIndicator: a rotated square on the panel's top edge,
   pointing at the open trigger. It slides between triggers rather than
   reappearing, which is what ties the panel to what opened it. */
.nav-ind {
  position: absolute; top: 100%; left: 0; z-index: 41;
  width: 0; height: 0; opacity: 0; pointer-events: none;
  transition: transform .32s var(--ease), opacity .2s var(--ease);
}
.nav-ind i {
  position: absolute; left: -5px; top: 4px;
  width: 10px; height: 10px; border-radius: 2px 0 0 0;
  background: color-mix(in oklab, var(--surface) 88%, transparent);
  border-left: 1px solid var(--line); border-top: 1px solid var(--line);
  transform: rotate(45deg);
}
:root:not([data-theme="dark"]) .site-header .nav-ind i {
  border-color: color-mix(in oklab, var(--brand) 30%, transparent);
}
@media (prefers-reduced-motion: reduce) { .nav-ind { transition: opacity .01s; } }

/* ── drawer rows: icon tile, title, description ──────────────────────
   NavItemMobile ported — the drawer was plain labels, which gave a touch
   visitor less than the desktop panel offers. */
.msub-l a {
  display: flex; align-items: center; gap: 12px;
  padding: 10px 2px;
}
.mi-tile {
  flex: none; display: grid; place-items: center;
  width: 40px; height: 40px; border-radius: 11px;
  border: 1px solid var(--line); background: var(--surface-2);
}
.mi-tx { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.mi-tx b { color: var(--ink); }
.mi-tx em {
  font-style: normal; color: var(--muted);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

/* ── icon tiles invert on hover ──────────────────────────────────────
   From the mega-menu reference: the bordered square fills with the accent
   and the glyph knocks out, rather than merely tinting. */
.menu-panel.mega a:hover .mm-ic,
.msub-l a:hover .mi-tile {
  background: var(--brand); border-color: var(--brand);
}
.menu-panel.mega a:hover .mm-ic::after { background: var(--on-brand); }
.msub-l a:hover .mi-tile .mm-ic { background: none; border: 0; }
.msub-l a:hover .mi-tile .mm-ic::after { background: var(--on-brand); }
.mm-ic, .mi-tile { transition: background .18s var(--ease), border-color .18s var(--ease); }
.mm-ic::after { transition: background .18s var(--ease); }

/* ── error dialog + error pages ───────────────────────────────────────
   Ported from the error-alert-dialog reference onto the native <dialog>
   element — ESC, focus containment and the backdrop come from the
   platform. The same card, as a static <main>, is the body of the /errors/
   pages (generated by build-errors.mjs) and of 404.html.

   Reference mapping: gray-900 panel → surface tokens both themes; rose
   accent → the error pair below; font-mono code box → meta role (the site
   carries two families, and a third is not negotiable).

   Card anatomy — one strict text column: the icon sits in its own 20px
   gutter; kicker, heading, description, code strip and actions all share
   the second column's left edge. Actions are left-aligned to match it. */

:root { --err: #c0392b; --err-soft: rgba(192, 57, 43, .1); }
:root[data-theme="dark"] { --err: #e8705f; --err-soft: rgba(232, 112, 95, .14); }

.dlg {
  width: min(480px, calc(100vw - 40px));
  padding: 24px; border: 1px solid var(--line); border-radius: 16px;
  background: var(--surface); color: var(--ink);
  display: grid; grid-template-columns: 20px 1fr; column-gap: 12px;
  align-items: start;
  box-shadow: 0 24px 60px rgba(var(--sh), .2);
}
.dlg::backdrop { background: rgba(0, 0, 0, .55); }

@media (prefers-reduced-motion: no-preference) {
  dialog.dlg[open] { animation: dlg-in .22s var(--ease); }
  dialog.dlg[open]::backdrop { animation: dlg-fade .22s ease; }
  @keyframes dlg-in { from { opacity: 0; transform: translateY(6px) scale(.97); } }
  @keyframes dlg-fade { from { opacity: 0; } }
}

.dlg > * { grid-column: 2; margin: 0; }
.dlg-ic {
  grid-column: 1; grid-row: 1;
  width: 20px; height: 20px; margin-top: 1px; color: var(--err);
}
.errp .dlg-ic { grid-row: 2; }   /* error pages carry a kicker in row 1 */

.dlg-kick { color: var(--err); margin-bottom: 8px; }
.dlg :is(h1, h2) { color: var(--ink); margin-bottom: 10px; }
.dlg-desc { color: var(--muted); max-width: 48ch; margin-bottom: 14px; }

.dlg-code {
  padding: 12px 14px; margin-bottom: 18px;
  border: 1px solid var(--line); border-left: 2px solid var(--err);
  border-radius: 4px 10px 10px 4px; background: var(--err-soft);
  color: var(--muted);
}

.dlg-foot { display: flex; justify-content: flex-start; gap: 10px; flex-wrap: wrap; }
.dlg-foot :is(.btn, .btn-danger) { cursor: pointer; font: inherit; text-decoration: none; }
.dlg-foot .btn { padding: 9px 18px; }
.btn-danger {
  display: inline-block; padding: 9px 18px; border-radius: 999px;
  background: var(--err); color: #fff;
  transition: opacity .2s;
}
.btn-danger:hover { opacity: .88; }

.dlg-links { display: flex; flex-wrap: wrap; gap: 6px 14px; margin-top: 16px; padding: 0; list-style: none; }
.dlg-links a { color: var(--muted); text-decoration: underline; text-underline-offset: 3px; }
.dlg-links a:hover { color: var(--ink); }

@media (max-width: 480px) {
  .dlg-foot { flex-direction: column; }
  .dlg-foot .btn, .dlg-foot .btn-danger { width: 100%; text-align: center; }
}

/* the page shell around the card: brand top-left, card centred, legal
   line at the foot. Dark only — the template hard-codes data-theme="dark"
   and ships no theme script, per the corporate error standard. */
.errp { min-height: 100svh; display: grid; grid-template-rows: auto 1fr auto; background: var(--bg); }
.errp .errp-top { padding: 18px 24px; }
.errp .errp-mid { display: grid; place-items: center; padding: 32px 20px; }
.errp .errp-end { padding: 18px 24px; color: var(--muted); }
.errp .errp-end a { color: inherit; }

/* the open error hero — ported from the not-found reference: a giant
   status-code watermark behind centred text, actions below. The numeral
   is decorative artwork (aria-hidden), like the reference's SVG — the
   twelve roles govern all real copy on the page. The reference's search
   bar was dropped: the site has no search to wire it to, and a dead
   input is worse than none. */
.err-hero {
  position: relative; text-align: center; width: min(760px, 100%);
  padding-top: clamp(110px, 20vh, 190px);
  /* The status numeral behind the text is sized in vw, and vw counts the
     scrollbar — so at very narrow widths with a classic scrollbar it grew
     wider than the column and pushed the document sideways. Clipping here
     costs nothing (it is 5%-opacity decoration) and stops a background
     flourish from ever driving the page width. */
  overflow-x: clip;
}
.err-mark {
  position: absolute; top: 0; left: 50%; transform: translateX(-50%);
  font-family: var(--font-display); font-weight: 400; line-height: .9;
  font-size: clamp(170px, 30vw, 340px); letter-spacing: -0.02em;
  color: var(--ink); opacity: .05;
  pointer-events: none; user-select: none;
}
.err-hero > * { position: relative; }
.err-kick { color: var(--err); margin: 0 0 16px; }
.err-title { color: var(--ink); margin: 0; text-wrap: balance; }
.err-desc { color: var(--muted); margin: 18px auto 0; max-width: 52ch; text-wrap: pretty; }
.err-actions { display: flex; flex-wrap: wrap; gap: 12px; justify-content: center;
               align-items: center; margin-top: 36px; }
.err-actions :is(.btn, .btn-danger) { cursor: pointer; font: inherit; text-decoration: none; }
.err-actions .btn { padding: 12px 24px; }
.err-actions .arr { display: inline-block; margin-right: 8px; opacity: .6;
                    transition: transform .2s var(--ease); }
.err-actions .btn:hover .arr { transform: translateX(-2px); }
.err-links { display: flex; flex-wrap: wrap; gap: 6px 16px; justify-content: center;
             margin: 28px 0 0; padding: 0; list-style: none; }
.err-links a { color: var(--muted); text-decoration: underline; text-underline-offset: 3px; }
.err-links a:hover { color: var(--ink); }
@media (max-width: 560px) {
  .err-actions { flex-direction: column; }
  .err-actions > * { width: 100%; text-align: center; }
}

/* the /errors/ gallery: one boxed row per status code */
.errp-wide { width: min(1120px, 100%); }
.errp-wide .dlg { width: 100%; }
.errx { display: grid; gap: 10px; padding: 0; margin: 24px 0 0; list-style: none;
        grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); }
.errx a {
  display: block; padding: 14px 16px; text-decoration: none;
  border: 1px solid var(--line); border-radius: 12px; background: var(--surface);
  transition: border-color .18s var(--ease), transform .18s var(--ease);
}
.errx a:hover { border-color: var(--err); transform: translateY(-1px); }
.errx b { display: block; color: var(--ink); }
.errx em { display: block; font-style: normal; color: var(--muted); margin-top: 3px; }

/* ══ GET STARTED ══════════════════════════════════════════════════════
   The conversion page. Structure ported from the ProjectPro "Get
   Started" page (sticky section rail, hero + enquiry card, four-tab
   artefact panel, four steps, two-panel comparison, personas, guidance,
   numbers, voices, enquiry repeat, FAQ, closing band) and re-dressed in
   this site's language: bone ground, two families, the twelve roles,
   one accent. The reference's neon-on-black and per-card colour coding
   are deliberately not carried over — they belong to that brand. */

.gs { display: block; }
.gs .wrap { max-width: 1180px; margin-inline: auto; padding-inline: clamp(20px, 4vw, 40px); }

/* ── this page's own header ──────────────────────────────────────────
   The site header is not rendered on /get-started/ at all (see
   build-get-started.mjs), so there is nothing to hide at runtime and
   nothing to restore: every other page still ships the full one. This
   bar scrolls away with the hero, which is what lets the section rail
   below take the top of the screen. */
.gs-hdr { border-bottom: 1px solid var(--line); background: var(--bg); }
.gs-hdr-in {
  max-width: 1180px; margin-inline: auto;
  padding: 14px clamp(20px, 4vw, 40px);
  display: flex; align-items: center; justify-content: space-between; gap: 20px;
}
.gs-hdr-nav { display: flex; align-items: center; gap: clamp(14px, 2.4vw, 28px); }
.gs-hdr-nav a { color: var(--muted); text-decoration: none; white-space: nowrap; transition: color .18s var(--ease); }
.gs-hdr-nav a:hover { color: var(--ink); }

/* ── line-height ─────────────────────────────────────────────────────
   The role layer governs family, size and weight but deliberately not
   line-height, so each component sets its own. Nothing here did, and
   the copy fell back to `normal` (~1.2) — cramped, and visibly unlike
   the landing page, whose prose all sits at 1.6. These mirror the
   landing page's values by role, so the two pages read as one site. */
.gs :is(p, li, dd, dt, blockquote, figcaption, label) { line-height: 1.6; }
.gs :is(.gs-step b, .gs-guide-c b, .gs-who-c h3, .gs-after b, .gsf-h,
        .gs-banner-t, .gs-flow-t, .gs-chaps-h b, .gs-side-h) { line-height: 1.3; }
.gs :is(.kicker, .gs-step-n, .gs-node-k, .gs-banner-k, .gs-flow-k,
        .gs-group-k, .gs-diff-h, .gs-repo-vis, .pill-note) { line-height: 1.45; }
.gs .gs-h { line-height: 1.15; }
.gs .hero-h1 { line-height: 1.12; }
.gs .gs-close-h { line-height: 1.2; }
/* the mock windows keep their own tighter rhythm */
.gs .gs-pre code { line-height: 1.8; }
.gs :is(.gs-file, .gs-repo-head, .gs-chaps-l li) :is(span, b, em) { line-height: 1.4; }

/* ── the sticky section rail ── */
.gs-rail {
  /* this page has no floating site header, so the rail takes the top */
  position: sticky; top: 0; z-index: 40;
  border-bottom: 1px solid transparent;
  background: color-mix(in srgb, var(--bg) 88%, transparent);
  backdrop-filter: blur(10px);
  transition: border-color .2s var(--ease), opacity .2s var(--ease);
  opacity: 0; pointer-events: none;
}
.gs-rail.is-stuck { opacity: 1; pointer-events: auto; border-bottom-color: var(--line); }
.gs-rail-in {
  max-width: 1180px; margin-inline: auto; padding: 10px clamp(20px, 4vw, 40px);
  display: flex; align-items: center; gap: 16px;
}
.gs-rail ul {
  display: flex; gap: 4px; margin: 0; padding: 0; list-style: none;
  overflow-x: auto; scrollbar-width: none; flex: 1 1 auto;
}
.gs-rail ul::-webkit-scrollbar { display: none; }
.gs-rail ul a {
  display: block; padding: 7px 11px; border-radius: 999px; white-space: nowrap;
  color: var(--muted); text-decoration: none; transition: color .18s, background .18s;
}
.gs-rail ul a:hover { color: var(--ink); }
.gs-rail ul a.is-on { color: var(--ink); background: var(--surface-2); }
.gs-rail-cta { flex: none; padding: 8px 18px; }
@media (max-width: 720px) { .gs-rail-cta { display: none; } }

/* ── hero ── */
.gs-hero { padding: clamp(36px, 6vw, 72px) 0 clamp(28px, 4vw, 48px); }
.gs-hero-in {
  display: grid; gap: clamp(28px, 4vw, 56px);
  grid-template-columns: minmax(0, 1.05fr) minmax(0, .95fr); align-items: start;
}
.gs-hero-tx .lede { max-width: 46ch; }
.gs-hero-cta { margin: 30px 0 0; }
@media (max-width: 900px) { .gs-hero-in { grid-template-columns: 1fr; } }

/* ── the enquiry card ── */
.gsf {
  border: 1px solid var(--line); border-radius: 18px; background: var(--surface);
  padding: clamp(22px, 3vw, 30px);
  box-shadow: 0 18px 46px -28px rgba(var(--sh), .5);
}
.gsf-h { margin: 0 0 6px; color: var(--ink); }
.gsf-sub { margin: 0 0 20px; color: var(--muted); }
.gsf-row { display: grid; gap: 6px; margin: 0 0 14px; }
.gsf label { color: var(--ink); }
/* THE CARD RUNS OPPOSITE TO THE PAGE — the whole panel, not just the
   fields: white while the page is dark, near-black while the page is
   light. That is what makes it the brightest object on the screen, and
   it is the one element on the site that deliberately ignores the
   surface tokens. Everything inside therefore has to take its colour
   from --fld-*, not from --ink/--muted/--line, or it inverts with the
   page and disappears against the card. */
:root           { --fld-bg: #1f1e1d; --fld-in: #2b2a27; --fld-ink: #f6f3f1; --fld-hint: #9a958e; --fld-line: #3d3b37; }
:root[data-theme="dark"] { --fld-bg: #ffffff; --fld-in: #ffffff; --fld-ink: #1a1917; --fld-hint: #6b6660; --fld-line: #d8d4cd; }
/* --fld-hint was #8b8781: 3.57:1 on the white card, under the 4.5:1 floor
   for body text. It carries the card subtitle, the footnote and every
   placeholder, so it is the most-read grey on the page. */

.gsf { background: var(--fld-bg); border-color: var(--fld-line); color: var(--fld-ink); }
.gsf .gsf-h { color: var(--fld-ink); }
.gsf .gsf-sub, .gsf .gsf-note { color: var(--fld-hint); }
.gsf label { color: var(--fld-ink); }

.gsf :is(input, textarea) {
  width: 100%; padding: 11px 14px; border: 1px solid var(--fld-line); border-radius: 10px;
  font: inherit; resize: vertical;
  background: var(--fld-in); color: var(--fld-ink);
  transition: border-color .18s var(--ease), box-shadow .18s var(--ease);
}
.gsf :is(input, textarea)::placeholder { color: var(--fld-hint); }
.gsf :is(input, textarea):focus-visible {
  outline: none; border-color: var(--brand);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--brand) 30%, transparent);
}
/* THE PHONE FIELD IS ONE CONTROL, not a chip bolted to an input. The
   border, radius, background and focus ring all live on the wrapper; the
   select and the input inside are stripped bare and separated by a single
   hairline. Without this the picker reads as a foreign object sitting
   next to the field, which is exactly how the earlier fixed prefix read. */
.gsf-pre {
  display: flex; align-items: stretch;   /* NOT overflow:hidden — the
     combobox popup has to escape this box */
  border: 1px solid var(--fld-line); border-radius: 10px; background: var(--fld-in);
  transition: border-color .18s var(--ease), box-shadow .18s var(--ease);
}
.gsf-pre:focus-within {
  border-color: var(--brand);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--brand) 30%, transparent);
}
/* the inner controls carry no chrome of their own */
.gsf-pre input {
  border: 0 !important; border-radius: 0 !important;
  background: transparent; box-shadow: none !important; min-width: 0;
}
.gsf-ccw { position: relative; flex: none; display: flex; }

/* the native select is the value holder and the no-script fallback; the
   combobox below replaces it visually once get-started.js runs */
.gsf-cc {
  appearance: none; -webkit-appearance: none;
  width: 92px; padding: 11px 24px 11px 13px;
  border: 0; border-right: 1px solid var(--fld-line); border-radius: 9px 0 0 9px;
  background: transparent; color: var(--fld-ink); cursor: pointer;
  text-overflow: ellipsis; white-space: nowrap;
}
.gsf-cc option { background: var(--fld-in); color: var(--fld-ink); }
.gsf-cc[hidden] { display: none; }

/* ── the dialling-code combobox ── */
.cc { position: relative; flex: none; display: flex; }
.cc-btn {
  display: flex; align-items: center; gap: 7px;
  padding: 11px 12px 11px 13px; border: 0; border-right: 1px solid var(--fld-line);
  border-radius: 9px 0 0 9px; background: transparent; color: var(--fld-ink);
  cursor: pointer; white-space: nowrap;
}
.cc-btn:hover { background: color-mix(in srgb, var(--fld-ink) 5%, transparent); }
.cc-btn:focus-visible { outline: 2px solid var(--brand); outline-offset: -2px; }
.cc-flag { font-size: 15px; line-height: 1; }
.cc-car {
  width: 0; height: 0; margin-left: 1px;
  border-inline: 4px solid transparent; border-top: 5px solid var(--fld-hint);
}

/* [hidden] sets display:none only at UA-stylesheet strength, so this
   explicit display would beat it and the popup would never close — the
   same trap as the filtered rows below. Both are pinned shut here. */
.cc-pop[hidden] { display: none; }
.cc-pop {
  position: absolute; top: calc(100% + 7px); left: 0; z-index: 60;
  width: min(310px, calc(100vw - 64px));
  display: flex; flex-direction: column;
  border: 1px solid var(--fld-line); border-radius: 12px; background: var(--fld-in);
  box-shadow: 0 18px 44px -16px rgba(0, 0, 0, .45);
  overflow: hidden;
}
.cc-q {
  flex: none; margin: 8px; padding: 9px 12px;
  border: 1px solid var(--fld-line); border-radius: 8px;
  background: color-mix(in srgb, var(--fld-ink) 5%, var(--fld-in));
  color: var(--fld-ink); font: inherit;
}
.cc-q::placeholder { color: var(--fld-hint); }
.cc-q:focus-visible { outline: none; border-color: var(--brand); }
.cc-list {
  margin: 0; padding: 0 6px 6px; list-style: none;
  max-height: 268px; overflow-y: auto; overscroll-behavior: contain;
}
.cc-list li {
  display: flex; align-items: center; gap: 9px;
  padding: 8px 10px; border-radius: 7px; cursor: pointer; color: var(--fld-ink);
}
/* an explicit display beats the UA's [hidden] rule, so filtered-out rows
   stay on screen without this — the search looked broken, not the filter */
.cc-list li[hidden] { display: none; }
.cc-list li.is-active { background: color-mix(in srgb, var(--fld-ink) 9%, transparent); }
.cc-list li[aria-selected="true"] { color: var(--brand); }
.cc-n { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cc-d { flex: none; color: var(--fld-hint); font-variant-numeric: tabular-nums; }
.cc-none { margin: 0; padding: 4px 16px 14px; color: var(--fld-hint); }

.gsf-lead .gsf-pre { border-radius: 11px; }
.gsf-lead :is(.gsf-cc, .cc-btn) { padding-block: 13px; border-radius: 10px 0 0 10px; }
.gsf-pre input { border-radius: 0 10px 10px 0 !important; }
.gsf-go { width: 100%; margin: 6px 0 0; cursor: pointer; border: 0; }
.gsf-note { margin: 12px 0 0; color: var(--muted); }

/* the hero card, matched to the supplied reference: a lifted white panel,
   labelled fields, one full-width action, a centred reassurance line.
   The reference's logo strip beneath it was dropped on instruction. */
.gsf-lead { padding: clamp(26px, 3.2vw, 40px); border-radius: 22px; }
.gsf-lead .gsf-h { margin-bottom: 8px; }
.gsf-lead .gsf-sub { margin-bottom: 24px; }
.gsf-lead .gsf-row { margin-bottom: 18px; gap: 8px; }
.gsf-lead :is(input, textarea) { padding: 13px 16px; border-radius: 11px; }
.gsf-lead .gsf-go {
  padding: 15px 24px; border-radius: 11px; margin-top: 4px;
  /* the reference's action is the brand accent, not the ink button */
  background: var(--brand); color: var(--on-brand); box-shadow: none;
}
.gsf-lead .gsf-go:hover { background: var(--brand-2); transform: none; }
.gsf-lead .gsf-note { text-align: center; margin-top: 14px; }

/* the reference sets its accent phrase in a gradient; ours runs through
   the two brand tones. The flat colour under @supports is the fallback —
   background-clip:text silently paints nothing where it is unsupported. */
.hero-h1 em { font-style: normal; color: var(--brand); }
@supports (background-clip: text) or (-webkit-background-clip: text) {
  .hero-h1 em {
    background: linear-gradient(96deg, var(--brand) 8%, var(--brand-2) 92%);
    -webkit-background-clip: text; background-clip: text; color: transparent;
  }
}
/* the reference's secondary action is a soft rectangle, not a pill */
.gs-ghost { border-radius: 12px; padding: 14px 26px; }
.gsf-err { margin: 0 0 10px; color: var(--err); }
.gsf.is-sent .gsf-note { color: var(--ink); }

/* ── section furniture ── */
.gs-sec { padding: clamp(44px, 7vw, 90px) 0; border-top: 1px solid var(--line); }
.gs-hero, .gs-sec, .gs-close { scroll-margin-top: 78px; }  /* clears the pinned rail */
.gs-h { margin: 10px 0 0; color: var(--ink); max-width: 22ch; text-wrap: balance; }
.gs-h-sm { max-width: 26ch; }
.gs-lede { margin: 16px 0 0; max-width: 62ch; }

/* ── the four-tab artefact panel ─────────────────────────────────────
   Replicated from the supplied reference: one dark device panel holding
   a tab strip and, beneath it, a mock application window — a code
   editor, an architecture diagram, a session player, a repository.

   These stay DARK in both site themes. They are pictures of developer
   tools, and a light-mode IDE screenshot would read as another product.
   So the palette below is self-contained: --d-* tokens, never the site
   surface tokens, which is also what keeps the panel legible when the
   page around it flips. */
.gs-tabs {
  --d-bg: #16162b; --d-bar: #1e1e33; --d-strip: #1a1a29; --d-panel: #12121f;
  --d-ink: #ececf5; --d-dim: #8e8eaa; --d-line: #2b2b46;
  /* deep shades — every accent on this page is taken to its dark tone */
  --d-blue: #7093e0; --d-violet: #6753c4; --d-green: #268a5c; --d-amber: #a1741b;   /* d-blue was #3f6cba — 3.43:1 on #16162b */
  margin-top: clamp(26px, 3vw, 38px);
  border: 1px solid var(--d-line); border-radius: 16px;
  background: var(--d-bg); overflow: hidden;
  color: var(--d-ink);
}

.gs-tabbar {
  display: flex; gap: 0; margin: 0; padding: 0;
  border: 0; border-radius: 0; background: var(--d-strip);
  width: 100%; overflow-x: auto; scrollbar-width: none;
}
.gs-tabbar::-webkit-scrollbar { display: none; }
.gs-tab {
  flex: none; padding: 15px clamp(16px, 2.2vw, 28px);
  border: 0; border-radius: 0; background: none; cursor: pointer;
  color: var(--d-dim); white-space: nowrap;
  transition: background .18s var(--ease), color .18s var(--ease);
}
.gs-tab:hover { color: var(--d-ink); }
.gs-tab[aria-selected="true"] { background: var(--d-bar); color: #fff; box-shadow: none; }
.gs-tab:focus-visible { outline: 2px solid var(--d-blue); outline-offset: -2px; }

.gs-panel { padding: 0; }
.gs-cap {
  margin: 0; padding: 16px clamp(16px, 2.6vw, 30px) 22px;
  color: var(--d-dim); text-align: center;
}

/* the window itself */
.gs-win { border: 0; border-radius: 0; background: var(--d-bg); overflow: hidden; }
.gs-win-bar {
  display: flex; gap: 8px; padding: 14px 18px;
  background: var(--d-bar); border-bottom: 1px solid var(--d-line);
}
.gs-win-bar i { width: 11px; height: 11px; border-radius: 50%; background: #ff5f57; }
.gs-win-bar i:nth-child(2) { background: #febc2e; }
.gs-win-bar i:nth-child(3) { background: #28c840; }

/* code, in both the editor and the player. A system monospace stack —
   no third family is downloaded; code that is not monospaced does not
   read as code, and this is a depiction of an editor, not page text. */
/* The code cell is a terminal: pure black ground, pure white text, no
   syntax colouring of any kind. Asked for explicitly, and it is why the
   build-time highlighter was removed rather than merely overridden —
   there are no token spans left to colour. */
.gs-pre {
  margin: 0; padding: clamp(16px, 2.2vw, 26px);
  background: #000; border: 1px solid #232338; border-radius: 10px;
  color: #fff;
}
.gs-pre code {
  font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
  font-size: 13px; line-height: 1.8; color: #fff;
  white-space: pre-wrap; overflow-wrap: anywhere;   /* wrap, never scroll */
}
.gs-pre-sm code { font-size: 12px; }

/* ── 1 · the code window ── */
.gs-code { padding: clamp(18px, 2.6vw, 30px); display: grid; gap: clamp(18px, 2.4vw, 28px); }
.gs-banner {
  padding: clamp(16px, 2.2vw, 24px);
  border: 1px solid color-mix(in srgb, var(--d-blue) 32%, transparent);
  border-radius: 12px;
  background: color-mix(in srgb, var(--d-blue) 12%, transparent);
}
.gs-banner-k { margin: 0 0 8px; color: var(--d-blue); }
.gs-banner-t { margin: 0; color: #fff; }
.gs-banner-s { margin: 6px 0 0; color: var(--d-dim); }

/* ── 2 · the architecture diagram ── */
.gs-flow { padding: clamp(22px, 3vw, 36px) clamp(16px, 2.4vw, 28px); text-align: center; }
.gs-flow-k { margin: 0 0 6px; color: var(--d-violet); }
.gs-flow-t { margin: 0 0 26px; color: #fff; }
.gs-down { margin: 9px 0; color: var(--d-dim); line-height: 1; }
.gs-flow-r { --tone: var(--d-blue); }
.gs-flow-r.tone-violet { --tone: var(--d-violet); }
.gs-flow-r.tone-green  { --tone: var(--d-green); }
.gs-flow-r.tone-amber  { --tone: var(--d-amber); }
.gs-flow-r.is-group {
  padding: 16px clamp(12px, 1.8vw, 20px) 18px; border-radius: 14px;
  border: 1px solid color-mix(in srgb, var(--tone) 34%, transparent);
  background: color-mix(in srgb, var(--tone) 8%, transparent);
}
.gs-group-k { margin: 0 0 14px; color: var(--tone); }
.gs-nodes { display: flex; flex-wrap: wrap; gap: 10px; justify-content: center; align-items: stretch; }
.gs-right { display: grid; place-items: center; color: var(--d-dim); line-height: 1; }
.gs-node {
  flex: 0 1 208px; padding: 13px 14px; border-radius: 10px; text-align: center;
  border: 1px solid color-mix(in srgb, var(--tone) 38%, transparent);
  background: color-mix(in srgb, var(--tone) 11%, transparent);
}
.gs-node-k { display: block; color: var(--tone); margin-bottom: 5px; }
.gs-node b { display: block; color: #fff; }
.gs-node em { display: block; font-style: normal; color: var(--d-dim); margin-top: 3px; }

/* ── 3 · the session player ── */
.gs-play {
  display: grid; grid-template-columns: 286px minmax(0, 1fr);
  gap: clamp(14px, 2vw, 22px); padding: clamp(16px, 2.4vw, 28px);
}
.gs-chaps { border: 1px solid var(--d-line); border-radius: 10px; background: var(--d-panel); overflow: hidden; }
.gs-chaps-h { padding: 16px; border-bottom: 1px solid var(--d-line); }
.gs-chaps-h b { display: block; color: #fff; }
.gs-chaps-h em { display: block; font-style: normal; color: var(--d-dim); margin-top: 3px; }
.gs-chaps-l { margin: 0; padding: 6px 0; list-style: none; }
.gs-chaps-l li {
  display: grid; grid-template-columns: 20px minmax(0, 1fr) auto; align-items: center; gap: 8px;
  padding: 9px 14px; border-left: 2px solid transparent; color: var(--d-dim);
}
.gs-ch-i { width: 13px; height: 13px; border-radius: 50%; border: 1.5px solid currentColor; opacity: .55; }
.gs-chaps-l li[data-st="done"] .gs-ch-i { border: 0; opacity: 1; color: var(--d-green); }
.gs-chaps-l li[data-st="done"] .gs-ch-i::after { content: "\2713"; font-size: 12px; line-height: 1; }
.gs-chaps-l li[data-st="now"] {
  color: #fff; border-left-color: var(--d-blue);
  background: color-mix(in srgb, var(--d-blue) 14%, transparent);
}
.gs-chaps-l li[data-st="now"] .gs-ch-i { border: 0; opacity: 1; color: var(--d-blue); }
.gs-chaps-l li[data-st="now"] .gs-ch-i::after { content: "\25B6"; font-size: 10px; line-height: 1; }
.gs-ch-t { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.gs-ch-d { color: var(--d-dim); font-variant-numeric: tabular-nums; }

.gs-screen { border: 1px solid var(--d-line); border-radius: 10px; background: var(--d-panel); overflow: hidden; }
.gs-screen-b {
  margin: 0; padding: 11px 16px; color: #cfe0ff;
  background: color-mix(in srgb, var(--d-blue) 20%, transparent);
  border-bottom: 1px solid var(--d-line);
}
.gs-screen .gs-pre { border-radius: 0; background: transparent; }
.gs-bar { display: flex; align-items: center; gap: 13px; padding: 12px 16px 14px; border-top: 1px solid var(--d-line); }
.gs-playi {
  flex: none; width: 0; height: 0;
  border-block: 6px solid transparent; border-left: 10px solid var(--d-ink);
}
.gs-track { flex: 1 1 auto; height: 4px; border-radius: 999px; background: #2c2c48; position: relative; }
.gs-fill { display: block; height: 100%; border-radius: 999px; background: #7c8cff; position: relative; }
.gs-fill::after {
  content: ""; position: absolute; right: -6px; top: 50%; translate: 0 -50%;
  width: 12px; height: 12px; border-radius: 50%; background: #fff;
}
.gs-time { flex: none; color: var(--d-dim); font-variant-numeric: tabular-nums; }

/* ── 4 · the repository view ── */
/* The repository view carries GitHub's own dark palette rather than the
   violet chrome the other three windows use — it is a picture of that
   product, and the reference shows it in those exact greys. The tokens
   are redeclared HERE, so the override reaches every descendant and
   stops at this panel; the code, diagram and player tabs are untouched. */
.gs-repo {
  --d-bg: #0d1117; --d-panel: #0d1117; --d-line: #30363d;
  --d-ink: #e6edf3; --d-dim: #8b949e;
  --d-blue: #3a72c0; --d-green: #2b7f3f;
  margin: clamp(12px, 1.6vw, 20px); padding: 0;
  border: 1px solid var(--d-line); border-radius: 10px;
  background: var(--d-bg); overflow: hidden;
}
.gs-repo-hd {
  display: flex; align-items: center; flex-wrap: wrap; gap: 10px;
  padding: 15px clamp(14px, 2vw, 20px); border-bottom: 1px solid var(--d-line);
}
.gs-repo-ic { display: grid; place-items: center; width: 16px; color: var(--d-dim); }
.gs-repo-ic svg, .gs-fi svg { width: 15px; height: 15px; fill: currentColor; }
.gs-repo-path { color: var(--d-dim); }
.gs-repo-path a { color: var(--d-blue); text-decoration: none; }
.gs-repo-path a:hover { text-decoration: underline; }
.gs-repo-vis { padding: 2px 9px; border: 1px solid var(--d-line); border-radius: 999px; color: var(--d-dim); }
.gs-repo-acts { margin-left: auto; color: var(--d-ink); white-space: nowrap; }
.gs-repo-b { display: grid; grid-template-columns: minmax(0, 1fr) 232px; gap: clamp(16px, 2.4vw, 30px); padding: clamp(14px, 2vw, 20px); }
.gs-repo-branch { display: flex; align-items: center; gap: 12px; margin-bottom: 12px; }
.gs-branch { padding: 6px 13px; border: 1px solid var(--d-line); border-radius: 7px; background: #21262d; color: var(--d-ink); }
.gs-commits { color: var(--d-dim); }
.gs-repo-files { border: 1px solid var(--d-line); border-radius: 9px; overflow: hidden; background: var(--d-bg); }
.gs-repo-head {
  display: flex; align-items: center; gap: 12px; padding: 13px 16px;
  background: #161b22;
  border-bottom: 1px solid var(--d-line);
}
.gs-dot { flex: none; width: 15px; height: 15px; border-radius: 50%; background: var(--d-green); }
.gs-hmsg { color: var(--d-ink); min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.gs-hago { margin-left: auto; color: var(--d-dim); white-space: nowrap; }
.gs-file { display: flex; align-items: center; gap: 10px; padding: 11px 16px; border-bottom: 1px solid #21262d; }
.gs-file:last-child { border-bottom: 0; }
.gs-fi { display: grid; place-items: center; width: 16px; flex: none; color: var(--d-dim); }
.gs-fi-dir { color: #7d8590; }
.gs-fn { color: var(--d-ink); }
.gs-fi-dir + .gs-fn { color: var(--d-blue); }
.gs-fm { margin-left: auto; color: var(--d-dim); text-align: right; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.gs-repo-side { padding-left: clamp(8px, 1.4vw, 18px); }
.gs-side-h { margin: 0 0 8px; color: #fff; }
.gs-side-d { margin: 0 0 14px; color: var(--d-dim); }
.gs-side-f { margin: 0 0 20px; padding: 0; list-style: none; display: grid; gap: 7px; }
.gs-side-f li { display: flex; align-items: center; gap: 8px; color: var(--d-dim); }
.gs-langbar { display: flex; height: 8px; border-radius: 999px; overflow: hidden; margin-bottom: 10px; }
.gs-langs { margin: 0; padding: 0; list-style: none; display: flex; flex-wrap: wrap; gap: 5px 14px; }
.gs-langs li { display: flex; align-items: center; gap: 6px; color: var(--d-dim); }
.gs-langs i { width: 9px; height: 9px; border-radius: 50%; }

@media (max-width: 900px) {
  .gs-play { grid-template-columns: 1fr; }
  .gs-repo-b { grid-template-columns: 1fr; }
  .gs-repo-side { border-left: 0; border-top: 1px solid var(--d-line); padding: 18px 0 0; }
}
@media (max-width: 640px) {
  /* four labels cannot sit on one 375px line; two rows fit exactly and
     spare the reader a sideways scroll to discover tabs three and four */
  .gs-tabbar { flex-wrap: wrap; overflow-x: visible; }
  .gs-tab { flex: 1 1 50%; padding: 13px 10px; text-align: center; }
}
@media (max-width: 560px) {
  .gs-fm, .gs-repo-acts { display: none; }
  .gs-node { flex-basis: 100%; }
  .gs-right { display: none; }
  .gs-play, .gs-repo, .gs-code, .gs-flow { padding-inline: 12px; }
}

/* ── four steps ─────────────────────────────────────────────────────
   Laid out to the supplied reference: pill kicker over a centred
   heading, then the four cards inside one frame, each opening with a
   rounded icon tile in its own accent. Only this section is centred —
   the rest of the page keeps its left-aligned headings. */
.gs-mid { text-align: center; }
.gs-mid .gs-h { max-width: 20ch; margin-inline: auto; }
.gs-kpill { margin: 0 0 18px; }

.gs-steps-frame {
  margin-top: clamp(28px, 3.4vw, 44px);
  padding: clamp(14px, 1.8vw, 22px);
  border: 1px solid var(--line); border-radius: 22px;
  background: color-mix(in srgb, var(--surface) 55%, transparent);
}
.gs-steps {
  display: grid; gap: clamp(12px, 1.6vw, 18px); margin: 0; padding: 0; list-style: none;
  grid-template-columns: repeat(auto-fit, minmax(232px, 1fr));
}
.gs-step {
  text-align: left; padding: clamp(20px, 2.4vw, 28px);
  border: 1px solid var(--line); border-radius: 16px; background: var(--surface);
}
.gs-step.tone-violet { --tone: #7c6cf0; }
.gs-step.tone-green  { --tone: #1fa97a; }
.gs-step.tone-amber  { --tone: #d08700; }
.gs-step.tone-pink   { --tone: #db4d8e; }
:root[data-theme="dark"] .gs-step.tone-violet { --tone: #6653c6; }
:root[data-theme="dark"] .gs-step.tone-green  { --tone: #1f8a63; }
:root[data-theme="dark"] .gs-step.tone-amber  { --tone: #a97c17; }
:root[data-theme="dark"] .gs-step.tone-pink   { --tone: #b04478; }

.gs-step-ic {
  display: grid; place-items: center; width: 48px; height: 48px;
  margin-bottom: clamp(20px, 2.4vw, 30px);
  border-radius: 13px; color: var(--tone);
  background: color-mix(in srgb, var(--tone) 15%, transparent);
}
.gs-step-ic svg { width: 23px; height: 23px; }
/* the reference sets every STEP label in one blue, whatever the tile beside
   it — the tile carries the identity, the label stays a constant */
:root { --step-lab: #2f6fd0; }
:root[data-theme="dark"] { --step-lab: #7ea9ea; }   /* was #3f74c2 — 4.06:1 on the dark ground */
.gs-step-n { display: block; color: var(--step-lab); margin-bottom: 11px; }
.gs-step b { display: block; color: var(--ink); margin-bottom: 9px; }
.gs-step p { margin: 0; color: var(--muted); }

/* ── the comparison ────────────────────────────────────────────────
   To the supplied reference: centred pill and heading, then two panels
   that MEET — no gap between them — so the pair reads as one block cut
   down the middle rather than as two cards. Red on the left for what
   you are being sold elsewhere, blue on the right for what we do; the
   brand rust is deliberately not used here, since a warm accent beside
   the red would blur exactly the distinction this section exists for. */
.gs-lede-mid { margin-inline: auto; }
.gs-diff {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
  gap: 0; margin-top: clamp(28px, 3.4vw, 46px);
  border-radius: 18px; overflow: hidden; text-align: left;
}
.gs-diff-c { padding: clamp(24px, 3vw, 38px) clamp(20px, 2.6vw, 34px); }
.gs-diff-c.is-bad {
  --tone: #ef8570;                 /* was #c2503f — 3.81:1 on the #2a110e ground */
  background: #2a110e;
  border: 1px solid #4d1f18;
}
.gs-diff-c.is-good {
  --tone: #6ea8e8;                 /* was #4180c4 — 4.25:1 on the #0d1a2d ground */
  background: #0d1a2d;
  border: 1px solid #1d3a5e;
  border-left-width: 0;
}
@media (max-width: 700px) {
  .gs-diff-c.is-good { border-left-width: 1px; border-top-width: 0; }
}
.gs-diff-h { margin: 0 0 22px; color: var(--tone); }
.gs-diff-c ul { margin: 0; padding: 0; list-style: none; display: grid; gap: 18px; }
.gs-diff-c li { display: grid; grid-template-columns: 24px minmax(0, 1fr); gap: 13px; align-items: start; }
.gs-mk {
  display: grid; place-items: center; width: 24px; height: 24px; margin-top: 1px;
  border-radius: 50%; line-height: 1;
  background: color-mix(in srgb, var(--tone) 20%, transparent); color: var(--tone);
}
/* the left column is what you are leaving behind, so it sits back a step */
.is-bad li > span:last-child { color: var(--muted); }
.is-good li > span:last-child { color: var(--ink); }

/* ── personas ── */
.gs-who { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 14px; margin-top: clamp(26px, 3vw, 38px); }
.gs-who-c { padding: clamp(20px, 2.6vw, 28px); border: 1px solid var(--line); border-radius: 16px; background: var(--surface); }
.gs-who-n {
  display: inline-grid; place-items: center; width: 34px; height: 34px; margin-bottom: 16px;
  border-radius: 9px; background: var(--surface-2); color: var(--brand);
}
/* The brand red is picked for white. On the dark page these two counters
   sit on --surface-2, where it falls to 3.26:1, so they take a lifted
   tone of the same hue rather than a different colour. */
:root[data-theme="dark"] :is(.gs-who-n, .gs-after-n) { color: #f0897a; }
.gs-who-c h3 { margin: 0 0 8px; color: var(--ink); }
.gs-who-c > p { margin: 0; color: var(--muted); }
.gs-who-c ul { margin: 16px 0 0; padding: 0; list-style: none; display: grid; gap: 9px; }
.gs-who-c li { position: relative; padding-left: 16px; color: var(--ink); }
.gs-who-c li::before {
  content: ""; position: absolute; left: 0; top: .55em;
  width: 5px; height: 5px; border-radius: 50%; background: var(--brand);
}

/* ── how guidance works ── */
.gs-guide { display: grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); gap: 14px; margin-top: clamp(26px, 3vw, 38px); }
.gs-guide-c { padding: clamp(20px, 2.6vw, 26px); border: 1px solid var(--line); border-radius: 14px; background: var(--surface); }
.gs-guide-c b { display: block; color: var(--ink); margin-bottom: 8px; }
.gs-guide-c p { margin: 0; color: var(--muted); }

/* ── the numbers ── */
.gs-stats { margin-top: clamp(26px, 3vw, 38px) !important; max-width: none !important; padding-top: 0 !important; }
.gs-stats::before, .gs-stats::after { content: none !important; }

/* ── student voices ── */
.gs-voices { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 14px; margin-top: clamp(26px, 3vw, 38px); }
.gs-voice { margin: 0; padding: clamp(20px, 2.6vw, 26px); border: 1px solid var(--line); border-radius: 16px; background: var(--surface); display: flex; flex-direction: column; gap: 18px; }
.gs-voice blockquote { margin: 0; flex: 1 1 auto; }
.gs-voice blockquote p { margin: 0; color: var(--ink); }
.gs-voice figcaption { display: flex; align-items: center; gap: 11px; }
.gs-av { display: grid; place-items: center; flex: none; width: 34px; height: 34px; border-radius: 50%; background: var(--surface-2); color: var(--muted); }
.gs-who-t { display: grid; }
.gs-who-t b { color: var(--ink); }
.gs-who-t em { font-style: normal; color: var(--muted); }

/* ── the enquiry repeat ── */
.gs-enq-in { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, .9fr); gap: clamp(28px, 4vw, 56px); align-items: center; }
.gs-ticks { margin-top: 24px; }
@media (max-width: 900px) { .gs-enq-in { grid-template-columns: 1fr; } }

/* ── what happens next ── */
.gs-after {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(210px, 1fr));
  gap: 14px; margin: clamp(26px, 3vw, 38px) 0 0; padding: 0; list-style: none;
}
.gs-after li { padding: 20px; border: 1px solid var(--line); border-radius: 14px; background: var(--surface); }
.gs-after-n {
  display: inline-grid; place-items: center; width: 28px; height: 28px; margin-bottom: 14px;
  border-radius: 50%; background: var(--surface-2); color: var(--brand);
}
.gs-after b { display: block; color: var(--ink); margin-bottom: 6px; }
.gs-after p { margin: 0; color: var(--muted); }

/* ── FAQ ── */
.gs-faq { margin-top: clamp(26px, 3vw, 38px); border-top: 1px solid var(--line); }
.gs-fq { border-bottom: 1px solid var(--line); }
.gs-fq summary {
  display: flex; align-items: center; justify-content: space-between; gap: 18px;
  padding: 20px 2px; cursor: pointer; list-style: none; color: var(--ink);
}
.gs-fq summary::-webkit-details-marker { display: none; }
.gs-fq summary:hover { color: var(--brand); }
.gs-fq-i { position: relative; flex: none; width: 14px; height: 14px; }
.gs-fq-i::before, .gs-fq-i::after {
  content: ""; position: absolute; inset: 0; margin: auto;
  background: var(--muted); transition: transform .2s var(--ease), opacity .2s var(--ease);
}
.gs-fq-i::before { width: 14px; height: 1.5px; }
.gs-fq-i::after { width: 1.5px; height: 14px; }
.gs-fq[open] .gs-fq-i::after { transform: rotate(90deg); opacity: 0; }
.gs-fq .fq-p { padding: 0 2px 22px; max-width: 76ch; }
.gs-fq .fq-p p { margin: 0; color: var(--muted); }
.gs-faq-all { margin: 26px 0 0; }

/* ── closing band ── */
.gs-close { padding: clamp(52px, 8vw, 104px) 0; border-top: 1px solid var(--line); background: var(--surface-2); text-align: center; }
.gs-close-h { margin: 0; color: var(--ink); text-wrap: balance; }
.gs-close .lede { margin: 18px auto 0; max-width: 54ch; }
.gs-close-cta { margin: 30px 0 0; }

/* ── the role layer ───────────────────────────────────────────────────
   AUTHORITATIVE. A section-by-section walk found the same job done at a
   dozen different sizes — labels at 11/600, 11/700, 12/700, 14/400 and
   25/600; headings at 40, 49, 52, 63 and 64; three different "small bold"
   treatments. Local rules kept winning on specificity, so this layer is
   marked important on the type properties only. Layout, colour and spacing
   stay with the component.

   Twelve roles. Nothing on the site is outside them.

     sans   11/600 UP .08em   labels, kickers, eyebrows, column heads
     sans   13/400            meta, fine print
     sans   14/500            controls
     sans   14/600            names, inline emphasis
     sans   16/400            table and answer text
     sans   18/600            card titles
     serif  14/400            small prose
     serif  16/400            prose
     serif  18/400            ledes
     serif  26/400            h3
     serif  40/400            h2, stat figures, bento numerals
     serif  52/400            h1

   To add a style, add a role here — do not set font-size in a component. */

/* label */
:is(.kicker, .ab-kicker, .ab-star, .foot-cols h4, .st-ix, .st-ix span,
    .ab-big .tl span, .fq-meta, .pill-note, .mq-label, .stats span,
    .band span, .cmp .ch-t, .rv-label, .dlg-kick, .err-kick,
    .gs-step-n, .gs-node-k, .gs-who-n, .gs-after-n,
    .gs-banner-k, .gs-flow-k, .gs-group-k, .gs-diff-h,
    .hr-lead, .marquee-lbl, .fqi-label, .conf-card header span,
    .abfig-h, .abstand-k, .abvm-k, .abnav-k,
    .ap-eyebrow, .ap-sec-k, .ap-vs-k, .ap-str-row dt, .ap-badge-txt) {
  font-family: var(--font) !important;
  font-size: 13px !important; line-height: 1.38 !important;
  font-weight: 500 !important; letter-spacing: 0.6px !important; text-transform: uppercase !important;
}

/* meta / fine print */
:is(.foot-legal, .rv figcaption em, .mq-set span, .foot-note,
    .panel-note, .foot-bar p, .abx-chips li, .abpage-crumb, .abpage-crumb a,
    .svc-more em, .cat-row .cat-meta, .ttl-row .ttl-meta, .ttl-count,
    .ttl-chip, .ttl-lvl, .ttl-chip span, .faq-tag, .mi-tx em, .mm-tx em,
    .dlg-code, .errx em, .dlg-links a, .err-links a, .errp-end, .cc-d, .cc-none,
    .gsf-sub, .gsf-note, .gsf-err, .gs-cap, .gs-node em, .gs-who-t em,
    .gs-banner-s, .gs-ch-d, .gs-time, .gs-fm, .gs-hago, .gs-commits,
    .gs-side-d, .gs-langs, .gs-chaps-h em, .gs-repo-path, .gs-side-f, .gs-repo-vis,
    .gs-repo-acts, .foot-contact li, .foot-legalrow a, .foot-top a, .sm-list a,
    .abnav em) {
  font-family: var(--font) !important;
  font-size: 13px !important; line-height: 1.38 !important;
  font-weight: 400 !important; letter-spacing: 0 !important; text-transform: none !important;
}

/* controls */
:is(.btn, .bg-tx em, .nav-signin, .nav-start, .scard-go, .mm-cta, .mm-all, .abpage-back, .fq-all a, .mm-col h3, .btn-danger,
    .gs-tab, .gs-rail ul a, .gsf-cc, .cc-btn, .cc-n, .abnav b, .ap-str-tab,
    .nav-links > a, .mp-plain > a, .menu-trigger, .gs-hdr-nav a) {
  font-family: var(--font) !important;
  font-size: 15px !important; line-height: 1.33 !important;
  font-weight: 500 !important; letter-spacing: 0 !important;
}

/* The header's own controls. They were sharing the inline-control size,
   and 15px in a 77px bar reads as fine print — the nav is the one set of
   labels every visitor has to find without looking for them. Registered
   here rather than sized in the header component, which this layer forbids.

   Specificity note: the controls row above is (0,1,2) because its :is()
   contains `.gs-rail ul a`, and :is() takes its most specific argument.
   `.site-header :is(...)` lands at (0,2,1) — two classes beats one, so
   this wins without needing to be tripled. */
.site-header :is(.menu-trigger, .nav-links > a, .nav-signin, .nav-start) {
  font-size: 17px !important; line-height: 1.3 !important;
}

/* The bar carries six destinations plus Sign in and Get Started, and at
   17px that row needs about 1400px to sit inside the header's own margin.
   Between 1281 (where the inline row first appears) and there, the item
   padding closes up rather than the type shrinking back — the labels are
   the thing that had to be readable, the space between them is not. */
@media (min-width: 1281px) and (max-width: 1439px) {
  .site-header :is(.menu-trigger, .nav-links > a) { padding-inline: 9px; }
  .site-header .nav-auth { gap: 8px; }
  .site-header .nav-signin { padding-inline: 12px; }
}

/* The one full-size control that closes every page — a distinct job from
   the inline .btn, so it earns a row here rather than a size set in the
   component, which this layer forbids.

   Tripled on purpose. :is() takes the specificity of its MOST SPECIFIC
   argument and applies it to every branch, and the controls row above
   contains `.gs-rail ul a` — (0,1,2). A single class with !important
   loses to that, so the button kept the 15px inline-control size. */
.cta-pill.cta-pill.cta-pill {
  font-family: var(--font) !important;
  font-size: 17px !important; line-height: 1.3 !important;
  font-weight: 600 !important; letter-spacing: 0 !important;
}

/* names and inline emphasis */
:is(.rv figcaption b, .cmp td b, .cmp li b, .conf-card li b, .conf-card header b,
    .mm-compact b, .ttl-row b, .faq-q, .mm-tx b, .errx b,
    .gs-who-t b, .gs-node b, .gsf label, .gs-ch-t, .gs-fn, .gs-hmsg,
    .gs-chaps-h b, .gs-side-h, .gs-branch, .gs-screen-b) {
  font-family: var(--font) !important;
  font-size: 15px !important; line-height: 1.33 !important;
  font-weight: 600 !important; letter-spacing: 0 !important;
}

/* table and answer text */
:is(.cmp th, .cmp td, .fq-p p, .dlg-desc,
    .gsf input, .gsf textarea, .cc-q) {
  font-family: var(--font) !important;
  font-size: 17px !important; line-height: 1.53 !important;
  font-weight: 400 !important; letter-spacing: 0 !important;
}

/* card titles */
:is(.scard h3, .feat h3, .mm-gc-t b, .mm-rt b, .mm-lg-t b, .abx-card h3, .svc-more b, .cat-row b, .mi-tx b, .dlg h1, .dlg h2,
    .gs-banner-t, .gs-flow-t, .gs-step b, .gs-guide-c b, .gs-who-c h3, .gs-after b,
    .fq-q, .brand,
    .abstand-list b, .abvm-list b, .ap-cell b) {
  font-family: var(--font-display) !important;
  font-size: 20px !important; line-height: 1.30 !important;
  font-weight: 600 !important; letter-spacing: -0.2px !important; text-transform: none !important;
}

/* small prose */
:is(.assure, .ab-note, .foot-brand p, .mm-gc-t em, .mm-rt em, .mm-lg-t em,
    .scard p, .feat p, .conf-card li, .conf-card li p, .st-note, .abx-card p,
    .gs-step p, .gs-who-c > p, .gs-who-c li, .gs-guide-c p, .gs-after p,
    .gs-diff-c li > span:last-child, .gs-side-d,
    .abx-note p, .cat-row p, .cat-empty, .ttl-row p, .ttl-none, .faq-a p,
    .m-feat, .fqm-tx, .fqi-open, .fqi-shut, .conf-lede p,
    .abstand-list p, .ctx-form .ct-note,
    .ap-cell p, .ap-stats span, .ap-hold-list li, .ap-note p,
    .ap-str-row dd, .ap-vs-col li, .ap-tick-list li) {
  font-family: var(--font) !important;
  font-size: 15px !important; line-height: 1.47 !important;
  font-weight: 400 !important; letter-spacing: 0 !important;
}

/* prose */
:is(.about p, .story .st-d, .rv blockquote p, .tl, .svc-steps p,
    .gs-voice blockquote p) {
  font-family: var(--font) !important;
  font-size: 17px !important; line-height: 1.53 !important;
  font-weight: 400 !important; letter-spacing: 0 !important;
}

/* ledes */
:is(.lede, .sub, .cmp-sub, .fq-head .lede, .err-desc,
    .abst-stand, .abstand-close, .ctx-sub, .ap-sub, .ap-sec-d) {
  font-family: var(--font) !important;
  font-size: 20px !important; line-height: 1.50 !important;
  font-weight: 400 !important; letter-spacing: 0 !important;
}

/* headings */
:is(.conf-lede h3, .grid6 .feat.feat-lead h3, .abx-t,
    .abst-pull p, .abvm-pledge) {
  font-family: var(--font-display) !important;
  font-size: clamp(24px, 2.6vw, 32px) !important; line-height: 1.19 !important;
  font-weight: 600 !important; letter-spacing: -0.6px !important;
}
.gs-h.gs-h-sm {
  font-family: var(--font-display) !important;
  font-size: clamp(21px, 2vw, 24px) !important; line-height: 1.25 !important;
  font-weight: 600 !important; letter-spacing: -0.4px !important;
}
:is(.sec h2, .sec h2.cmp-h, .story .st h2, .fq-head h2, .cta-band h2, .about h2, .ab-h, .abx-cta h2,
    .abstand-say, .ap-sec-h, .ap-say,
    .gs-h, .gs-close-h) {
  font-family: var(--font-display) !important;
  font-size: clamp(30px, 3.6vw, 44px) !important; line-height: 1.09 !important;
  font-weight: 600 !important; letter-spacing: -1.1px !important;
}
:is(.band b, .stats b, .grid6 .feat .feat-n, .ap-stats b, .ap-num) {
  font-family: var(--font-display) !important;
  font-size: clamp(30px, 3.6vw, 44px) !important; line-height: 1.09 !important;
  font-weight: 600 !important; letter-spacing: -1.1px !important;
}
/* 9vw so phones land at 32–36px — the 40px floor was breaking the
   headline one word per line at 390px */
/* The journey curve is the one place on the site that scales in vw rather
   than px. It is a single composition — path, markers, risers and captions
   all sized against the same 95vw canvas — so a px type scale would drift
   away from the drawing at every other viewport. Kept here in the role
   layer rather than in the component, per this layer's own instruction. */
.ap-jrn-h.ap-jrn-h.ap-jrn-h {
  font-family: var(--font-display) !important;
  font-size: 1.667vw !important; line-height: 1.25 !important;
  font-weight: 500 !important; letter-spacing: -0.01em !important;
}
.ap-jrn-sub.ap-jrn-sub {
  font-family: var(--font) !important;
  font-size: 1.389vw !important; line-height: 1.45 !important;
  font-weight: 400 !important; letter-spacing: 0 !important;
}
.ap-mark-tx.ap-mark-tx b {
  font-family: var(--font-display) !important;
  font-size: 1.389vw !important; line-height: 1.3 !important;
  font-weight: 500 !important; letter-spacing: -0.01em !important;
}
.ap-mark-tx.ap-mark-tx em {
  font-family: var(--font) !important;
  font-size: 1.11vw !important; line-height: 1.32 !important;
  font-weight: 400 !important; letter-spacing: 0 !important; font-style: normal !important;
}

/* ── nothing draws through a label ────────────────────────────────────
   The markers stand on the curve and the curve doubles back four times,
   so it runs behind the labels — measured, it was through all six of
   them, between 7 and 104 sampled points inside each. The label paints
   over the line already; what it lacked was anything to hold the line
   off the glyphs, which is why the text was unreadable at some of the
   bends while it was perfectly visible at others.

   A halo hugs each glyph and a soft-edged ground fills between the
   lines, because a halo alone leaves the line running through the gap
   between two lines of a subtitle. The ground fades out rather than
   ending at an edge: a hard fill would be a black card cut out of the
   starfield. */
/* No panel behind the text. The halo alone, tightened to make up for
   it: more passes at shorter radii read as an outline around the
   letters rather than a patch behind them, so the line dims where it
   meets a glyph instead of disappearing under a shape. */
.ap-mark-tx,
.ap-mark-ic {
  text-shadow:
    0 0 4px var(--road-ground), 0 0 4px var(--road-ground),
    0 0 3px var(--road-ground), 0 0 2px var(--road-ground),
    0 0 2px var(--road-ground), 0 0 1px var(--road-ground);
}
.ap-mark-ic svg {
  filter: drop-shadow(0 0 3px var(--road-ground)) drop-shadow(0 0 3px var(--road-ground));
}

/* below the desktop drawing the same roles step up, or they would be
   unreadable against a 375px screen */
@media (max-width: 860px) {
  .ap-jrn-h.ap-jrn-h.ap-jrn-h.ap-jrn-h { font-size: 5.4vw !important; }
  .ap-jrn-sub.ap-jrn-sub { font-size: 3.9vw !important; }
  .ap-mark-tx.ap-mark-tx b { font-size: 4.2vw !important; }
  .ap-mark-tx.ap-mark-tx em { font-size: 3.4vw !important; }
}

.hero h1, .hr-word, .ap-h1 {
  font-family: var(--font-display) !important;
  font-size: clamp(40px, 6.5vw, 76px) !important; line-height: 1.06 !important;
  font-weight: 600 !important; letter-spacing: -2.4px !important;
}

/* ══ THE HEADER PANEL — NEW VIEW ══════════════════════════════════════
   Ported from the mega-menu reference, replacing the old rendering
   outright. Sits last so it wins every earlier panel rule: the aurora,
   spotlight, glass, plates and per-layout styling are all switched off
   here rather than hunted through the file.

   The look: one quiet card. Titled columns, generous column gaps, rows of
   a bordered icon square beside label + description. The only hover
   response is the reference's — the icon square inverts to ink and the
   description sharpens. The slide-between-triggers morph, the trigger
   pill and the indicator all stay: they are behaviour, not dressing. */

.menu-panel.mega {
  background: var(--surface);
  -webkit-backdrop-filter: none;
  backdrop-filter: none;
  border: 1px solid var(--line);
  border-radius: 16px;
  padding: 20px 24px;
  width: max-content; max-width: calc(100vw - 32px);
  overflow: hidden;
}
.menu-panel.mega::before,
.menu-panel.mega::after { content: none; }

.menu-panel.mega .mm-cols { display: flex; gap: 16px; padding: 0; }
.menu-panel.mega .mm-col { min-width: 0; flex: 1; width: 300px; padding: 0; border: 0; }
.menu-panel.mega .mm-col + .mm-col { border-left: 0; }
.menu-panel.mega .mm-col h3 {
  margin: 0 0 14px; padding: 0; border: 0;
  color: var(--faint); text-transform: none;
}
.menu-panel.mega .mm-col ul {
  list-style: none; margin: 0; padding: 0;
  display: grid; gap: 10px;
}
.menu-panel.mega .mm-col li { border: 0; }

/* Each option is its own box — a full border of its own, so every row has
   a distinct top line and bottom line rather than sharing a hairline with
   its neighbour. white-space is reset here because the old plain-list rule
   (.menu-panel .mm-col a) set nowrap and the descriptions inherited it,
   which is what was pushing text past the border. */
.menu-panel.mega a.mm-row {
  display: flex; align-items: flex-start; gap: 12px;
  padding: 12px 14px; margin: 0;
  border: 1px solid var(--line); border-radius: 12px;
  background: var(--surface-2);
  text-decoration: none; white-space: normal;
  -webkit-backdrop-filter: none; backdrop-filter: none;
  transition: border-color .2s var(--ease), background .2s var(--ease),
              transform .2s var(--ease), box-shadow .2s var(--ease);
}
.menu-panel.mega a.mm-row:hover,
.menu-panel.mega a.mm-row:focus-visible {
  border-color: var(--brand-line); background: var(--surface);
  transform: translateY(-1px);
  box-shadow: 0 6px 16px -10px rgba(var(--sh), .35);
}
@media (prefers-reduced-motion: reduce) {
  .menu-panel.mega a.mm-row:hover { transform: none; }
}
.menu-panel.mega a.mm-row .mm-ic {
  flex: none; width: 36px; height: 36px; border-radius: 9px;
  border: 1px solid var(--line-strong); background: none;
  transition: background .25s var(--ease), border-color .25s var(--ease);
}
.menu-panel.mega a.mm-row:hover .mm-ic,
.menu-panel.mega a.mm-row:focus-visible .mm-ic {
  background: var(--ink); border-color: var(--ink);
}
.menu-panel.mega a.mm-row:hover .mm-ic::after,
.menu-panel.mega a.mm-row:focus-visible .mm-ic::after { background: var(--surface); }
.menu-panel.mega a.mm-row:focus-visible { outline: none; }

.menu-panel.mega .mm-tx { display: flex; flex-direction: column; gap: 2px; min-width: 0; text-align: left; }
/* an ancient rule (.menu-panel b { align-self: center }) from the first
   panel design was centring every title inside its column — everything
   here flows left-to-right, hard against the icon */
.menu-panel.mega .mm-tx b,
.menu-panel.mega .mm-tx em { align-self: flex-start; text-align: left; }
.menu-panel.mega .mm-tx b { color: var(--ink); }
.menu-panel.mega .mm-tx em {
  font-style: normal; color: var(--muted);
  transition: color .25s var(--ease);
}
.menu-panel.mega a.mm-row:hover .mm-tx em { color: var(--ink); }

/* The one type rule this page still needs of its own: the code cell is a
   terminal and must stay monospace. Everything else now comes from the
   site-wide SF Pro scale — the bespoke block that used to live here was
   removed so no component is typeset differently from the rest of the
   site. */
.gs .gs-pre code {
  font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace !important;
  line-height: 1.8 !important; letter-spacing: 0 !important;
}

/* ══ GET STARTED — COMPLETE DARK GROUND ═══════════════════════════════
   The page ground drops to a near-black and every surface, hairline and
   accent is re-based on it, so nothing light is left anywhere: the
   enquiry cards lose their white panel, and the brand orange goes to its
   dark shade alongside the other accents.

   Scoped to body.gsp — only this page carries that class, so the rest of
   the site keeps its bone ground, its light theme and the bright brand.
   The class is doubled because :root[data-theme="dark"] (0,2,0) sets
   some of these already and a plain body.gsp (0,1,1) would lose to it. */
body.gsp.gsp {
  /* ground and surfaces */
  --bg: #08080a;
  --surface: #101013;
  --surface-2: #17171b;
  --line: #24242a;
  --line-strong: #34343c;

  /* the brand, dark */
  --brand: #a8501f;
  --brand-2: #c2632c;
  --brand-soft: rgba(174, 14, 32, .10);
  --brand-line: #4a1219;

  /* The enquiry cards stay WHITE against the near-black ground — the one
     deliberate light object on the page, which is what makes it the
     thing your eye lands on. Everything inside reads from these, so the
     card's text, labels, fields and dropdown all flip with it. */
  --fld-bg: #ffffff;
  --fld-in: #ffffff;
  --fld-ink: #1a1917;
  /* was #8b8781 — 3.57:1 on the white card, under the 4.5:1 floor. This
     grey carries the subtitle, the footnote and every placeholder, so it
     is the most-read text on the card. */
  --fld-hint: #6b6660;
  --fld-line: #d8d4cd;

  background: var(--bg);
}

/* the one remaining light element was the solid ink pill; on this page
   the primary action is the dark brand, like every other accent here */
body.gsp .btn-primary,
body.gsp .gs-rail-cta {
  background: var(--brand) !important;
  color: #fff !important;
  box-shadow: none !important;
}
body.gsp :is(.btn-primary, .gs-rail-cta):hover { background: var(--brand-2) !important; }

/* ══ GET STARTED — CENTRED ═══════════════════════════════════════════
   Every heading and every body paragraph on this page is centred. Two
   things are NOT: the enquiry cards (the details-entering place, left
   as they were on instruction) and the four mock application windows —
   a centred file list, code block or repository header stops looking
   like the software it is depicting.

   Rows that hang a marker off the left edge cannot simply be given
   text-align:center — the marker would stay pinned left while the words
   drifted away from it. Those are rebuilt as centred flex rows below so
   the marker travels with its text. */

/* section headers */
body.gsp .gs-sec > .wrap > :is(.kicker, .pill-note, .gs-h, .lede, .gs-lede),
body.gsp .gs-close .wrap > * { text-align: center; }
body.gsp :is(.gs-h, .gs-lede, .gs-sec > .wrap > .lede) { margin-inline: auto; }

/* the hero column */
body.gsp .gs-hero-tx { text-align: center; }
body.gsp .gs-hero-tx .lede { margin-inline: auto; }

/* cards */
body.gsp :is(.gs-step, .gs-who-c, .gs-guide-c, .gs-after li, .gs-voice) { text-align: center; }
body.gsp :is(.gs-step-ic, .gs-who-n, .gs-after-n) { margin-inline: auto; }

/* persona bullets: the dot moves inline so it stays beside its words */
body.gsp .gs-who-c ul { justify-items: center; }
body.gsp .gs-who-c li { padding-left: 0; }
body.gsp .gs-who-c li::before {
  position: static; display: inline-block; margin-right: 8px; vertical-align: middle;
}

/* the enquiry section's tick list, same treatment */
body.gsp .gs-enq-tx { text-align: center; }
body.gsp .gs-ticks li { padding-left: 0; }
body.gsp .gs-ticks li::before {
  position: static; display: inline-block; margin: 0 7px 0 0; vertical-align: -2px;
}

/* voices */
body.gsp .gs-voice figcaption { justify-content: center; }

/* comparison rows: badge and text centred together as one unit */
body.gsp .gs-diff-c { text-align: center; }
body.gsp .gs-diff-c li {
  display: flex; grid-template-columns: none;
  justify-content: center; align-items: flex-start; gap: 11px; text-align: center;
}
body.gsp .gs-mk { flex: none; }

/* FAQ: question and its toggle centred as a pair, answer centred under it */
body.gsp .gs-fq summary { justify-content: center; gap: 12px; }
body.gsp .gs-fq .fq-p { margin-inline: auto; text-align: center; }
body.gsp .gs-faq-all { text-align: center; }

/* EXCLUDED — the details-entering place keeps its left alignment */
body.gsp .gsf, body.gsp .gsf :is(h2, p, label, span, div, input, textarea) { text-align: left; }
body.gsp .gsf .gsf-note { text-align: center; }   /* beats the left-align exclusion above */

/* ── two sections keep the original view ──────────────────────────────
   The hero and the FAQ are explicitly excluded from the page-wide
   centring above and stay exactly as they were: hero copy ranged left
   in its column, FAQ heading left, and each question with its toggle
   pushed out to the right edge of the row.

   The #faq rules carry an id, which outranks the class-only centring
   rules regardless of source order — the hero's match on specificity
   and win by coming later. */
body.gsp .gs-hero-tx,
body.gsp .gs-hero-tx :is(.pill-note, .hero-h1, .lede, .gs-hero-cta) { text-align: left; }
body.gsp .gs-hero-tx .lede { margin-inline: 0; }

body.gsp #faq > .wrap > :is(.kicker, .pill-note, .gs-h, .lede, .gs-lede) { text-align: left; }
body.gsp #faq :is(.gs-h, .gs-lede) { margin-inline: 0; }
body.gsp #faq .gs-fq summary { justify-content: space-between; }
body.gsp #faq .gs-fq .fq-p { text-align: left; margin-inline: 0; }
body.gsp #faq .gs-faq-all { text-align: left; }

/* the comparison panels also keep the original view: each row back to a
   fixed marker column with its text ranged left beside it, and the panel
   headers left. The section's own kicker and heading stay centred — they
   were centred to the reference before the page-wide change, not by it. */
body.gsp #difference .gs-diff-c { text-align: left; }
body.gsp #difference .gs-diff-c li {
  display: grid; grid-template-columns: 24px minmax(0, 1fr);
  gap: 13px; align-items: start; text-align: left;
}

/* the persona cards keep the original view too: badge at the left, copy
   ranged left, and the bullet dots back in their own gutter rather than
   riding inline with the words. Section kicker and heading stay centred,
   which is also how the reference sets this section. */
body.gsp #who .gs-who-c { text-align: left; }
body.gsp #who .gs-who-n { margin-inline: 0; }
body.gsp #who .gs-who-c ul { justify-items: stretch; }
body.gsp #who .gs-who-c li { padding-left: 16px; }
body.gsp #who .gs-who-c li::before {
  position: absolute; left: 0; top: .55em;
  display: block; margin: 0; vertical-align: baseline;
}

/* ── the section pill ────────────────────────────────────────────────
   Every section on this page now opens with the same pill — dark ground,
   a filled dot, blue label — including the hero's. It replaced the plain
   coloured kickers so no section is dressed differently from the next.
   The blue is the page's own --step-lab, the same one already carrying
   the STEP labels and the right-hand comparison panel, so the pill reads
   as part of the deepened palette rather than a brighter import. */
body.gsp .pill-note {
  color: var(--step-lab);
  background: var(--surface-2);
  border-color: var(--line);
}
body.gsp .pill-note .ping { background: var(--step-lab); }
body.gsp .pill-note .ping::after { border-color: var(--step-lab); }
body.gsp .pill-note:hover { border-color: color-mix(in srgb, var(--step-lab) 45%, var(--line)); }

/* the pill is an inline object, so it centres with text-align on the
   parent; the sections that keep the original left view are unaffected */
body.gsp .gs-kpill { margin: 0 0 18px; }

/* The pill is an inline-flex box, so text-align on the pill itself does
   nothing to its position — it only lays out its own contents. To sit it
   under the centred heading it has to become a block sized to its
   content with auto side margins. #faq and the hero keep the original
   left view and are excluded. */
body.gsp .gs-sec:not(#faq) > .wrap > .pill-note {
  display: flex; width: max-content; max-width: 100%; margin-inline: auto;
}

/* ── the enquiry section, to the reference ───────────────────────────
   Left-ranged copy beside the white card, an accent phrase in the
   heading, and a filled blue check against each promise. */
body.gsp #enquiry .gs-enq-tx { text-align: left; }
body.gsp #enquiry :is(.gs-h, .lede) { margin-inline: 0; }

/* the heading's accent phrase runs the same gradient as the hero's */
body.gsp .gs-h em { font-style: normal; color: var(--step-lab); }
@supports (background-clip: text) or (-webkit-background-clip: text) {
  body.gsp .gs-h em {
    background: linear-gradient(96deg, var(--step-lab) 0%, #6a5ac6 55%, #a8467e 100%);
    -webkit-background-clip: text; background-clip: text; color: transparent;
  }
}

/* a solid blue disc with a white tick, in place of the site's soft dot */
body.gsp .gs-ticks li { padding-left: 30px; }
body.gsp .gs-ticks li::before {
  content: ''; position: absolute; left: 0; top: 3px;
  width: 19px; height: 19px; border-radius: 50%; margin: 0;
  background: var(--step-lab) center / 11px 11px no-repeat;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23fff' stroke-width='3.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M20 6 9 17l-5-5'/%3E%3C/svg%3E");
}

/* ── what happens after: numbered tiles, as the reference sets them ── */
body.gsp .gs-after-n {
  width: 34px; height: 34px; border-radius: 10px;
  background: color-mix(in srgb, var(--step-lab) 18%, transparent);
  color: var(--step-lab);
}

/* ══ SF PRO SCALE — THE REMAINING ROLES ══════════════════════════════
   The role layer above covers every selector that already existed. These
   fill the two rungs it had no group for, and separate button weight
   from navigation weight, which shared one rule.

     Hero   clamp(40 → 76) / 1.06 / 600 / -2.4px   the landing headline
     H1     clamp(36 → 56) / 1.07 / 600 / -1.5px   a page's own title
     H2     clamp(30 → 44) / 1.09 / 600 / -1.1px   section headings
     H3     clamp(24 → 32) / 1.19 / 600 / -0.6px
     H4     clamp(21 → 24) / 1.25 / 600 / -0.4px
     H5     20 / 1.30 / 600 / -0.2px               card titles
     Large  20 / 1.50 / 400                        ledes
     Body   17 / 1.53 / 400
     Small  15 / 1.47 / 400
     Nav    15 / 1.33 / 500     Button 15 / 1.33 / 600
     Label  13 / 1.38 / 500 uppercase +0.6px       Caption 13 / 1.38 / 400

   Mobile falls out of the clamps: hero 40, H1 36, H2 30, H3 24, with
   body held at 17 so reading size never drops. */

/* A page's own title sits between the landing hero and a section heading.
   The selector is tripled because the role layer's h2 group reaches
   .ab-h through an :is() whose most specific member is `.sec h2.cmp-h`
   — :is() takes that specificity for the whole group, so a single class
   here would lose and the title would render at H2 size. */
.abpage-h1.abpage-h1.abpage-h1, .hero-h1.hero-h1, .err-title.err-title,
.pgh-h.pgh-h.pgh-h {
  font-family: var(--font-display) !important;
  font-size: clamp(36px, 4.6vw, 56px) !important; line-height: 1.07 !important;
  font-weight: 600 !important; letter-spacing: -1.5px !important;
}
/* the enquiry card's title is a card title, not a section heading */
.gsf-h {
  font-family: var(--font-display) !important;
  font-size: clamp(21px, 2vw, 24px) !important; line-height: 1.25 !important;
  font-weight: 600 !important; letter-spacing: -0.4px !important;
}
/* Buttons carry more weight than navigation, which shares their role
   group. Classes are tripled because that group's :is() inherits the
   specificity of its most specific member (`.gs-rail ul a`), which a
   single class cannot beat. */
:is(.btn.btn.btn, .btn-danger.btn-danger.btn-danger, .gsf-go.gsf-go.gsf-go,
    .nav-start.nav-start.nav-start, .mm-cta.mm-cta.mm-cta) {
  font-weight: 600 !important;
}

/* ── measure and rhythm ──────────────────────────────────────────────
   Premium typography is as much about line length as size: past roughly
   75 characters the eye loses the start of the next line. Headings are
   held shorter still so they break on meaning rather than on the edge of
   the column, and `text-wrap: balance` evens the resulting lines. */
:is(p, li) { text-wrap: pretty; }
:is(h1, h2, h3, h4, .gs-h, .hero h1, .abpage-h1, .hero-h1, .err-title, .gs-close-h) {
  text-wrap: balance;
}
:is(.lede, .sub, .cmp-sub, .err-desc, .gs-lede) { max-width: 72ch; }

/* ── footer, to the footer-2 reference ───────────────────────────────
   Three bands separated by hairlines: the link columns, then the social
   row, then a centred copyright line. Ported structure, our content.

   Two parts of the reference are deliberately absent. The App Store and
   Play Store buttons are gone — Shaventra ships no mobile app, and a
   download button for software that does not exist is a false claim, not
   a styling choice. And its four Linktree columns are replaced by this
   site's own sections, which the footer already generates from the menu
   so the two can never drift apart. */
.foot-rule { height: 1px; background: var(--line); margin: 0; border: 0; }

/* The social buttons now sit inside the brand block, directly beneath
   the location line, rather than on a band of their own — so they read
   as part of how you reach us, next to the email and the country. */
.foot-social {
  display: flex; gap: 8px; align-items: center;
  margin-top: 16px;
}
.foot-social a {
  display: grid; place-items: center; width: 38px; height: 38px;
  border: 1px solid var(--line); border-radius: 10px;
  color: var(--muted); background: transparent;
  transition: border-color .18s var(--ease), color .18s var(--ease), background .18s var(--ease);
}
.foot-social a:hover {
  color: var(--ink); border-color: var(--line-strong);
  background: var(--surface);
}
.foot-social svg { width: 18px; height: 18px; }

/* the copyright band, centred like the reference's */
.foot-bar {
  display: block; text-align: center; padding-block: 18px;
}
.foot-bar p { margin: 0; }
.foot-bar .foot-note { margin-top: 6px; }

/* The error pages are a single centred screen, so their layout has to
   make room for the footer's fixed reveal panel rather than sitting on
   top of it. The grid loses its 1fr stretch and the card centres in the
   viewport above the footer instead. */
body.errp-hasfoot { min-height: auto; display: block; }
body.errp-hasfoot .errp-mid {
  /* minmax(0, 1fr) rather than an implicit auto column: an auto track is
     sized to its content's MIN-CONTENT width, and when that exceeds the
     padded box the column grows — after which the card's `width: 100%`
     resolves against the inflated column and spills past the padding.
     Floored at 0, the track can never exceed the space available. */
  display: grid; grid-template-columns: minmax(0, 1fr); justify-items: center;
  align-content: center;
  min-height: calc(100svh - 190px); padding: 40px 20px 60px;
}
body.errp-hasfoot .err-hero { max-width: 100%; }
body.errp-hasfoot .errp-end { text-align: center; padding-bottom: 26px; }

/* the reference's own values, on the dark theme it was drawn for */
:root[data-theme="dark"] .foot-panel {
  background:
    radial-gradient(125% 125% at 50% 10%, rgba(15, 15, 17, .40) 50%, rgba(60, 162, 250, .20) 100%),
    linear-gradient(180deg, var(--bg), var(--surface-2) 22%);
}

/* ── footer: the enterprise bands ────────────────────────────────────
   What an established company publishes and a small site usually skips:
   a reachable contact block, a legal and compliance row with pages
   actually behind it, and a way back to the top. */

.foot-contact {
  list-style: none; margin: 16px 0 0; padding: 0;
  display: grid; gap: 8px;
}
.foot-contact li { display: flex; align-items: center; gap: 9px; color: var(--muted); }
.foot-contact svg { width: 17px; height: 17px; flex: none; opacity: .75; }
.foot-contact a { color: inherit; text-decoration: none; }
.foot-contact a:hover { color: var(--ink); text-decoration: underline; text-underline-offset: 3px; }

/* the legal row sits above the copyright, separated by a hairline */
.foot-legalrow {
  display: flex; flex-wrap: wrap; justify-content: center;
  gap: 10px 22px; margin: 0 0 14px; padding: 0 0 14px;
  border-bottom: 1px solid var(--line);
}
.foot-legalrow a {
  color: var(--muted); text-decoration: none;
  transition: color .18s var(--ease);
}
.foot-legalrow a:hover { color: var(--ink); text-decoration: underline; text-underline-offset: 3px; }

.foot-top { margin: 14px 0 0; }
.foot-top a {
  display: inline-block; padding: 7px 15px;
  border: 1px solid var(--line); border-radius: 999px;
  color: var(--muted); text-decoration: none;
  transition: border-color .18s var(--ease), color .18s var(--ease);
}
.foot-top a:hover { color: var(--ink); border-color: var(--line-strong); }

/* the sitemap page's grouped lists */
.sm-list { list-style: none; margin: 12px 0 0; padding: 0; display: grid; gap: 7px; }
.sm-list a { color: var(--muted); text-decoration: none; }
.sm-list a:hover { color: var(--brand); text-decoration: underline; text-underline-offset: 3px; }

@media (max-width: 560px) {
  .foot-legalrow { gap: 8px 16px; }
}

/* ══ FOOTER — BLACK AND WHITE ═════════════════════════════════════════
   The footer is monochrome in both themes: a black ground with white and
   grey type, no accent, no tint, no gradient. That means the earlier
   radial wash and the three coloured glow blobs are switched off here
   rather than recoloured — a gradient with a blue in it is not black and
   white. The brand mark is masked, so it is turned white through
   --logo-ink instead of being swapped for another file. */
.foot-panel,
:root[data-theme="dark"] .foot-panel {
  background: #000;
}
.foot-glow { display: none; }
/* ...except that the footer is white now. .foot-panel is what actually
   paints the footer's ground — .foot behind it is only the box — so
   flipping .foot alone left a white element with a black panel sitting on
   top of it, which is exactly what it looked like. Both have to move. */
.foot-panel,
:root[data-theme="dark"] .foot-panel {
  background: #FFFFFF;
}

.foot {
  --f-ink: #ffffff;      /* headings, brand, hover */
  --f-dim: #a6a6a6;      /* links and body at rest */
  --f-faint: #7a7a7a;    /* legal line, fine print */
  --f-line: #2a2a2a;     /* hairlines and borders */
  --logo-ink: #ffffff;
  color: var(--f-dim);
}

.foot :is(h4, .foot-brand .brand, .gs-side-h) { color: var(--f-ink); }
.foot .foot-brand p { color: var(--f-dim); }
.foot .foot-legal { color: var(--f-faint) !important; }   /* their own rules are !important */

.foot .foot-cols a,
.foot .foot-contact li,
.foot .foot-contact a,
.foot .foot-legalrow a,
.foot .foot-top a,
.foot .foot-bar p { color: var(--f-dim); }

.foot :is(.foot-cols a, .foot-contact a, .foot-legalrow a, .foot-top a):hover {
  color: var(--f-ink);
}
.foot .foot-note { color: var(--f-faint) !important; }

.foot :is(.foot-rule, .foot-legalrow) { background: var(--f-line); border-color: var(--f-line); }
.foot .foot-rule { background: var(--f-line); }
.foot .foot-legalrow { background: none; border-bottom-color: var(--f-line); }

.foot .foot-social a { border-color: var(--f-line); color: var(--f-dim); background: none; }
.foot .foot-social a:hover { border-color: var(--f-dim); color: var(--f-ink); background: #111; }
.foot .foot-top a { border-color: var(--f-line); }
.foot .foot-top a:hover { border-color: var(--f-dim); }

/* ══ FOOTER — IN THE DOCUMENT, NOT PINNED TO THE VIEWPORT ═════════════
   The footer used to be a fixed layer the page scrolled over: .foot held
   a fixed height (max 620px), .foot-fixed was position:fixed and
   .foot-stick was sticky, which produced the reveal-on-scroll movement.

   Two things were wrong with that here. The movement was unwanted, and
   the footer's content has since outgrown 620px — a contact block, four
   columns, a social band, a legal row, copyright and back-to-top. A
   fixed layer that overflows its own clip is what pushed hairlines
   across the social icons and struck a line through Back to top.

   So the whole mechanism is switched off and the footer is an ordinary
   block at the end of the document, as tall as its contents need. */
.foot {
  height: auto; position: relative; clip-path: none;
  background: #000;
}
.foot-fixed { position: static; width: auto; height: auto; }
.foot-stick { position: static; top: auto; height: auto; overflow: visible; }
.foot-panel {
  display: block; min-height: 0;
  padding-block: clamp(44px, 5vw, 72px) 0;
}

/* the hairlines span the content column and nothing more */
.foot-rule { width: 100%; margin: 0; height: 1px; border: 0; background: var(--f-line); }

/* the three bands, evenly spaced now that nothing is being justified apart */
.foot-in { padding-bottom: clamp(30px, 4vw, 46px); }
.foot-bar { padding-block: 22px clamp(26px, 3vw, 38px); }

/* ── back to top, pinned to the viewport edge ────────────────────────
   Identical behaviour at every width: it appears once you are 600px
   down and sits in the bottom-right corner. On a phone the footer's own
   link is more than 1200px below the fold, which made it effectively
   unreachable — this is always one tap away.

   [hidden] needs restating because the explicit display below would
   otherwise beat the UA rule and the button would never hide — the same
   trap that kept the country dropdown open. */
.to-top[hidden] { display: none; }
.to-top {
  position: fixed; z-index: 70;
  right: clamp(14px, 2.4vw, 28px);
  /* clears the home indicator on notched phones */
  bottom: calc(clamp(14px, 2.4vw, 28px) + env(safe-area-inset-bottom, 0px));
  display: grid; place-items: center;
  width: clamp(44px, 3.6vw, 52px); height: clamp(44px, 3.6vw, 52px);
  border-radius: 50%; cursor: pointer;
  background: #000; color: #fff; border: 1px solid #2f2f2f;
  box-shadow: 0 8px 24px -6px rgba(0, 0, 0, .55);
  transition: background .18s var(--ease), border-color .18s var(--ease), transform .18s var(--ease);
}
.to-top svg { width: 20px; height: 20px; }
.to-top:hover { background: #141414; border-color: #4a4a4a; transform: translateY(-2px); }
.to-top:focus-visible { outline: 2px solid #fff; outline-offset: 3px; }
@media (prefers-reduced-motion: reduce) { .to-top { transition: none; } .to-top:hover { transform: none; } }

/* the footer's own link is redundant once the pinned control exists */
.foot-top { display: none; }

/* ── footer columns as dropdowns ─────────────────────────────────────
   Each column is a <details>. On desktop it is simply an open block and
   the summary reads as a plain heading; below 700px it becomes a
   tappable row with a chevron, which is what makes a four-column footer
   usable on a phone instead of a 1,200px scroll. */
.foot-col { border: 0; }
.foot-col > summary {
  list-style: none; cursor: default;
  display: flex; align-items: center; justify-content: space-between;
}
.foot-col > summary::-webkit-details-marker { display: none; }
.foot-col > summary h4 { margin: 0; }
.foot-col-i { display: none; }
.foot-col-links { display: grid; }

@media (max-width: 700px) {
  .foot-cols { grid-template-columns: 1fr; gap: 0; }
  .foot-col { border-bottom: 1px solid var(--f-line); }
  .foot-col > summary { cursor: pointer; padding-block: 15px; }
  .foot-col > summary h4 { margin: 0; }
  .foot-col-links { padding-bottom: 14px; }

  /* the chevron, drawn from a border so it needs no asset */
  .foot-col-i {
    display: block; width: 9px; height: 9px; flex: none; margin-right: 2px;
    border-right: 1.5px solid currentColor; border-bottom: 1.5px solid currentColor;
    rotate: 45deg; translate: 0 -2px;
    transition: rotate .22s var(--ease), translate .22s var(--ease);
  }
  .foot-col[open] > summary .foot-col-i { rotate: 225deg; translate: 0 2px; }
}
@media (prefers-reduced-motion: reduce) { .foot-col-i { transition: none; } }

/* ── the bottom bar, to the reference ────────────────────────────────
   Copyright and the legal links sit together as one inline group on the
   right, exactly as the reference sets them, with the positioning line
   on the left. Below 700px the two stack and centre. */
.foot-bar {
  display: flex; align-items: center; justify-content: space-between;
  gap: 14px 28px; flex-wrap: wrap; text-align: left;
  /* clears the pinned back-to-top, which floats over the bottom-right
     corner of the viewport and was landing on the last legal link */
  padding-bottom: clamp(64px, 7vw, 86px);
}
.foot-bar-end {
  display: flex; align-items: center; flex-wrap: wrap; gap: 8px 20px;
  margin-left: auto;
}
.foot-bar-end p { margin: 0; }
.foot-legalrow {
  display: flex; flex-wrap: wrap; gap: 8px 20px;
  margin: 0; padding: 0; border: 0;
}
@media (max-width: 700px) {
  .foot-bar { flex-direction: column; text-align: center; }
  .foot-bar-end { margin-left: 0; flex-direction: column; gap: 10px; }
  .foot-legalrow { justify-content: center; }
}

/* ── skip to content ─────────────────────────────────────────────────
   WCAG 2.4.1. Off-screen until it takes focus, then it lands in the
   top-left corner as the first thing a keyboard user reaches. */
.skip {
  position: absolute; left: 12px; top: 12px; z-index: 200;
  transform: translateY(-160%);
  padding: 11px 18px; border-radius: 10px;
  /* --bg, not --on-brand. --on-brand means "what reads on the brand
     colour", which is white wherever the brand is dark — and on a dark skin
     --ink is near-white too, so this came out white on white and vanished
     for exactly the keyboard users it exists for. --bg is the page's own
     ground, so the pill inverts correctly on light and dark alike. */
  background: var(--ink); color: var(--bg);
  text-decoration: none; font-weight: 600;
  transition: transform .18s var(--ease);
}
.skip:focus-visible { transform: translateY(0); outline: 2px solid var(--brand); outline-offset: 2px; }
#main:focus { outline: none; }


/* ── guides ──────────────────────────────────────────────────────────────
   The blog7 reference block, in this site's own material. Its Tailwind is
   not portable — there is no Tailwind here, no React and no shadcn — so what
   crosses over is the composition: a centred badge / heading / lede with a
   text link above a three-up grid of cards, each an image over a title, a
   summary and a "Read more" row pinned to the bottom.

   The reference sets its own type sizes on every element (text-3xl,
   md:text-4xl, lg:text-5xl, text-lg …). None of that comes across either —
   sizes belong to the role layer, so these rules set colour, spacing and
   structure only. */

.gd-head { text-align: center; max-width: 720px; margin-inline: auto; }

/* the reference's <Badge variant="secondary"> */
.gd-badge {
  display: inline-block; margin-bottom: 22px;
  padding: 6px 14px; border-radius: 999px;
  border: 1px solid var(--line);
  background: var(--brand-soft); color: var(--brand);
  font-size: 13px; font-weight: 500; letter-spacing: .6px; text-transform: uppercase;
}
.gd-head h2 { margin: 0; text-wrap: balance; }
.gd-lede { margin: 18px auto 0; color: var(--muted); text-wrap: pretty; }

/* its <Button variant="link"> — a text link with a trailing arrow, not a
   filled control */
.gd-all {
  display: inline-flex; align-items: center; gap: 8px;
  margin-top: 26px; text-decoration: none; color: var(--brand); font-weight: 600;
}
.gd-arr { display: inline-block; transition: transform .2s var(--ease); }
.gd-all:hover .gd-arr, .gd-more:hover .gd-arr { transform: translateX(4px); }

.gd-grid {
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: clamp(20px, 2.4vw, 32px);
  margin-top: clamp(44px, 5vw, 72px);
}

/* grid-rows in the reference is [auto_auto_1fr_auto], which pins the footer
   to the bottom whatever the summary's length. A column flex with the
   summary on `flex: 1` does the same thing in one fewer declaration. */
.gd-card {
  display: flex; flex-direction: column;
  border: 1px solid var(--line); border-radius: 16px;
  background: var(--surface); overflow: hidden;
  transition: border-color .25s, box-shadow .25s, transform .25s var(--ease);
}
.gd-card:hover {
  border-color: var(--line-strong); transform: translateY(-2px);
  box-shadow: 0 16px 40px -18px rgba(var(--sh), .28);
}

.gd-img { display: block; aspect-ratio: 16 / 9; overflow: hidden; background: var(--surface-2); }
.gd-img img {
  width: 100%; height: 100%; object-fit: cover; display: block;
  transition: transform .5s var(--ease), opacity .2s;
}
.gd-card:hover .gd-img img { transform: scale(1.03); }

.gd-meta {
  margin: 22px 24px 0; color: var(--muted);
  font-size: 13px; font-weight: 500; letter-spacing: .6px; text-transform: uppercase;
}
.gd-card h3 { margin: 10px 24px 0; text-wrap: pretty; }
.gd-card h3 a { color: var(--ink); text-decoration: none; }
.gd-card h3 a:hover { text-decoration: underline; }
.gd-sum { flex: 1; margin: 12px 24px 0; color: var(--muted); }
.gd-more {
  display: inline-flex; align-items: center; gap: 8px; align-self: flex-start;
  margin: 22px 24px 24px; text-decoration: none; color: var(--brand); font-weight: 600;
}
.gd-more:hover { text-decoration: underline; }

/* The image is decorative here — the title beside it is the real link, and a
   screen reader does not need to hear the same destination twice. It is kept
   clickable because a card whose picture is dead reads as broken. */
.gd-img:focus-visible { outline: 2px solid var(--brand); outline-offset: -2px; }

@media (max-width: 980px) {
  .gd-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  /* an orphan third card centres rather than hanging left */
  .gd-card:last-child:nth-child(odd) { grid-column: 1 / -1; max-width: 520px; margin-inline: auto; width: 100%; }
}
@media (max-width: 640px) {
  .gd-grid { grid-template-columns: minmax(0, 1fr); }
  .gd-card:last-child:nth-child(odd) { grid-column: auto; max-width: none; }
}

/* the blog index reuses the landing grid, without the centred head above it */
.gd-grid-index { margin-top: clamp(28px, 3.4vw, 44px); }

/* The old post template's byline and hero figure lived here. Both are gone —
   .bl-hero is now the full-width dark band at the top of a post, and this
   rule's `aspect-ratio: 16/9` was still applying to it, stretching the band
   to 802px on a 1425px viewport. Class collision with my own earlier CSS. */


/* ═══ BLOG ═══════════════════════════════════════════════════════════════
   Arrangement follows projectpro.io's blog, which the founder chose as the
   reference — featured post, a numbered "Latest" panel, category chips, then
   everything in a dated card grid; and on a post, a dark hero band, a
   contents list and a sticky sidebar.

   Their identity is not taken with it. Every colour here resolves from this
   site's own tokens, the type comes from the role layer, and no size is set
   in this block that the role layer owns. */

/* ── index: featured ─────────────────────────────────────────────────── */
/* These two bands were pinned dark because the card diagrams were drawn
   on a dark ground. The diagrams are drawn on the page's ground now, so
   a dark band would put a bright rectangle inside a black strip — the
   exact inverse of the problem the dark band was solving. Both bands sit
   on the page ground; the type follows. */
.bl-feat { background: none; color: var(--ink); padding-block: clamp(40px, 6vw, 80px); }
.bl-feat-in {
  display: grid; grid-template-columns: minmax(0, 1.05fr) minmax(0, 1fr);
  gap: clamp(28px, 4vw, 64px); align-items: center;
}
.bl-feat-img {
  display: block; border-radius: 18px; overflow: hidden;
  aspect-ratio: 16 / 9; background: var(--surface-2);
  box-shadow: 0 24px 60px -40px rgba(0, 0, 0, .3);
}
.bl-feat-img img { width: 100%; height: 100%; object-fit: cover; display: block; transition: transform .5s var(--ease); }
.bl-feat-img:hover img { transform: scale(1.03); }
.bl-feat-meta {
  margin: 0 0 14px; color: var(--muted);
  font-size: 13px; font-weight: 500; letter-spacing: .6px; text-transform: uppercase;
}
.bl-feat-meta b { color: var(--brand-2); font-weight: 600; }
.bl-feat-copy h1 { margin: 0; text-wrap: balance; }
.bl-feat-copy h1 a { color: var(--ink); text-decoration: none; }
.bl-feat-copy h1 a:hover { text-decoration: underline; }
.bl-feat-sum { margin: 18px 0 22px; color: var(--muted); max-width: 52ch; text-wrap: pretty; }

/* the pill that carries a post's category, on both grounds */
.bl-chip {
  display: inline-block; padding: 7px 15px; border-radius: 999px;
  border: 1px solid var(--line); background: var(--brand-soft); color: var(--brand);
  font-size: 13px; font-weight: 500; letter-spacing: .4px;
}
.bl-feat .bl-chip { border-color: var(--line); background: var(--surface-2); color: var(--muted); }

/* ── index: the numbered "Latest" panel ──────────────────────────────── */
.bl-latest { padding-block: clamp(44px, 6vw, 84px); }
.bl-latest-panel {
  background: var(--brand-soft); border: 1px solid var(--line);
  border-radius: 22px; padding: clamp(28px, 3.6vw, 52px);
}
.bl-latest-panel > h2 { margin: 0 0 clamp(24px, 3vw, 40px); }
.bl-latest-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: clamp(22px, 3vw, 44px); }
.bl-num-n {
  display: grid; place-items: center; width: 58px; height: 58px; margin-bottom: 20px;
  border: 1.5px solid var(--brand); border-radius: 50%;
  color: var(--brand); font-family: var(--font-display); font-size: 26px; font-weight: 600;
}
.bl-num h3 { margin: 0 0 12px; text-wrap: pretty; }
.bl-num h3 a { color: var(--ink); text-decoration: none; }
.bl-num h3 a:hover { text-decoration: underline; }
.bl-num p { margin: 0 0 18px; color: var(--muted); }

/* ── index: category chips ───────────────────────────────────────────── */
.bl-cats { padding-bottom: clamp(36px, 4.5vw, 64px); }
.bl-cats h2 { margin: 0 0 clamp(20px, 2.4vw, 32px); }
.bl-cat-list { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; gap: 10px; }
.bl-cat {
  display: inline-flex; align-items: center; gap: 9px;
  padding: 9px 16px; border-radius: 999px;
  border: 1px solid var(--line); background: var(--surface); color: var(--ink);
  text-decoration: none; font-size: 15px; font-weight: 500;
  transition: border-color .2s, background .2s, color .2s;
}
.bl-cat span { color: var(--muted); font-size: 13px; font-variant-numeric: tabular-nums; }
.bl-cat:hover { border-color: var(--brand); background: var(--brand-soft); color: var(--brand); }
.bl-cat:hover span { color: var(--brand); }

/* ── index: everything, grouped ──────────────────────────────────────── */
.bl-all { background: var(--surface-2, var(--bg-alt, transparent)); padding-block: clamp(44px, 6vw, 84px); }
.bl-all > .wrap > h2 { margin: 0 0 clamp(28px, 3.4vw, 48px); display: flex; align-items: baseline; gap: 14px; }
.bl-all-n {
  font-family: var(--font); font-size: 15px; font-weight: 500; letter-spacing: 0;
  color: var(--muted); font-variant-numeric: tabular-nums;
}
/* scroll-margin so a chip jump does not land the heading under the header */
.bl-cat-h {
  margin: clamp(36px, 4vw, 60px) 0 clamp(18px, 2vw, 26px);
  scroll-margin-top: calc(var(--hdr, 64px) + 28px);
}
.bl-cat-h:first-of-type { margin-top: 0; }
.bl-all .gd-grid { margin-top: 0; }

/* ── post: the hero band ─────────────────────────────────────────────── */
.bl-hero {
  background: none; color: var(--ink);
  padding-block: clamp(20px, 2.6vw, 34px) clamp(36px, 5vw, 68px);
}
.bl-crumb {
  display: flex; flex-wrap: wrap; align-items: center; gap: 10px;
  margin-bottom: clamp(22px, 3vw, 38px);
  font-size: 13px; color: var(--muted);
}
.bl-crumb a { color: var(--muted); text-decoration: none; }
.bl-crumb a:hover { color: var(--ink); text-decoration: underline; }
.bl-crumb span[aria-hidden] { color: var(--faint); }

.bl-hero-in {
  display: grid; grid-template-columns: minmax(0, 1.15fr) minmax(0, .85fr);
  gap: clamp(28px, 4vw, 60px); align-items: center;
}
.bl-hero-copy h1 { margin: 0; color: var(--ink); text-wrap: balance; }
.bl-hero-sub { margin: 20px 0 0; color: var(--muted); max-width: 56ch; text-wrap: pretty; }
.bl-hero-cta { display: flex; flex-wrap: wrap; gap: 12px; margin-top: clamp(24px, 3vw, 34px); }
/* Both site buttons are built for a light ground and neither survives here:
   .btn-primary is --ink on --ink, and .btn-ghost is --surface with white
   text. Restated for the dark band rather than patched globally. */
.bl-hero-cta .btn-primary { background: var(--brand); color: #fff; border-color: transparent; }
.bl-hero-cta .btn-primary:hover { background: var(--brand-2); }
.bl-hero-cta .btn-ghost {
  background: transparent; border-color: var(--line-strong); color: var(--ink);
}
.bl-hero-cta .btn-ghost:hover { border-color: var(--ink); background: var(--surface-2); }
.bl-hero-cta .arr { margin-left: 8px; display: inline-block; transition: transform .2s var(--ease); }
.bl-hero-cta .btn:hover .arr { transform: translateX(4px); }
.bl-hero-img {
  margin: 0; border-radius: 16px; overflow: hidden;
  aspect-ratio: 16 / 9; background: var(--surface-2);
}
.bl-hero-img img { width: 100%; height: 100%; object-fit: cover; display: block; }

/* the "last updated / by" rule under the band */
.bl-stamp { border-bottom: 1px solid var(--line); }
.bl-stamp p {
  margin: 0; padding-block: 16px; color: var(--muted);
  font-size: 13px; font-weight: 500; letter-spacing: .5px;
}

/* ── post: body and sidebar ──────────────────────────────────────────── */
.bl-body {
  display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 320px);
  gap: clamp(32px, 4.5vw, 72px);
  padding-block: clamp(36px, 4.5vw, 64px) clamp(56px, 7vw, 104px);
  align-items: start;
}
/* The intro runs before the contents list, as it does on the reference —
   several paragraphs of context, the first set slightly larger so the page
   opens on a lede rather than straight into body copy. */
.bl-intro { margin: 0 0 18px; color: var(--muted); max-width: 68ch; text-wrap: pretty; }
.bl-intro:last-of-type { margin-bottom: clamp(28px, 3.4vw, 44px); }
.bl-intro-1 { color: var(--ink); }

.bl-toc {
  border: 1px solid var(--line); border-radius: 16px;
  background: var(--surface); padding: clamp(22px, 2.6vw, 32px);
  margin-bottom: clamp(32px, 4vw, 56px);
}
.bl-toc h2 { margin: 0 0 16px; }
.bl-toc ul { list-style: none; margin: 0; padding: 0; display: grid; gap: 10px; }
.bl-toc li { position: relative; padding-left: 20px; }
.bl-toc li::before {
  content: ''; position: absolute; left: 2px; top: .62em;
  width: 6px; height: 6px; border-radius: 50%; background: var(--brand);
}
.bl-toc a { color: var(--ink); text-decoration: none; }
.bl-toc a:hover { color: var(--brand); text-decoration: underline; }

/* anchor targets clear the floating header when a contents link is used */
.bl-sec { scroll-margin-top: calc(var(--hdr, 64px) + 28px); }
.bl-sec .abx:first-child { margin-top: 0; }

.bl-end {
  margin-top: clamp(40px, 5vw, 68px); padding-top: clamp(30px, 3.6vw, 44px);
  border-top: 1px solid var(--line);
}
.bl-end h2 { margin: 0 0 14px; }
.bl-end p { margin: 0; color: var(--muted); max-width: 66ch; }
.bl-end-cta { display: flex; flex-wrap: wrap; gap: 12px; margin-top: 24px !important; }
.bl-end-cta .arr { margin-left: 8px; }

.bl-side { position: sticky; top: calc(var(--hdr, 64px) + 20px); display: grid; gap: 18px; }
.bl-side-card {
  border: 1px solid var(--line); border-radius: 16px;
  background: var(--surface); padding: clamp(22px, 2.4vw, 28px);
}
.bl-side-card h2 { margin: 0 0 16px; }
.bl-side-card .btn { width: 100%; justify-content: center; margin-top: 20px; }
.bl-side-card .ticks { margin: 0; }
.bl-side-more ul { list-style: none; margin: 0; padding: 0; display: grid; gap: 2px; }
.bl-side-more li + li { border-top: 1px solid var(--line); }
.bl-side-more a { display: block; padding: 13px 0; text-decoration: none; }
.bl-side-more b { display: block; color: var(--ink); font-weight: 500; text-wrap: pretty; }
.bl-side-more em {
  display: block; margin-top: 5px; color: var(--muted); font-style: normal;
  font-size: 13px; letter-spacing: .5px; text-transform: uppercase;
}
.bl-side-more a:hover b { color: var(--brand); }
.bl-side-more .gd-more { margin-top: 16px; }

/* ── narrower ────────────────────────────────────────────────────────── */
@media (max-width: 1080px) {
  .bl-body { grid-template-columns: minmax(0, 1fr); }
  /* sticky is meaningless once the sidebar is a full-width block below the
     article — it would pin an already-passed panel to the top of the screen */
  .bl-side { position: static; grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 900px) {
  .bl-feat-in, .bl-hero-in { grid-template-columns: minmax(0, 1fr); }
  /* image after the copy on the featured card, before it in the post hero —
     a post's thumbnail is decoration, the index's is the entry point */
  .bl-feat-img { order: 2; }
  .bl-latest-grid { grid-template-columns: minmax(0, 1fr); }
  .bl-latest-grid .bl-num + .bl-num { border-top: 1px solid var(--line); padding-top: clamp(22px, 3vw, 34px); }
}
@media (max-width: 640px) {
  .bl-side { grid-template-columns: minmax(0, 1fr); }
  .bl-hero-cta .btn, .bl-end-cta .btn { width: 100%; justify-content: center; }
}


/* ── blog lead panel ─────────────────────────────────────────────────────
   The reference blog opens with the featured post on the left and a capture
   form on the right, both on the dark band. Same arrangement here, on this
   site's own tokens.

   The panel sits on --ink, so the shared form styling — built for a light
   surface — does not survive. Fields, the select and the button are all
   restated for the dark ground rather than patched globally. */

.bl-feat-in { align-items: start; }
.bl-feat-card {
  border: 1px solid rgba(255, 255, 255, .1); border-radius: 20px;
  background: rgba(255, 255, 255, .03); overflow: hidden;
}
.bl-feat-card .bl-feat-img { border-radius: 0; box-shadow: none; }
.bl-feat-copy { padding: clamp(22px, 2.6vw, 34px); }
.bl-feat-copy h1 { font-size: clamp(26px, 2.4vw, 34px) !important; line-height: 1.2 !important; }

/* This panel was written for a dark band. `.bl-feat { background: none }`
   removed that band when the tinted washes came off the site, and the
   panel kept its white type and white hairlines — so the whole lead form
   has been white-on-white and invisible in production ever since. The
   colours below are the theme tokens, which follow the ground instead of
   assuming one. */
.bl-lead { padding-top: clamp(4px, 1vw, 14px); }
.bl-lead h2 { margin: 0; color: var(--ink); }
.bl-lead-sub {
  margin: 8px 0 0; color: var(--brand-2);
  font-size: 17px; font-weight: 600;
}
.bl-lead-note { margin: 22px 0 18px; color: var(--muted); }

.bl-lead-row { margin: 0 0 14px; }
/* The whole control set, restated for the dark band. A 16px minimum keeps
   iOS from zooming the viewport when a field takes focus. */
.bl-lead input[type="email"],
.bl-lead input[type="tel"],
.bl-lead select {
  width: 100%; padding: 17px 18px;
  font: inherit; font-size: 16px;
  color: var(--ink); background: var(--surface);
  border: 1px solid var(--line-strong); border-radius: 12px;
  transition: border-color .2s, background .2s;
}
.bl-lead input::placeholder { color: var(--faint); }
.bl-lead input:focus, .bl-lead select:focus {
  outline: none; border-color: var(--brand-2);
  background: var(--surface);
}
.bl-lead select { appearance: none; cursor: pointer; }
/* the chevron, drawn rather than shipped as an asset */
.bl-lead select {
  background-image: linear-gradient(45deg, transparent 50%, var(--muted) 50%),
                    linear-gradient(135deg, rgba(255,255,255,.6) 50%, transparent 50%);
  background-position: calc(100% - 21px) center, calc(100% - 15px) center;
  background-size: 6px 6px, 6px 6px;
  background-repeat: no-repeat;
  padding-right: 44px;
}
/* the option list is drawn by the OS and inherits nothing — set it explicitly
   or it renders white-on-white in some browsers */
.bl-lead option { background: var(--surface); color: var(--ink); }

/* dialling code and number share a row, the code sized to its content */
.bl-lead-phone { display: grid; grid-template-columns: 116px minmax(0, 1fr); gap: 10px; }
.bl-lead-cc { padding-inline: 14px 30px !important; background-position: calc(100% - 15px) center, calc(100% - 9px) center; }

.bl-lead-opt {
  display: grid; grid-template-columns: 20px minmax(0, 1fr); gap: 12px;
  align-items: start; margin: 18px 0 22px;
}
.bl-lead-opt input { width: 20px; height: 20px; accent-color: var(--brand); margin-top: 2px; }
.bl-lead-opt label { color: var(--muted); font-size: 15px; line-height: 1.45; }

.bl-lead-go { width: 100%; justify-content: center; background: var(--brand); color: #fff; border-color: transparent; }
.bl-lead-go:hover { background: var(--brand-2); }
.bl-lead-go:disabled { opacity: .6; cursor: default; }

.bl-lead-done:empty { display: none; }
.bl-lead-done {
  margin: 14px 0 0; padding: 12px 14px; border-radius: 10px;
  background: var(--surface-2); color: var(--ink); font-size: 15px;
}
.bl-lead-done.is-bad { background: rgba(210, 60, 40, .10); color: #a3231a; }
.bl-lead.is-sent .bl-lead-row, .bl-lead.is-sent .bl-lead-opt, .bl-lead.is-sent .bl-lead-go { display: none; }

.bl-lead-fine { margin: 16px 0 0; color: var(--faint); font-size: 13px; }
.bl-lead-fine a { color: var(--muted); }

@media (max-width: 900px) {
  /* the form leads on a phone — someone who scrolled to the blog and is ready
     to enquire should not have to pass the whole featured card first */
  .bl-feat-in { grid-template-columns: minmax(0, 1fr); }
  .bl-lead { order: -1; padding-bottom: clamp(28px, 5vw, 40px); }
  .bl-feat-card .bl-feat-img { order: 0; }
}


/* ── blog toolbar: search, sort and filter beside "All posts" ───────────
   One row on the normal page ground — heading, then the search stretching
   to fill, then the sort control; the category chips sit under it and the
   grid follows. Everything filters in place, so there is no mode. */
.bl-toolbar {
  display: flex; flex-wrap: wrap; align-items: center;
  gap: 12px 16px; margin-bottom: 14px;
}
.bl-toolbar h2 { margin: 0; flex: 0 0 auto; display: flex; align-items: baseline; gap: 14px; }
#bl-q {
  flex: 1 1 260px; min-width: 200px;
  padding: 13px 16px; font: inherit; font-size: 16px;
  color: var(--ink); background: var(--surface);
  border: 1px solid var(--line); border-radius: 12px;
  transition: border-color .2s;
}
#bl-q::placeholder { color: var(--muted); }
#bl-q:focus, #bl-sort:focus { outline: none; border-color: var(--brand); }
#bl-sort {
  flex: 0 0 auto; padding: 13px 40px 13px 15px;
  font: inherit; font-size: 15px; color: var(--ink);
  background: var(--surface); border: 1px solid var(--line); border-radius: 12px;
  appearance: none; cursor: pointer;
  background-image: linear-gradient(45deg, transparent 50%, var(--muted) 50%),
                    linear-gradient(135deg, var(--muted) 50%, transparent 50%);
  background-position: calc(100% - 21px) center, calc(100% - 15px) center;
  background-size: 6px 6px, 6px 6px;
  background-repeat: no-repeat;
}
#bl-sort option { background: var(--surface); color: var(--ink); }
.bl-count { margin: 0 0 12px; color: var(--muted); font-size: 15px; }
.bl-count:empty { display: none; }
.bl-all .bl-cat-list { margin: 0 0 clamp(20px, 2.6vw, 30px); }

/* chips are buttons now — same look, button semantics */
.bl-cat { cursor: pointer; font: inherit; font-size: 15px; font-weight: 500; }
.bl-cat.is-on { border-color: var(--brand); background: var(--brand-soft); color: var(--brand); }
.bl-cat.is-on span { color: var(--brand); }

/* a hidden card must actually hide: .gd-card sets display:flex, which beats
   the UA's [hidden] rule — the same trap this stylesheet has hit before */
.gd-card[hidden] { display: none; }


/* ═══ BLOG — CORPORATE EDITORIAL SYSTEM ══════════════════════════════════
   A complete replacement for the earlier blog look, written to the register
   of a large-company engineering blog: type does the work, whitespace does
   the structuring, and the accent appears once or twice per screen.

   Placed at the end of the sheet so it outranks the earlier .bl-* rules at
   equal specificity rather than requiring !important anywhere.

   CONTRAST. A measured audit found 24 elements failing WCAG AA in light and
   5 in dark — none invisible, but muted-on-surface at 4.47:1 and the chips
   at 3.85:1 read washed out, which is what "cannot see the text" describes.
   The blog scopes its own text tokens below, each verified against the
   surface it actually sits on. Nothing here drops below 4.5:1. */

.blp, .bl-all, .ba-more {
  /* AA-safe against --surface and --bg in the theme they belong to */
  --bl-body:  var(--ink);
  --bl-muted: #574c44;          /* 6.4:1 on --surface, up from #6f6259 4.47 */
  --bl-rule:  var(--line);
}
[data-theme="dark"] .blp, [data-theme="dark"] .bl-all, [data-theme="dark"] .ba-more {
  --bl-muted: #c3b9b1;          /* 7.1:1 on the dark surface */
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .blp,
  :root:not([data-theme="light"]) .bl-all,
  :root:not([data-theme="light"]) .ba-more { --bl-muted: #c3b9b1; }
}

/* ── the two measures every element resolves to ──────────────────────── */
.ba-col      { width: min(720px, 100% - 2 * var(--gutter)); margin-inline: auto; }
.ba-col-wide { width: min(980px, 100% - 2 * var(--gutter)); margin-inline: auto; }

/* ── masthead ────────────────────────────────────────────────────────── */
.ba-head { padding-block: clamp(28px, 4vw, 56px) 0; }
.blp .bl-crumb {
  display: flex; flex-wrap: wrap; align-items: center; gap: 8px;
  margin-bottom: clamp(24px, 3vw, 36px);
  font-size: 14px; color: var(--bl-muted);
}
.blp .bl-crumb a { color: var(--bl-muted); text-decoration: none; }
.blp .bl-crumb a:hover { color: var(--brand); text-decoration: underline; }
.blp .bl-crumb span[aria-hidden] { color: var(--bl-muted); opacity: .55; }
.blp .bl-crumb [aria-current] { color: var(--bl-body); }

.ba-eyebrow {
  display: flex; align-items: center; gap: 12px; margin: 0 0 18px;
  font-size: 13px; font-weight: 600; letter-spacing: .09em; text-transform: uppercase;
  color: var(--bl-muted);
}
.ba-cat { color: var(--brand); }
.ba-dot { width: 3px; height: 3px; border-radius: 50%; background: currentColor; opacity: .55; }

.ba-head h1 {
  margin: 0; text-wrap: balance;
  font-family: var(--font-display);
  font-size: clamp(34px, 4.4vw, 52px) !important;
  line-height: 1.13 !important;
  letter-spacing: -1.4px !important;
  font-weight: 600 !important;
  color: var(--bl-body);
}
.ba-standfirst {
  margin: clamp(18px, 2.2vw, 26px) 0 0;
  font-size: clamp(19px, 1.5vw, 21px) !important;
  line-height: 1.55 !important;
  color: var(--bl-muted); text-wrap: pretty;
}

.ba-byline {
  display: flex; align-items: center; gap: 14px;
  margin-top: clamp(26px, 3vw, 36px);
  padding-block: 20px;
  border-top: 1px solid var(--bl-rule); border-bottom: 1px solid var(--bl-rule);
}
.ba-avatar {
  display: grid; place-items: center; width: 42px; height: 42px; flex: 0 0 auto;
  border-radius: 50%; background: var(--brand); color: var(--on-brand);
  font-family: var(--font-display); font-weight: 600; font-size: 19px;
}
.ba-by b { display: block; font-size: 15px; font-weight: 600; color: var(--bl-body); }
.ba-by em { display: block; margin-top: 2px; font-style: normal; font-size: 14px; color: var(--bl-muted); }

/* ── hero diagram ────────────────────────────────────────────────────── */
.ba-hero { margin: clamp(32px, 4vw, 52px) 0 0; }
.ba-hero img {
  display: block; width: 100%; height: auto;
  border-radius: 14px; border: 1px solid var(--bl-rule);
}

/* ── article body ────────────────────────────────────────────────────── */
.ba-body { padding-block: clamp(36px, 4.5vw, 60px) clamp(56px, 7vw, 96px); }

/* The reading voice: larger and looser than the site's default body, which
   is tuned for marketing copy rather than for a 1,200-word article. */
.blp .bl-main p,
.blp .bl-main li {
  font-size: 18px !important;
  line-height: 1.78 !important;
  color: var(--bl-body);
}
.blp .bl-main .abx p { margin-block: 0 1.35em; max-width: none; }
.blp .bl-intro { margin: 0 0 1.35em; color: var(--bl-body); max-width: none; }
.blp .bl-intro-1 {
  font-size: 20px !important; line-height: 1.7 !important;
  color: var(--bl-body);
}

/* Section headings: the vertical rhythm is the hierarchy — a large gap above
   and a small one below binds a heading to the text it introduces. */
.blp .bl-sec { margin-top: clamp(44px, 5vw, 68px); }
.blp .bl-sec:first-of-type { margin-top: clamp(32px, 4vw, 48px); }
.blp .bl-main .abx { margin: 0; }
.blp .bl-main .abx-t {
  margin: 0 0 .7em;
  font-family: var(--font-display);
  font-size: clamp(24px, 2.3vw, 29px) !important;
  line-height: 1.25 !important;
  letter-spacing: -.6px !important;
  font-weight: 600 !important;
  color: var(--bl-body);
  text-wrap: balance;
}

/* ── "In this article" ───────────────────────────────────────────────── */
.blp .bl-toc {
  margin: clamp(32px, 4vw, 48px) 0 0;
  padding: 26px 30px;
  border: 0; border-left: 3px solid var(--brand); border-radius: 0;
  background: var(--surface);
}
.blp .bl-toc h2 {
  margin: 0 0 14px;
  font-size: 13px !important; font-weight: 600 !important;
  letter-spacing: .09em !important; text-transform: uppercase;
  color: var(--bl-muted);
}
.blp .bl-toc ol { list-style: none; margin: 0; padding: 0; counter-reset: toc; display: grid; gap: 9px; }
.blp .bl-toc li { counter-increment: toc; position: static; padding: 0; }
.blp .bl-toc li::before {
  content: counter(toc, decimal-leading-zero);
  position: static; width: auto; height: auto; border-radius: 0; background: none;
  margin-right: 12px; color: var(--brand);
  font-size: 13px; font-weight: 600; font-variant-numeric: tabular-nums;
}
.blp .bl-toc a { color: var(--bl-body); text-decoration: none; font-size: 16px !important; }
.blp .bl-toc a:hover { color: var(--brand); text-decoration: underline; }

/* ── body blocks restyled as editorial callouts ──────────────────────── */
.blp .abx-areas { gap: 2px; margin-top: 4px; }
.blp .abx-card {
  border: 0; border-top: 1px solid var(--bl-rule); border-radius: 0;
  background: none; padding: 22px 0;
}
.blp .abx-card h3 {
  margin: 0 0 8px;
  font-size: 17px !important; font-weight: 600 !important; letter-spacing: 0 !important;
  color: var(--bl-body);
}
.blp .abx-card p { margin: 0; font-size: 17px !important; line-height: 1.7 !important; color: var(--bl-muted); }

.blp .ticks { margin-top: 6px; display: grid; gap: 12px; }
.blp .ticks li { padding-left: 32px; font-size: 17px !important; color: var(--bl-body); }
.blp .ticks li::before { top: 7px; width: 18px; height: 18px; background: var(--brand-soft); }
.blp .ticks li::after { left: 5px; top: 12px; border-color: var(--brand); }

.blp .svc-steps { counter-reset: st; margin-top: 6px; display: grid; gap: 16px; }
.blp .svc-steps li { padding-left: 46px; }
.blp .svc-steps li::before {
  content: counter(st, decimal-leading-zero); counter-increment: st;
  position: absolute; left: 0; top: 2px;
  color: var(--brand); font-size: 14px; font-weight: 600; font-variant-numeric: tabular-nums;
  background: none; width: auto; height: auto; border: 0;
}
.blp .svc-steps li p { margin: 0; }

/* ── closing CTA ─────────────────────────────────────────────────────── */
.blp .bl-end {
  margin-top: clamp(52px, 6vw, 80px); padding: clamp(30px, 3.4vw, 42px);
  border: 0; border-radius: 14px; background: var(--surface);
}
.blp .bl-end h2 {
  margin: 0 0 12px;
  font-size: clamp(22px, 2vw, 26px) !important; letter-spacing: -.5px !important;
}
.blp .bl-end p { margin: 0; color: var(--bl-muted); max-width: none; }
.blp .bl-end-cta { display: flex; flex-wrap: wrap; gap: 12px; margin-top: 24px !important; }
/* white-on-brand is 3.18:1 in dark theme; --on-brand is the token that
   flips correctly with it */
.blp .btn-primary { color: var(--on-brand); }

/* ── related reading ─────────────────────────────────────────────────── */
.ba-more { border-top: 1px solid var(--bl-rule); padding-block: clamp(44px, 5.5vw, 76px); }
.ba-more > .wrap > h2 {
  margin: 0 0 clamp(24px, 3vw, 36px);
  font-size: 13px !important; font-weight: 600 !important;
  letter-spacing: .09em !important; text-transform: uppercase;
  color: var(--bl-muted);
}

/* ── index cards, corporate ──────────────────────────────────────────── */
.bl-all .gd-card {
  border: 1px solid var(--bl-rule); border-radius: 14px;
  background: var(--surface); box-shadow: none;
  transition: border-color .2s, transform .2s var(--ease);
}
.bl-all .gd-card:hover { border-color: var(--brand); transform: none; box-shadow: none; }
.bl-all .gd-meta {
  margin: 22px 24px 0; font-size: 12px; font-weight: 600;
  letter-spacing: .09em; text-transform: uppercase; color: var(--bl-muted);
}
.bl-all .gd-card h3 { margin: 10px 24px 0; }
.bl-all .gd-card h3 a { color: var(--bl-body); }
.bl-all .gd-sum { margin: 10px 24px 0; font-size: 16px; line-height: 1.65; color: var(--bl-muted); }
.bl-all .gd-more { margin: 20px 24px 24px; font-size: 15px; }

/* the category pill: brand-on-brand-soft measured 3.85:1, so the pill now
   carries body text on the plain surface and keeps the accent as its edge */
.bl-chip, .bl-all .bl-chip {
  background: var(--surface); border-color: var(--line-strong); color: var(--bl-body);
}
.bl-all-n { color: var(--bl-muted); }

@media (max-width: 700px) {
  .ba-head h1 { font-size: 30px !important; letter-spacing: -.9px !important; }
  .blp .bl-main p, .blp .bl-main li { font-size: 17px !important; }
  .blp .bl-toc { padding: 22px 20px; }
}


/* ── landing: the blog section reveals, and reads centred ───────────────
   about.js adds .rv (opt in) then .in (play). The hidden state hangs off
   .rv, so a page without JavaScript never hides anything. */
#blog.blog-reveal .gd-head > *,
#blog.blog-reveal .bcar-track > .gd-card {
  opacity: 0; transform: translate3d(0, 22px, 0);
}
#blog.blog-reveal.in .gd-head > *,
#blog.blog-reveal.in .bcar-track > .gd-card {
  opacity: 1; transform: none;
  transition: opacity .7s var(--ease), transform .8s cubic-bezier(.16, 1, .3, 1);
}
#blog.blog-reveal.in .gd-head > *:nth-child(1) { transition-delay: .04s }
#blog.blog-reveal.in .gd-head > *:nth-child(2) { transition-delay: .12s }
#blog.blog-reveal.in .gd-head > *:nth-child(3) { transition-delay: .20s }
#blog.blog-reveal.in .gd-head > *:nth-child(4) { transition-delay: .28s }
#blog.blog-reveal.in .bcar-track > .gd-card:nth-child(1) { transition-delay: .34s }
#blog.blog-reveal.in .bcar-track > .gd-card:nth-child(2) { transition-delay: .44s }
#blog.blog-reveal.in .bcar-track > .gd-card:nth-child(3) { transition-delay: .54s }
@media (prefers-reduced-motion: reduce) {
  #blog.blog-reveal .gd-head > *, #blog.blog-reveal .bcar-track > .gd-card { opacity: 1; transform: none; transition: none; }
}

/* everything in the landing blog section sits on the centre line */
#blog .gd-card { text-align: center; align-items: center; }
#blog .gd-meta, #blog .gd-card h3, #blog .gd-sum { margin-inline: 24px; }
#blog .gd-more { align-self: center; margin-inline: 24px; }

/* ── post pages: one theme, white, with the diagram supplying the black ──
   The toggle is hidden because it cannot do anything here. */
.post-light { color-scheme: light; }

/* ── remaining WCAG AA fixes, each measured ─────────────────────────────
   .bl-num p was 4.47:1 on the tinted Latest panel; the active chip's count
   was 4.0:1 light and 3.88:1 dark; the lead button was white-on-brand at
   3.18:1 in dark, where --on-brand is the token that flips correctly. */
.bl-latest-panel .bl-num p { color: #574c44; }
[data-theme="dark"] .bl-latest-panel .bl-num p { color: #c3b9b1; }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .bl-latest-panel .bl-num p { color: #c3b9b1; }
}
.bl-cat.is-on span { color: var(--ink); }
.bl-lead-go { color: var(--on-brand); }

/* ── post pages: true white, per instruction ────────────────────────────
   The site's ground is a warm cream (--bg #f0eee6). A post page is white:
   the generated diagram is the only black on it, and the panels step down
   in the smallest increment that still separates them from the page. */
.post-light body, .post-light .blp { background: #fff; }
.post-light { background: #fff; }
.blp { background: #fff; }
.blp, .ba-more {
  --bl-body: #16130f;                 /* 17.4:1 on white */
  --bl-muted: #55504a;                /* 7.6:1 on white  */
  --bl-rule: #e4e1db;
  --surface: #fafaf9;
  background: #fff;
}
.ba-more { background: #fff; }
.blp .bl-toc { background: #fafaf9; }
.blp .bl-end { background: #fafaf9; border: 1px solid var(--bl-rule); }
.bl-all .gd-card, .ba-more .gd-card { background: #fff; }


/* ═══ LANDING: "Stay updated with Blogs" ═════════════════════════════════
   Committed dark, in both themes. The section is a black band by design —
   the card tops carry the generated diagrams, which are drawn on black, so
   a light ground would put a hard black rectangle on a cream page. Pinning
   the band makes the diagram the deepest note rather than an intrusion.

   Same reasoning the story section uses. Tokens are restated rather than
   inherited because --ink and --surface flip with the theme, and this band
   must not. */
/* The band was pinned black because the generated card diagrams were
   drawn on a dark ground, so a light band would have put a black
   rectangle on a light page. The diagrams are drawn on the page's own
   ground now (build-blog-art.mjs), so the band does not have to be a
   band — it sits on the same continuous white as everything else.

   The accents here were the pre-rebrand orange throughout. They are the
   current accent now. */
#blog { background: none; color: var(--ink); }
#blog .gd-badge {
  background: rgba(174, 14, 32, .07);
  border-color: rgba(174, 14, 32, .22);
  color: var(--accent-b);           /* 9.4:1 on white */
}
#blog .gd-head h2 { color: var(--ink); }
#blog .gd-lede { color: var(--muted); }
#blog .gd-all { color: var(--accent-a); }

#blog .gd-card {
  background: var(--surface);
  border: 1px solid var(--line);
  box-shadow: 0 1px 2px rgba(0, 0, 0, .03);
}
#blog .gd-card:hover { border-color: var(--line-strong); transform: translateY(-2px); }
#blog .gd-img { background: var(--surface-2); }
#blog .gd-meta { color: var(--muted); }
#blog .gd-card h3 a { color: var(--ink); }
#blog .gd-sum { color: var(--muted); }
#blog .gd-more { color: var(--accent-a); }

/* ── the scroll-snap track ────────────────────────────────────────────── */
.bcar { position: relative; }
.bcar-track {
  display: grid; grid-auto-flow: column;
  grid-auto-columns: calc((100% - 2 * clamp(20px, 2.4vw, 32px)) / 3);
  gap: clamp(20px, 2.4vw, 32px);
  overflow-x: auto; overscroll-behavior-x: contain;
  scroll-snap-type: x mandatory;
  /* room for the lift on hover and for focus rings, which a clipped
     scroller would otherwise cut off */
  padding: 6px 4px 14px;
  scrollbar-width: none;
}
.bcar-track::-webkit-scrollbar { display: none; }
.bcar-track > .gd-card { scroll-snap-align: start; height: 100%; }

.bcar-nav {
  position: absolute; top: 50%; z-index: 3;
  display: grid; place-items: center;
  width: 46px; height: 46px; margin-top: -23px;
  border-radius: 50%; cursor: pointer;
  background: #161413; border: 1px solid #3d3833; color: #fff;
  transition: background .2s, border-color .2s, opacity .2s;
}
.bcar-nav svg { width: 20px; height: 20px; }
.bcar-nav:hover { background: #221e1c; border-color: var(--brand); }
.bcar-nav[hidden] { display: none; }
.bcar-prev { left: clamp(-22px, -1.6vw, -8px); }
.bcar-next { right: clamp(-22px, -1.6vw, -8px); }

.bcar-dots {
  display: flex; justify-content: center; gap: 8px;
  margin: 22px 0 0;
}
.bcar-dot {
  width: 7px; height: 7px; border-radius: 50%;
  background: #4a423c; transition: background .25s, width .25s;
}
.bcar-dot.is-on { width: 22px; border-radius: 4px; background: var(--brand-2); }

@media (max-width: 980px) {
  .bcar-track { grid-auto-columns: calc((100% - clamp(20px, 2.4vw, 32px)) / 2); }
}
@media (max-width: 640px) {
  .bcar-track { grid-auto-columns: 84%; }
  /* on a phone the swipe is the control; arrows would only take room */
  .bcar-nav { display: none !important; }
}


/* ═══ HEADER — FULL WIDTH, NO SCROLL ANIMATION ═══════════════════════════
   The bar was a floating pill that condensed on scroll: inset by --hdr-side,
   max-width 1600 → 1300, radius 16 → 20, and the orange going translucent
   with a backdrop blur as content passed under. All of that is removed by
   instruction — the bar now spans the exact screen width and looks the same
   scrolled or not.

   nav.js still toggles .scrolled; it is left alone deliberately (the drawer
   reads it to close on scroll). Every rule below simply gives the class
   nothing to change, which is safer than deleting behaviour other code
   depends on. */
:root { --hdr-gap: 0px; --hdr-side: 0px; }

.site-header,
.site-header.scrolled {
  position: sticky; top: 0;
  width: 100%; max-width: none; margin: 0;
  border-radius: 0;
  transition: none;
}
.hdr-inner,
.site-header.scrolled .hdr-inner {
  padding-inline: var(--gutter);
  transition: none;
}

/* the condensed state is now identical to the resting one, in both themes */
:root:not([data-theme="dark"]) .site-header,
:root:not([data-theme="dark"]) .site-header.scrolled {
  background:
    radial-gradient(120% 200% at 6% -60%, rgba(255, 255, 255, .15) 0%, rgba(255, 255, 255, 0) 52%),
    linear-gradient(102deg, #8f0914 0%, #ae0e20 44%, #c31428 100%);
  color: #fff;
  border-bottom: 1px solid rgba(255, 255, 255, .16);
  box-shadow: none;
  backdrop-filter: none; -webkit-backdrop-filter: none;
}
:root[data-theme="dark"] .site-header,
:root[data-theme="dark"] .site-header.scrolled {
  background: var(--surface);
  border-bottom: 1px solid var(--line);
  box-shadow: none;
  backdrop-filter: none; -webkit-backdrop-filter: none;
}


/* ═══ STUDENT FAQ ════════════════════════════════════════════════════════
   This was a committed-black band, restated token by token — every colour
   pinned at id specificity, which no class-level rule could ever beat.
   The band is light now and the whole treatment lives in one place (see
   "FAQ — the ledger" further down); all that is left here are the two
   rules that have nothing to do with the rows themselves.

   Every value checked against the band; nothing is under 4.5:1. */
:is(#faq, #cfq) .kicker { color: var(--accent-a); }

/* the closing "all questions" link */
:is(#faq, #cfq) .fq-all, :is(#faq, #cfq) a.gd-all, :is(#faq, #cfq) .sec-more a { color: var(--accent-a); }

/* #fqMode is a second site-wide theme toggle (nav.js proxies it to the
   header button). Now that this band is pinned black it cannot show its own
   effect, so it would silently retheme the rest of the page while appearing
   inert — the same defect the post-page toggle had. Hidden here; the header
   toggle remains the single control. Delete this rule to bring it back. */
:is(#faq, #cfq) .fq-mode { display: none; }

/* ── the header steps aside for the full-screen story sequence ───────────
   story.js adds .is-away while that section owns the entire viewport, so
   the pinned scene is genuinely full-screen rather than being clipped by
   the bar. A short transform is the whole treatment — this is a hide, not
   a return of the condensing animation that was removed above.

   :focus-within brings it straight back for a keyboard user who tabs into
   the bar while it is away, so no control becomes unreachable. */
.site-header.is-away {
  transform: translateY(-100%);
  transition: transform .22s var(--ease);
  pointer-events: none;
}
.site-header { transition: transform .22s var(--ease); }
.site-header.is-away:focus-within {
  transform: none;
  pointer-events: auto;
}
@media (prefers-reduced-motion: reduce) {
  .site-header, .site-header.is-away { transition: none; }
}


/* ═══ CLOUD SEQUENCE — the second hero ═══════════════════════════════════
   A pinned, full-viewport canvas scrubbed by scroll. The track is what
   gives the sequence room to play; the panel inside it is what stays on
   screen. Same mechanism the story section uses, and deliberately so —
   two scroll-driven scenes on one page should behave identically. */
.cq {
  position: relative;
  height: 400vh;                 /* the scrub distance */
  /* White, like every other section. It used to be the darkest tone in the
     first cloud frame so the pin never flashed pale before the canvas
     painted; on an all-white page that flash is now the correct colour
     anyway, and a black band was the one dark edge left on the site. */
  background: var(--bg);
}
.cq-panel {
  position: sticky; top: 0;
  height: 100svh;                /* svh: a mobile toolbar must not clip it */
  overflow: hidden;
  display: grid;
}
.cq-canvas {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  display: block;
}

/* ── the copy ─────────────────────────────────────────────────────────── */
.cq-copy {
  position: relative; z-index: 2;
  align-self: end;
  padding-block: clamp(64px, 12svh, 132px);
  display: grid;                 /* every beat in one cell, so the block does
                                    not resize as the words change */
}
.cq-beat {
  grid-area: 1 / 1;
  pointer-events: none;
}
/* No transition and no class-driven opacity here. The scroll driver writes
   transform, opacity and filter inline every frame; a transition on those
   makes each rendered frame lag the scroll by its own duration, which is
   what made the fly-through read as swimming rather than scrubbing. */

/* Layout only. Every type declaration for the hero lives in the editorial
   block at the end of this file, so there is one place to read it. */
.cq-line { margin: 0; text-wrap: balance; }
.cq-sub  { margin: clamp(16px, 2vw, 24px) 0 0; text-wrap: pretty; }

/* ── progress rail ────────────────────────────────────────────────────── */
.cq-rail {
  position: absolute; z-index: 2;
  left: var(--gutter); right: var(--gutter);
  bottom: clamp(24px, 4svh, 44px);
  height: 2px; border-radius: 2px;
  background: rgba(255, 255, 255, .22);
  max-width: clamp(160px, 22vw, 300px);
}
.cq-rail i {
  display: block; height: 100%; width: 100%; border-radius: 2px;
  transform-origin: left center; transform: scaleX(var(--fill, 0));
  background: linear-gradient(90deg, var(--brand), var(--brand-2));
  transition: transform .18s linear;
}

/* ── reduced motion ───────────────────────────────────────────────────
   No scrub and no track: the panel becomes an ordinary full-height still
   on the arrival frame, with all three beats legible in sequence. */
@media (prefers-reduced-motion: reduce) {
  .cq { height: auto; }
  .cq-panel { position: relative; height: auto; min-height: 100svh; }
  .cq-beat { position: static; opacity: 1; transform: none; transition: none; }
  .cq-copy { grid-auto-flow: row; gap: clamp(36px, 6vh, 64px); }
  .cq-beat { grid-area: auto; }
  .cq-rail { display: none; }
}

@media (max-width: 700px) {
  .cq { height: 340vh; }
}


/* ═══ OPENER SWAP ════════════════════════════════════════════════════════
   The cloud sequence is now the first section and the original hero sits
   below it.

   .hero carried the opener's treatment — a negative top margin pulling it
   under the sticky header, with matching padding to put its content back.
   Left in place it would now drag the hero up over the cloud panel by the
   height of the header. The treatment moves to .cq, and .hero returns to
   ordinary flow.

   .cq needs the margin but not the padding: its panel is `sticky; top: 0`
   and already fills the viewport, and its copy is bottom-aligned, so there
   is nothing for the header to cover. */
.cq { margin-top: calc((var(--hdr) + var(--hdr-gap)) * -1); }

.hero { margin-top: 0; padding-top: clamp(40px, 6vw, 80px); }


/* ═══ CLOUD SEQUENCE — CENTRED COMPOSITION ═══════════════════════════════
   Restyled to the landing page's own standards: centred stack, the badge
   and CTA persistent, and the accent phrase carrying the brand gradient
   the hero already uses — not a second accent treatment invented for one
   section.

   Overrides the earlier bottom-left block; placed after it so it wins at
   equal specificity. */
.cq-copy {
  align-self: center;
  justify-items: center;
  text-align: center;
  display: grid;
  grid-auto-flow: row;
  gap: 0;
  padding-block: clamp(28px, 5svh, 56px);
  /* .wrap carries `margin-inline: auto`, and an auto margin on a grid item
     makes it size to max-content and centre instead of stretching — which
     shrank this to 537px, wrapped the headline one word per line and pushed
     the button below the panel. An explicit width restores the stretch;
     .wrap's max-width still caps it at the shell. */
  width: 100%;
}

/* the badge reads on a photograph, so it carries its own ground rather
   than the page's --surface */
.cq-badge {
  display: inline-flex; align-items: center; gap: 6px;
  margin: 0 0 clamp(30px, 3.8vh, 46px);
  padding: 4px 12px 4px 4px; border-radius: 999px;
}
/* The reference anchors its pill with a cluster of student photographs.
   The file that was supplied for this was three real, identifiable people
   cut from someone else's site — not ours to publish. These are our own:
   assets/people/, nine headshots already sitting in the repo unused. Same
   composition, same near-white rings, and the likenesses are ones we hold.

   96px sources for a 26px slot, so they stay sharp at 3x; ~1.7KB each. */
.cq-faces { display: inline-flex; flex: none; }
.cq-faces img {
  width: 18px; height: 18px; border-radius: 50%;
  object-fit: cover; display: block;
  border: 2px solid #fffdfc;
  box-shadow: 0 1px 3px rgba(90, 45, 20, .3);
}
.cq-faces img + img { margin-left: -7px; }   /* overlap, front-to-back */
.cq-faces img:nth-child(1) { z-index: 3; }
.cq-faces img:nth-child(2) { z-index: 2; }
.cq-faces img:nth-child(3) { z-index: 1; }

/* ── the 3D stage ────────────────────────────────────────────────────────
   One cell for all three beats, so they occupy the same spot and pass
   through each other in depth rather than displacing one another.

   PERSPECTIVE is the number that sets how hard things scale as they travel
   in Z: rendered scale is perspective / (perspective - Z), so a scene at
   Z -1000 renders at 0.50x and one approaching Z +1000 blows up and passes
   the camera. 1000px is the reference's measured value.

   Anything at Z >= perspective is behind the camera and stops rendering,
   so the driver in cloudseq.js takes opacity to 0 while Z is still short
   of 1000 — otherwise a scene pops out of existence mid-flight. */
.cq-beats {
  display: grid; width: 100%;
  perspective: 1000px;
  transform-style: flat;
}
.cq-beat {
  grid-area: 1 / 1;
  /* The <br> in the markup sets the line break, so the headline needs room,
     not a character measure. `max-width` must be cleared explicitly: the
     earlier bottom-left block set 30ch on this same class and, being a
     different property from `width`, it survived and kept capping the
     headline at 321px. */
  width: min(940px, 100%);
  max-width: none;
  justify-self: center;
  /* Opacity, transform and filter are written every frame by the scroll
     driver — no transition here, or each one would lag the scrub by its
     own duration and the text would swim behind the clouds. */
  opacity: 0;
  transform: translate3d(0, 0, -1000px);
  backface-visibility: hidden;
  pointer-events: none;
}
.cq-beat.is-on { pointer-events: auto; }

.cq-line { margin: 0 auto; }
.cq-sub  { margin: clamp(18px, 2.2vh, 26px) auto 0; }

.cq-cta {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: center;
  gap: 14px 20px;
  margin: clamp(30px, 4vh, 46px) 0 0;
}
.cq-go { padding: 13px 26px; }
/* the action earns more room to breathe once there is room to give */
@media (min-width: 768px) { .cq-go { padding: 14px 34px; } }
@media (min-width: 1200px) { .cq-go { padding: 15px 40px; } }
.cq-go:hover { transform: translateY(-1px); }

/* the rail centres under a centred stack */
.cq-rail { left: 50%; right: auto; transform: translateX(-50%); }

/* ── mobile: the reference composition ──────────────────────────────────
   Centred stack with the action full width, which is the one place a
   thumb reliably lands. */
@media (max-width: 700px) {
  .cq-cta { gap: 16px; }
  .cq-go { padding-block: 17px; }
}

/* reduced motion: the three beats stack and read in order */
@media (prefers-reduced-motion: reduce) {
  .cq-beats { grid-auto-flow: row; gap: clamp(36px, 6vh, 64px); }
  .cq-beat { grid-area: auto; }
}


/* ── header: nav centred between brand and actions ──────────────────────
   Matches the reference layout — logo left, navigation on the centre line,
   the action pair right. Auto margins on the nav do the centring, so the
   brand and the buttons keep their natural widths and nothing needs a
   fixed column. Only above the drawer breakpoint: below it the nav is
   hidden and the burger takes the right edge. */
@media (min-width: 1281px) {
  /* Centred between the brand and the action pair.
     True page-centring was tried and reverted: this nav is 752px wide (six
     entries with mega menus, against the reference's three), and centring
     it on the inner's midpoint pushed its right edge to 1088 against the
     action pair at 1080 — an 8px overlap, with the out-of-flow nav painting
     above the Sign in button and intercepting its clicks. There is only
     734px of symmetric room at 1440. Auto margins centre it in the space
     that actually exists and cannot overlap. */
  .hdr-inner > .nav-links { margin-inline: auto; }
}


/* ═══ CLOUD SEQUENCE — LIGHT COMPOSITION ═════════════════════════════════
   Dark type set directly on the bright frames. This block is composition
   only — where the copy sits and how wide it runs. Face, size, weight,
   tracking, leading and ink all come from the editorial block below. */

/* copy sits toward the top, not vertically centred */
.cq-copy { align-self: start; padding-block: clamp(72px, 13svh, 150px) 0; }

/* the accent phrase takes its own line, which is what makes it read as a
   turn in the sentence rather than a coloured word inside it */
.cq-stmt { margin: 0 auto; text-wrap: balance; }
.cq-stmt em { display: block; }

.cq-badge .ping, .cq-badge .ping::after { background: var(--ink-editorial); border-color: var(--ink-editorial); }

/* the rail, on a light ground */
.cq-rail { background: rgba(35, 24, 15, .18); }

@media (max-width: 700px) {
  .cq-copy { padding-block: clamp(56px, 11svh, 96px) 0; }
}


/* the glass lozenge: two inset rules — a grey line below the top edge and a
   white one above the bottom — which is what reads as a solid catching
   light rather than a flat pill */
.cq-go {
  border-radius: 22px;
  background: linear-gradient(to bottom, #fff 0%, rgba(255, 255, 255, .78) 100%);
  border: 1px solid rgba(255, 255, 255, .9);
  box-shadow:
    inset 0 2.26px 0 -1px #dedede,
    inset 0 -3.52px 0 -1px #ffffff,
    0 10px 30px -16px rgba(90, 45, 20, .45);
}
.cq-go:hover { background: #fff; }

/* the badge as frosted glass: a vertical white gradient and a near-white
   hairline, rather than a tinted panel */
.cq-badge {
  border: 1px solid #fffdfc;
  background: linear-gradient(to bottom, rgba(255, 255, 255, .5), rgba(255, 255, 255, .1));
  -webkit-backdrop-filter: blur(8px); backdrop-filter: blur(8px);
}


/* ═══ CLOUD HERO — EDITORIAL TYPOGRAPHY ══════════════════════════════════
   The single source of type for the hero. Everything above this point in
   the .cq blocks is layout, composition and surface chrome; every face,
   size, weight, tracking, leading and ink for hero text is here.

   THE PAIRING
     sans   Instrument Sans, self-hosted, named first — the whole hero
            except the emphasis phrases. Variable 400-700, so 500 and 600
            are real instances, not synthesised.
     serif  DM Serif Display Italic, self-hosted, used ONLY inside <em>.
            Chosen by measurement against the sans, not by name — see the
            @font-face note at the top of this file. High stroke contrast, a
            genuine italic cut, and enough weight to stand beside a 500
            grotesque rather than shrink away from it.

   WEIGHT. Nothing in the hero is bolder than 600, and the display line
   sits at 500: the contrast comes from the serif accent and from size,
   not from weight. Everything reads medium rather than heavy.

   NO GRADIENT IN TEXT. The accent was a background-clip gradient in the
   previous pass. It is a flat muted brick now — a gradient inside type
   costs legibility, forces a transparent text-fill that disappears where
   the clip is unsupported, and clips the tail of an italic glyph.

   SIZES come from the tokens in :root, all clamped, so desktop, tablet and
   mobile interpolate rather than snapping at a breakpoint. There is no
   size media query below — the clamps are the responsive system. */

/* ── badge / micro label ─────────────────────────────────────────────── */
.cq-badge {
  font-family: var(--font-sans);
  /* The line is 38 characters and fixed, so the type is sized off the
     viewport rather than clamped to a comfortable range — that is what
     keeps it on ONE row at 320 as well as at 430. It is a label on a
     photograph, not running copy; legibility here is the pill, not the
     point size. */
  font-size: min(12.5px, 3.05vw) !important;
  font-weight: 500 !important;
  line-height: var(--leading-ui) !important;
  letter-spacing: var(--tracking-label) !important;
  text-transform: none;
  color: var(--ink-primary);
  /* It used to be nowrap, which was safe while the line was three words.
     At 320px the current line needs 301px against a 296px card, and the
     type would have to drop to about 9.5px to hold one row — so it wraps
     instead. Everything from 360px up still sits on a single line. */
  text-wrap: balance;
  width: fit-content;
  max-width: 100%;
  margin-inline: auto;
}

/* ── display statement ───────────────────────────────────────────────── */
.cq-line {
  font-family: var(--font-sans);
  font-size: var(--text-display) !important;
  font-weight: 500 !important;
  line-height: var(--leading-display) !important;
  letter-spacing: var(--tracking-display) !important;
  color: var(--ink-primary);
  text-align: center;
  /* Wide enough that the sans clause holds one line; the accent takes its
     own line below it (see the emphasis rule). Without the measure the
     clause runs the full shell and the break lands mid-phrase. */
  max-width: 24ch;
  margin-inline: auto;
}

/* ── major statement (the two beats after the hero) ──────────────────── */
.cq-stmt {
  font-family: var(--font-sans);
  font-size: var(--text-h2) !important;
  font-weight: 500 !important;
  line-height: var(--leading-h3) !important;
  letter-spacing: var(--tracking-tight) !important;
  color: var(--ink-primary);
  text-align: center;
  max-width: 24ch;
}

/* ── editorial emphasis ──────────────────────────────────────────────────
   Regular weight, never bold: DM Serif Display ships one weight and a 600
   here would synthesise a fake bold and smear the strokes. Tracking is
   looser than the sans around it — a serif at display size needs the room
   a grotesque does not, and an italic needs more still. */
.cq-line em,
.cq-stmt em {
  font-family: var(--font-serif);
  font-style: italic;
  font-weight: 400;
  font-size: .96em;              /* optical match, not a numeric one: this
                                    serif carries more ink and a larger
                                    x-height than the sans, so at equal px
                                    it reads a size bigger */
  line-height: 1.0;
  letter-spacing: -.012em;
  /* The emphasis takes its own line, which is the structure the reference
     shows: a sans clause, then the editorial clause under it. As an inline
     it wrapped wherever the measure fell and split the phrase. */
  display: block;
  padding-right: .06em;          /* background-clip cuts the italic's tail
                                    without a little trailing room */
  color: var(--ink-editorial);   /* flat fallback, see @supports below */
  -webkit-text-fill-color: currentColor;
}
/* The gradient is applied only where the clip is supported, so an
   unsupporting engine keeps a real colour instead of transparent text.
   NOTE: the scene blur is applied to .cq-beat, never to this element —
   blurring a gradient-clipped span drops the gradient in Safari. */
@supports (background-clip: text) or (-webkit-background-clip: text) {
  .cq-line em, .cq-stmt em {
    background-image: linear-gradient(to right,
      var(--accent-a) 0%, var(--accent-b) 50%, var(--accent-a) 100%);
    -webkit-background-clip: text; background-clip: text;
    -webkit-text-fill-color: transparent; color: transparent;
    -webkit-box-decoration-break: clone; box-decoration-break: clone;
  }
}

/* ── supporting sentence ─────────────────────────────────────────────── */
.cq-sub {
  font-family: var(--font-sans);
  font-size: var(--text-lead) !important;
  font-weight: 400 !important;
  line-height: var(--leading-lead) !important;
  letter-spacing: var(--tracking-body) !important;
  color: var(--ink-secondary);
  text-align: center;
  max-width: 54ch;               /* inside the 55-75 character measure */
}

/* ── action label ────────────────────────────────────────────────────────
   Tripled, like the role layer's own button rule: the controls group at
   `:is(.btn, …, .gs-rail ul a, …)` carries that descendant's specificity,
   and `.btn.btn.btn` re-raises weight on top of it. A single class with
   !important loses to both. */
.cq-go.cq-go.cq-go {
  font-family: var(--font-sans) !important;
  font-size: var(--text-ui) !important;
  font-weight: 600 !important;
  line-height: var(--leading-ui) !important;
  letter-spacing: var(--tracking-body) !important;
  /* the reference's label is a deep maroon, not the page's near-black —
     set once rather than per breakpoint, so the same button is the same
     colour at every width */
  color: #6f1a0b;
  text-align: center;
}



/* ═══ HEADER OVER THE HERO ════════════════════════════════════════════════
   The bar is a solid dark panel everywhere else on the site. Over the cloud
   hero it goes clear — no ground, no shadow, no ring — so the sunrise runs
   unbroken to the top of the page and the navigation sits directly on the
   photograph, as it does in the reference.

   INK FLIPS WITH IT. The header's normal type is near-white on a dark
   panel; left alone on a bright sky it would disappear. Everything inside
   is restated in the hero's own near-black, and the two icon buttons pick
   up the same frosted-glass treatment the CTA below them already uses, so
   the whole bar belongs to the photograph rather than floating over it.

   The state is applied by cloudseq.js only while the hero is actually
   behind the bar, and never while the mobile drawer is open — the drawer
   is a solid panel and its controls need the solid header back.

   SPECIFICITY. The selectors are prefixed with :root and carry the state
   class twice. The header is painted by several theme-scoped blocks —
   `:root[data-theme="dark"] .site-header` and two `:root:not(...)` ones —
   all sitting at 0,3,0, and a plain `.site-header.on-hero` at 0,2,0 loses
   to every one of them: the bar kept its dark ground and its white type no
   matter what this block said. 0,4,0 here clears them outright, and the
   descendant rules land at 0,5,0 against their 0,4,0. Same trick the button
   role layer already uses further up. */
:root .site-header.on-hero.on-hero,
:root .site-header.on-hero.on-hero.scrolled {
  background: transparent;
  box-shadow: none;
  border-color: transparent;
  /* the wordmark is a masked graphic, so it recolours from this one value
     rather than needing a second asset */
  --logo-ink: #1c120e;
}
:root .site-header.on-hero.on-hero .brand { color: #1c120e; }

/* THE WORDMARK. The supplied artwork is a red gradient, which reads on the
   bright sky here and nowhere else: measured on the near-black bar the rest
   of the site uses, it comes out at 1.7:1. So it is used two ways from one
   file — painted in full colour over the hero, and used as its own alpha
   mask elsewhere, which is how the header lockup already worked. One asset,
   correct on every ground, and the gradient survives where it can be seen. */
.site-header .brand-mark { width: 125px; height: 40px; }
:root .site-header.on-hero.on-hero .brand-mark {
  background: url('assets/wordmark.webp') no-repeat center / contain;
  -webkit-mask: none; mask: none;
  /* The artwork is a gradient, and its light end — rgb(255,87,97) on the
     rim of the S — measures 1.09:1 against a bright cloud: those highlights
     vanish where the sky is lightest. A logo carries no contrast
     requirement, but a wordmark that dissolves at the edges looks broken.
     A shadow gives the light strokes an edge and leaves the brand colour
     exactly as supplied; recolouring it would not. */
  filter: drop-shadow(0 1px 2px rgba(70, 24, 12, .34));
}

/* navigation */
:root .site-header.on-hero.on-hero .nav-links > a,
:root .site-header.on-hero.on-hero .menu-trigger { color: #33231d; }
:root .site-header.on-hero.on-hero .nav-links > a:hover,
:root .site-header.on-hero.on-hero .menu-trigger:hover,
:root .site-header.on-hero.on-hero .menu-trigger[aria-expanded="true"] {
  color: #141414; background: rgba(28, 18, 14, .08);
}
:root .site-header.on-hero.on-hero .nav-links > .menu + .menu,
:root .site-header.on-hero.on-hero .nav-links > .menu + a,
:root .site-header.on-hero.on-hero .nav-links > a + a,
:root .site-header.on-hero.on-hero .nav-links > a + .menu { border-left-color: rgba(28, 18, 14, .18); }

/* Sign in becomes an outlined pill: the reference carries a heavier rule
   along the bottom edge only, which reads as a slight physical lift rather
   than as a flat outline */
:root .site-header.on-hero.on-hero .nav-signin {
  color: #1c120e;
  border: 1px solid rgba(28, 18, 14, .45);
  border-bottom-width: 2px;
  border-radius: 14px;
  background: transparent;
}
:root .site-header.on-hero.on-hero .nav-signin:hover { background: rgba(255, 255, 255, .5); }

/* the two icon controls take the same glass as the hero's own action */
:root .site-header.on-hero.on-hero .burger {
  color: #2a1a14;
  border: 1px solid rgba(255, 255, 255, .78);
  background: linear-gradient(to bottom, rgba(255, 255, 255, .74), rgba(255, 255, 255, .34));
  -webkit-backdrop-filter: blur(8px); backdrop-filter: blur(8px);
  box-shadow: inset 0 1.6px 0 -1px rgba(255, 255, 255, .9);
}
:root .site-header.on-hero.on-hero .burger:hover { background: rgba(255, 255, 255, .88); }
:root .site-header.on-hero.on-hero .burger .bg-tx em { color: #2a1a14; }

/* Get Started keeps the brand orange — it is the one piece of Shaventra's
   identity in the bar and the only thing that should pull the eye */


/* ═══ THE HERO CARD ═══════════════════════════════════════════════════════
   DESKTOP — the last stage of the run. The panel stops being full bleed and
   folds into a white-edged card that lifts up and away, handing the page to
   the section beneath it.

   Everything here hangs off one number, --card, written by cloudseq.js as
   the run passes 84%: 0 is full bleed, 1 is the finished card. Driving the
   corner, the ring and the rise from a single value keeps them in step —
   animating three properties on three timelines is how a fold like this
   ends up with the border arriving before the corner does.

   The ring is a box-shadow, not a border. A border would be part of the
   box and would resize the panel as it grew, dragging the canvas and the
   copy with it; a shadow paints outside the box and costs no layout. */
.cq-panel {
  /* THE HERO DOES NOT SHRINK. It gets cut.

     Scaling the panel pulled the whole hero into a small floating slab and
     resampled 318 canvas frames on the way; the reference never touches the
     element's box at all. It clips it — the element's rect is identical at
     rest and at the end — so the clouds stay at native resolution and the
     white that appears in the margin is the page itself showing through,
     not a gap the hero moved out of.

     One scalar drives all three values, which is what keeps the corner
     radius reading as a constant physical size instead of appearing to
     grow with the box:

         top : sides : radius  =  header : 12 : 21

     The top cut is not a decorative gap — it is exactly the measured
     height of the header, so the bar is uncovered flush, with no seam and
     no overlap. --cq-hdr is written by cloudseq.js from the real element;
     the fallback only matters before the first frame. */
  --cq-top:  var(--cq-hdr, 76px);
  --cq-side: 12px;
  --cq-r:    21px;
  --cq-clip: inset(
    calc(var(--card, 0) * var(--cq-top))
    calc(var(--card, 0) * var(--cq-side))
    calc(var(--card, 0) * var(--cq-side))
    round calc(var(--card, 0) * var(--cq-r)));
  -webkit-clip-path: var(--cq-clip);   /* Safari still wants the prefix */
  clip-path: var(--cq-clip);
  /* no transform, no border-radius, no shadow: a clip would cut the shadow
     off anyway, and the reference card carries none */
}
/* the fold is scrubbed, so it must not also be transitioning */
.cq-panel { transition: none; }

/* The progress rail is the sequence's own scrollbar — it says how far
   through the animation you are. Once the fold begins there is no more
   sequence to be through, so it goes out on the same value that folds the
   card, and the finished card carries no furniture. */
.cq-rail { opacity: calc(1 - var(--card, 0)); }

/* ── PHONE: full screen first, then it folds ─────────────────────────────
   The hero opens full bleed — the whole screen, the way it should be the
   moment the site loads. It is only as you scroll down that it draws in and
   rounds off into the card. Same --card value the desktop fold runs on,
   over a much shorter track: there is no text fly-through here, so the
   track exists purely to give the fold somewhere to happen.

   One beat, and it never moves. The other two are the scroll sequence's
   argument and there is no scroll sequence on a phone. */
@media (max-width: 700px) {
  .cq {
    height: 190vh;                     /* just enough to fold in */
    margin-top: calc((var(--hdr) + var(--hdr-gap)) * -1);
    background: var(--bg);
    /* Space beneath the fold. The panel is sticky and releases flush with
       the end of its track, so the finished card met the next section
       edge-to-edge and the two read as one continuous block — the card has
       to end somewhere visible for it to be a card at all. */
    padding-block: 0 clamp(26px, 7vw, 44px);
  }
  .cq-panel {
    /* THE FOLD CLEARS THE LOGO. Full bleed it is pinned at the very top and
       runs the whole screen, which is right — but the finished card was
       still pinned there, so its top edge and its white border stayed
       behind the wordmark. Both the sticky offset and the height ride
       --card: at rest the panel starts at 0 and is a full screen tall, and
       by the end it starts below the header and is exactly that much
       shorter, so the bottom edge does not push off screen as the top comes
       down.

       Derived from --hdr, so the clearance stays correct if the bar's
       height ever changes. */
    /* A CARD FROM THE FIRST PAINT. It used to open flush to all four edges
       and only become a card on the way down; it is inset from the display
       edge straight away — a small margin all round, corners already
       rounded — and the scroll takes it the rest of the way.

       So every value below has a resting figure AND a travel figure. The
       inset grows, the corner opens, the shadow deepens, and the top slides
       down until the card is clear of the wordmark. The animation is still
       there; it just no longer starts from full bleed.

       The header sits over the card's top edge at rest, which is why it is
       transparent there — the sky reads through it, as in the reference. */
    /* BOTH EDGES, TOGETHER. The top came down by 62px while the height
       only lost 58, so the bottom edge barely moved and the whole shrink
       read as the card sliding down rather than closing in. The height now
       loses TWICE what the top gains, so the bottom rises by exactly what
       the top descends and the card closes evenly from above and below.

       No ring. The border is nothing at all — the card is told from the
       page by its shadow, which is what a raised surface on white should be
       held up by anyway. */
    /* Same mechanism as the desktop, only the resting figures differ: the
       phone starts as a card already, with a small even gap on all four
       sides, and closes to the same 12px margin with the header band
       opening above it. The box is full-bleed and stays full-bleed — only
       the clip changes, so the canvas is never resized mid-scroll. */
    position: sticky;
    top: 0;
    height: 100svh;
    margin-inline: 0;
    border-radius: 0;
    transform: none;
    box-shadow: none;
    --cq-clip: inset(
      calc(6px + var(--card, 0) * (var(--cq-hdr, 64px) - 6px))
      calc(6px + var(--card, 0) * 6px)
      calc(6px + var(--card, 0) * 6px)
      round calc(16px + var(--card, 0) * 8px));
    display: grid; place-items: center;
  }
  /* The copy has to clear the CARD, not the viewport. The card's own inset
     reaches 12px, and the copy was padded 12px too — so at the end of the
     fold the headline's ink sat exactly on the rounded edge with nothing
     between them. 32px leaves a real 20px margin inside the card. */
  .cq-copy { align-self: center; padding-block: clamp(24px, 7vw, 40px); padding-inline: 32px; }
  /* 36px could not fit "Build a project you can" in what is left. The
     reference sets its headline at about 30px on this width. */
  .cq-line { font-size: clamp(27px, 21px + 2.4vw, 34px) !important; }
  .cq-sub  { font-size: 17px !important; }

  /* ── the action, to the reference ────────────────────────────────────
     A wide lozenge rather than a label-width pill: it runs to about 84%
     of the copy column, takes a heavier 2px white edge so it reads as
     glass with a lit rim, and carries the brand red rather than the page
     ink — the reference's label is a deep maroon, not near-black. */
  .cq-cta { display: flex; justify-content: center; }
  .cq-go.cq-go.cq-go {
    /* 84% of the copy column came out at 261px — 70% of a 375px screen,
       which is a slab rather than a button. 62% sits it comfortably inside
       the hero card without losing the tap target. */
    width: min(200px, 62%);
    padding: 13px 20px;
    border-radius: 18px;
    border-width: 1.5px;
  }
  .cq-beat[data-beat="1"],
  .cq-beat[data-beat="2"] { display: none; }
  .cq-rail { display: none; }
}

/* ═══ MOBILE HEADER — NO BAR, FLOATING CONTROLS ═══════════════════════════
   The bar itself disappears on a phone: no ground, no ring, no shadow, no
   edge. What is left floats — the wordmark, and the two controls as their
   own small cards with their own borders. Nothing is stretched to a bar
   and nothing spans the width, so the header reads as three objects
   sitting over the page rather than as a strip laid across it.

   The page behind it is dark here (the hero is a card below the header,
   not behind it), so the controls take a light hairline and a faint wash
   rather than the near-white glass they wear over the clouds. */
@media (max-width: 700px) {
  .site-header,
  .site-header.scrolled,
  :root[data-theme="dark"] .site-header,
  :root[data-theme="dark"] .site-header.scrolled,
  :root:not([data-theme="dark"]) .site-header:not(.on-hero),
  :root:not([data-theme="dark"]) .site-header:not(.on-hero).scrolled {
    background: transparent;
    box-shadow: none;
    border-color: transparent;
  }

  /* …but only where something sits behind it. The rule above was written
     for the landing page, where the cloud hero fills the space under the
     bar and the three floating objects read against it. Every other page
     has plain content there, so the same treatment left the wordmark and
     the burger hovering over nothing and the header looked absent.

     #ascent is the cloud hero, and it exists on the landing page alone —
     so its absence is the test for "no hero behind the bar". The id gives
     this rule a higher specificity than the transparent one above, which
     is why it does not need to repeat that selector list. */
  :root:not(:has(#ascent)) .site-header,
  :root:not(:has(#ascent)) .site-header.scrolled {
    background: var(--hdr-glass);
    border-bottom: 1px solid var(--line);
    box-shadow: 0 1px 3px rgba(0, 0, 0, .04);
  }

  /* Each control is its own card, and the cards follow the theme. They
     were first drawn for a dark page — a white hairline over a faint white
     wash — and the site now opens WHITE, which left the Menu label and the
     wordmark as pale ghosts on a pale ground. Light gets dark ink on a
     near-opaque white card; dark keeps the original glass. */
  .site-header .burger,
  :root:not([data-theme="dark"]) .site-header:not(.on-hero) .burger {
    border-radius: 15px;
    border: 1px solid rgba(28, 18, 14, .14);
    background: rgba(255, 255, 255, .78);
    color: #212121;
    -webkit-backdrop-filter: blur(12px); backdrop-filter: blur(12px);
    box-shadow: 0 8px 22px -14px rgba(40, 20, 10, .55);
  }
  /* The reference shows the mark on its own — no wordmark, no card behind
     it — with only the burger carrying a surface. The full lockup is 125px
     of type at 40px tall on a 375px screen; the mark says the same thing in
     a third of the width and reads at a glance. */
  .site-header .brand {
    padding: 0; border: 0; background: none;
    -webkit-backdrop-filter: none; backdrop-filter: none;
    box-shadow: none;
  }
  .site-header .brand-mark {
    width: 40px; height: 44px;
    background: url('assets/logo-mark.webp') no-repeat center / contain;
    -webkit-mask: none; mask: none;
  }
  /* matched selector-for-selector against the light-theme rule that knocks
     the mark out to white for the orange bar — that bar does not exist on a
     phone, and #fff on a white card is an invisible logo. Same specificity,
     later in the file, so this wins without an escalation. */
  :root:not([data-theme="dark"]) .site-header:not(.on-hero) .brand-mark,
  :root:not([data-theme="dark"]) .site-header .brand-mark { --logo-ink: var(--logo); }
  :root:not([data-theme="dark"]) .site-header .bg-tx em { color: #212121; }

  :root[data-theme="dark"] .site-header .burger {
    border-color: rgba(255, 255, 255, .26);
    background: rgba(255, 255, 255, .07);
    color: #fff;
    box-shadow: 0 8px 20px -12px rgba(0, 0, 0, .7);
  }
  :root[data-theme="dark"] .site-header .brand-mark { --logo-ink: #fff; }

  .site-header .burger:hover { border-color: rgba(28, 18, 14, .3); background: #fff; }
  :root[data-theme="dark"] .site-header .burger:hover {
    border-color: rgba(255, 255, 255, .42); background: rgba(255, 255, 255, .13);
  }

  /* the burger keeps its own width and sits at the right edge; nothing
     grows to fill the row */
  .hdr-inner > .nav-auth { display: none; }
  .site-header .burger { margin-left: auto; flex: none; }
}


/* ═══ MOBILE HEADER — PAST THE HERO ═══════════════════════════════════════
   Over the hero the floating cards are glass and the picture reads through
   them. Once the hero is behind you there is no picture to read through,
   only the page, and glass on a flat page is just a faint outline. So the
   bar inverts: it takes whichever end of the scale the page is not on, and
   is legible by being the opposite of its ground rather than a tint of it.

     white theme  →  soft near-black cards, light type
     dark  theme  →  soft near-white cards, dark type

   Neither is pure #000 or #fff — a flat black card on a white page reads as
   a hole punched in it, and pure white on near-black glares. Both are
   pulled a step in, which is what keeps the bar sitting ON the page rather
   than cut out of it. */
@media (max-width: 700px) {

  /* ── white theme: the bar goes dark ─────────────────────────────────── */
  /* Light grey past the hero, not the near-black it inverted to. The page
     underneath is white now; a black control on it reads as a hole, where a
     grey one reads as a surface sitting on the sheet. */
  :root:not([data-theme="dark"]) .site-header.past-hero .burger {
    background: #eceae6;
    border-color: rgba(20, 14, 10, .10);
    color: #221d19;
    box-shadow: 0 6px 16px -12px rgba(20, 14, 10, .35);
  }
  :root:not([data-theme="dark"]) .site-header.past-hero .brand-mark { --logo-ink: var(--logo); }
  :root:not([data-theme="dark"]) .site-header.past-hero .bg-tx em { color: #221d19; }
  :root:not([data-theme="dark"]) .site-header.past-hero .burger:hover {
    background: #e2dfda; border-color: rgba(20, 14, 10, .2);
  }

  /* ── dark theme: the bar goes light ─────────────────────────────────── */
  :root[data-theme="dark"] .site-header.past-hero .burger {
    background: #f2efec;
    border-color: rgba(20, 14, 10, .14);
    color: #1a1817;
    box-shadow: 0 10px 24px -14px rgba(0, 0, 0, .8);
  }
  :root[data-theme="dark"] .site-header.past-hero .brand-mark { --logo-ink: #1a1817; }
  :root[data-theme="dark"] .site-header.past-hero .bg-tx em { color: #1a1817; }
  :root[data-theme="dark"] .site-header.past-hero .burger:hover {
    background: #fff; border-color: rgba(20, 14, 10, .28);
  }

  /* the swap should read as a change of state, not a flicker */
  .site-header .brand,
  .site-header .burger {
    transition: background .28s var(--ease), border-color .28s var(--ease),
                color .28s var(--ease);
  }
}


/* ═══ WHY SHAVENTRA — SCROLL-DRIVEN STATE MACHINE ═════════════════════════
   Four reasons, handed off one at a time as the panel is pinned. The active
   card opens: its picture fades up and its paragraph unfolds. The other
   three hold at number and title, dimmed but never hidden, so the row reads
   as one object changing state rather than four cards taking turns.

   THE TRACK is what buys the scroll. The panel inside it is sticky, so the
   section pins on entry, spends four screens' worth of scroll changing
   state, then releases and the page carries on. Only this section pins.

   WHAT MOVES. The scroll driver writes one class and nothing else — every
   visual change here is a CSS transition off `.is-on`. Nothing is animated
   per pixel of scroll, so the handler stays cheap no matter how long the
   track is. Card width is the exception the layout genuinely needs: it is a
   four-step change with a transition, not a per-frame tween. */
/* The section is an ordinary block. It was pinned over four screens of
   scroll while the active card was chosen by scroll position; the pointer
   chooses now, so that track was four screens of nothing happening. */
.wy { position: relative; background: var(--bg); }
.wy-track { height: auto; }
.wy-panel {
  position: static;
  display: grid;
  padding-block: clamp(64px, 9vw, 132px);
  overflow: hidden;                    /* the row must not spill sideways */
}

/* ── the heading block ────────────────────────────────────────────────── */
/* flex + fit-content + auto margins. It was inline-flex with `margin: 0
   auto`, and auto margins do not centre an inline-level box — the pill sat
   hard against the left of the shell while the heading under it was
   centred. justify-self did nothing either: the parent is a block, not a
   grid. */
.wy-kicker {
  display: flex; align-items: center; gap: 8px;
  width: fit-content; margin-inline: auto;
  padding: 7px 16px; border-radius: 999px;
  border: 1px solid var(--line);
  background: var(--surface);
  font-family: var(--font-sans);
  font-size: var(--text-small); font-weight: 500;
  letter-spacing: var(--tracking-label);
  color: var(--muted);
}
.wy-h {
  margin: clamp(18px, 2.4vh, 30px) auto 0; max-width: 20ch;
  font-family: var(--font-sans);
  font-size: clamp(30px, 22px + 2.4vw, 56px) !important;
  font-weight: 500 !important;
  line-height: 1.08 !important;
  letter-spacing: var(--tracking-tight) !important;
  color: var(--ink);
  text-align: center; text-wrap: balance;
}
.wy-h em {
  font-family: var(--font-serif);
  font-style: italic; font-weight: 400;
  font-size: .97em;                    /* optical match, see the hero note */
  letter-spacing: -.012em;
  color: var(--accent-ink);
}
.wy-lede {
  margin: clamp(12px, 1.6vh, 20px) auto 0; max-width: 62ch;
  font-family: var(--font-sans);
  font-size: var(--text-lead) !important;
  font-weight: 400 !important;
  line-height: var(--leading-lead) !important;
  color: var(--muted);
  text-align: center; text-wrap: pretty;
}

/* ── the row ──────────────────────────────────────────────────────────
   Measured from the reference and matched: a card is 242x287 at rest and
   372x441 open. Only the width is animated — height follows from a fixed
   aspect-ratio, which is what keeps the two in step without a second
   tween that could drift out of sync with the first.

   The four layers inside all move on ONE curve and ONE duration, which is
   what makes the open read as a single motion rather than four things
   happening near each other. The numeral is the deliberate exception: it
   leaves on ease-out, slightly ahead of everything arriving. */
.wy-row {
  display: flex; gap: clamp(12px, 1.3vw, 22px);
  margin: clamp(34px, 6vh, 78px) 0 0; padding: 0;
  list-style: none;
  align-items: flex-start; justify-content: center;
}
.wy-card {
  position: relative;
  width: 242px; flex: 0 0 auto;
  aspect-ratio: 372 / 441;             /* height rides the width */
  display: flex; flex-direction: column; justify-content: space-between;
  padding: 19.6px;
  border-radius: 20px;
  background: #fff;
  border: 0;
  box-shadow: 2px 2px 8px 0 rgba(0, 0, 0, .05), 1px 1px 4px 0 rgba(0, 0, 0, .05);
  overflow: hidden;
  cursor: default;
  transition: width .6s cubic-bezier(.4, 0, .2, 1);
}
.wy-card.is-on { width: 372px; }
.wy-card:focus-visible { outline: 2px solid var(--accent-ink); outline-offset: 3px; }

/* ── the resting aurora ──────────────────────────────────────────────────
   Not an image and not a static gradient: a gradient ring spinning inside a
   white cut-out, the whole thing blurred and drifting. It never lands in
   the same place twice, which is why the resting cards look alive. */
/* A soft glow pinned behind the numeral. The drifting ring gives the card
   its life, but a ring has a hole in the middle and wherever that hole
   landed the number had nothing but white underneath it — chasing the ring
   around the card never fixed that. This sits under the figure and does not
   move, so the number always has colour to read against; the ring keeps
   moving on top of it. */
.wy-card::before {
  content: ''; position: absolute; inset: 0; z-index: 0; pointer-events: none;
  background: radial-gradient(56% 34% at 21% 17%,
    rgba(214, 120, 82, .48) 0%, rgba(224, 150, 110, .20) 44%, rgba(224, 150, 110, 0) 74%);
  transition: opacity .6s cubic-bezier(.4, 0, .2, 1);
}
.wy-card.is-on::before { opacity: 0; }

/* THE WORDS STAND ON WHITE. The reference is explicit about it: the colour
   lives in the top of the card and stops — the icon chip, the title and the
   paragraph are all on plain white, which is what makes the type read as
   crisply as it does there.

   A white base rather than a mask on the wash. The drifting ring is its own
   positioned element with a 27px blur, so its box is not the card's box and
   a mask on it would be cut relative to the wrong rectangle; a plate laid
   over the lower half of the card is measured in the card's own terms and
   catches both the ring and the anchored glow. It sits above them and below
   the text, and steps aside when the photograph takes over. */
.wy-card::after {
  content: ''; position: absolute; z-index: 1; pointer-events: none;
  inset: auto 0 0; height: 62%;
  background: linear-gradient(to bottom, rgba(255, 255, 255, 0) 0%, #fff 38%);
  transition: opacity .6s cubic-bezier(.4, 0, .2, 1);
}
.wy-card.is-on::after { opacity: 0; }

.wy-aura {
  position: absolute; z-index: 0; pointer-events: none;
  right: 20%; top: 40%;      /* sits behind the numeral, clears the words */
  /* The reference sets .5 over its own ground; on ours that washed out to
     almost nothing and the white numeral had no colour left to sit on.
     Raised until the numeral reads, which is the job the wash is doing. */
  /* Soft enough that the card still reads WHITE — it is a glow in the
     upper area, not a tint over the whole thing. Raised from the
     reference's .5 because the numeral needs something to sit on, but the
     first correction went to .85 and turned the cards orange. */
  opacity: .62; filter: blur(27px);
  animation: wy-float 8s ease-in-out infinite;
  transition: opacity .6s cubic-bezier(.4, 0, .2, 1);
}
.wy-aura i {
  position: relative; display: block; width: 250px; height: 250px;
  animation: wy-spin 6s linear infinite;
}
.wy-aura i::before, .wy-aura i::after { content: ''; position: absolute; border-radius: 50%; }
.wy-aura i::before { inset: 0; background: linear-gradient(to top right, #ed9c00, var(--accent-a)); }
.wy-aura i::after {
  /* The white punch-out was 175px on a 242px card, which put the hole
     directly behind the numeral in the top-left — the figure had nothing
     but white to sit on and read as barely there. Smaller hole, so colour
     carries all the way across the top of the card. */
  left: 50%; top: 50%; width: 146px; height: 146px;
  transform: translate(-50%, -50%) scaleY(1.2);
  background: #fff;
}
.wy-aura b {
  position: absolute; left: 50%; top: 50%; width: 88px; height: 88px;
  transform: translate(-50%, -50%); border-radius: 50%;
  background: linear-gradient(to top right, #ed9c00, var(--accent-a));
}
/* each card starts its drift at a different point, so the four never pulse
   together — synchronised auroras read as a loop, staggered ones read as
   weather */
.wy-card:nth-child(2) .wy-aura { animation-delay: -2s; }
.wy-card:nth-child(3) .wy-aura { animation-delay: -4s; }
.wy-card:nth-child(4) .wy-aura { animation-delay: -6s; }
.wy-card:nth-child(2) .wy-aura i { animation-delay: -1.5s; }
.wy-card:nth-child(3) .wy-aura i { animation-delay: -3s; }
.wy-card:nth-child(4) .wy-aura i { animation-delay: -4.5s; }
.wy-card.is-on .wy-aura { opacity: 0; }

@keyframes wy-spin { to { transform: rotate(360deg); } }
@keyframes wy-float {
  0%, 100% { transform: translateX(50%) translateY(-50%) scaleX(1.2); }
  50%      { transform: translateX(40%) translateY(-50%) scaleX(1); }
}

/* ── the numeral ─────────────────────────────────────────────────────────
   White type on a white card, legible only because the aurora is behind it.
   It clears on ease-out, a beat before the photograph lands. */
.wy-num {
  position: relative; z-index: 2;
  font-family: var(--font-serif); font-style: italic; font-weight: 400;
  font-size: 48px; line-height: 1;
  /* Opaque at the top, easing off at the tail — the first pass ran to .45
     and the figure all but disappeared into the wash behind it. The number
     is the card's whole identity at rest; it has to be the first thing
     read. */
  background: linear-gradient(to bottom, #fff 0%, rgba(255, 255, 255, .74) 100%);
  -webkit-background-clip: text; background-clip: text;
  -webkit-text-fill-color: transparent; color: transparent;
  transition: opacity .6s cubic-bezier(0, 0, .2, 1), filter .6s cubic-bezier(0, 0, .2, 1);
}
.wy-card.is-on .wy-num { opacity: 0; filter: blur(40px); }

/* ── the photograph ──────────────────────────────────────────────────────
   Pushes in from 2x as it fades up, which is what stops the swap reading as
   a plain cross-fade. */
.wy-media { position: absolute; inset: 0; z-index: 0; overflow: hidden; border-radius: 20px; }
.wy-shot {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: cover; display: block;
  opacity: 0; transform: scale(2); filter: blur(20px);
  transition: opacity .6s cubic-bezier(.4, 0, .2, 1),
              transform .6s cubic-bezier(.4, 0, .2, 1),
              filter .6s cubic-bezier(.4, 0, .2, 1);
}
.wy-card.is-on .wy-shot { opacity: 1; transform: none; filter: none; }
/* the words sit on white, so the picture is faded out beneath them */
/* The words sit on white, not on the photograph. The fade has to reach
   SOLID white before the title starts, or the copy is read against whatever
   the picture happens to be doing there — which is what it was doing. It
   clears by roughly the point the icon chip straddles, which is the join
   the reference shows. */
.wy-media::after {
  content: ''; position: absolute; inset: auto 0 0; height: 64%;
  background: linear-gradient(to bottom, rgba(255, 255, 255, 0) 0%, #fff 30%);
  opacity: 0; transition: opacity .6s cubic-bezier(.4, 0, .2, 1);
}
.wy-card.is-on .wy-media::after { opacity: 1; }

/* ── the words ────────────────────────────────────────────────────────── */
.wy-body { position: relative; z-index: 2; margin: 0; padding: 0; }
.wy-ic {
  position: relative; display: block; width: 48px; height: 48px;
  border-radius: 8px; padding: 7px; border: 0;
  background: linear-gradient(to bottom right, #f1e4e4, #fafafa);
  box-shadow: none;
}
.wy-ic::after {
  content: ''; position: absolute; inset: 7px;
  background: var(--accent-a);
  -webkit-mask-repeat: no-repeat; mask-repeat: no-repeat;
  -webkit-mask-size: contain; mask-size: contain;
  -webkit-mask-position: center; mask-position: center;
}
.wy-ic.mi-1::after { -webkit-mask-image: var(--i-wy1); mask-image: var(--i-wy1); }
.wy-ic.mi-2::after { -webkit-mask-image: var(--i-wy2); mask-image: var(--i-wy2); }
.wy-ic.mi-3::after { -webkit-mask-image: var(--i-wy3); mask-image: var(--i-wy3); }
.wy-ic.mi-4::after { -webkit-mask-image: var(--i-wy4); mask-image: var(--i-wy4); }

.wy-t {
  margin: 0; padding-top: 16px;
  font-family: var(--font-sans);
  font-size: 20px !important; font-weight: 500 !important;
  line-height: 1.22 !important; letter-spacing: -.02em !important;
  color: #111;
  /* the break is authored with <br>; balance would second-guess it and add
     its own, which is how a two-line title became four */
  text-wrap: nowrap;
  transition: font-size .6s cubic-bezier(.4, 0, .2, 1),
              padding-top .6s cubic-bezier(.4, 0, .2, 1);
}
.wy-card.is-on .wy-t { font-size: 24px !important; padding-top: 20px; }
.wy-t em { font-style: normal; font-weight: 500; color: var(--accent-a); }

/* Authored at 328px — WIDER than the 242px resting card — and clipped by the
   card's overflow. That is what stops the text reflowing as the card opens:
   it is already laid out at its final width and is simply revealed. */
.wy-d {
  width: 328px; max-width: 100%;
  margin: 0; padding-top: 0;
  max-height: 0; opacity: 0; filter: blur(5px);
  overflow: hidden;
  font-family: var(--font-sans);
  font-size: 18px !important; font-weight: 400 !important;
  line-height: 1.45 !important; letter-spacing: -.005em !important;
  color: #696969;
  transition: max-height .6s cubic-bezier(.4, 0, .2, 1),
              opacity .6s cubic-bezier(.4, 0, .2, 1),
              filter .6s cubic-bezier(.4, 0, .2, 1),
              padding-top .6s cubic-bezier(.4, 0, .2, 1);
}
.wy-card.is-on .wy-d { max-height: 124px; opacity: 1; filter: none; padding-top: 16px; }

/* reduced motion keeps the reveal and drops the theatre */
@media (prefers-reduced-motion: reduce) {
  .wy-aura, .wy-aura i { animation: none; }
  .wy-shot { transform: none; filter: none; }
  .wy-num { filter: none; }
}

.wy-dots { display: none; }        /* desktop has no carousel to indicate */

/* ── why us on a phone: a snap carousel, not a stack ─────────────────────
   Four cards side by side stop working long before phone widths, but a
   column of four tall cards is a lot of scrolling for what is one idea in
   four parts. So the row stays a row and becomes swipeable: one card fills
   the screen, the next is just off the edge, and the dots say where you
   are. Scroll-snap does the work — no carousel library, no JS driving the
   position, just a snap container the finger already knows how to use. */
@media (max-width: 860px) {
  .wy-panel {
    position: static; height: auto; min-height: 0;
    align-content: start; overflow: visible;
    padding-block: clamp(56px, 9vw, 96px);
    /* A grid item's min-width defaults to auto, so the shell happily grew to
       fit 1359px of cards instead of clipping them — the row measured 1399px
       wide inside a 375px screen and scrolled nothing, because there was
       nothing to scroll. This is the line that makes the carousel a
       carousel. */
    grid-template-columns: minmax(0, 1fr);
  }
  /* max-width, not just min-width. The shell was still sizing to its
     max-content — 1399px of cards inside a 375px panel — so the row had
     nothing to scroll within. Capping it at the panel is what turns the
     overflow into a scroll container. */
  .wy-panel > .wrap { min-width: 0; width: 100%; max-width: 100%; }
  .wy-row { max-width: none; }
  .wy-track { height: auto; }

  .wy-row {
    display: flex; flex-direction: row;
    /* flex-start, NOT the centre the desktop row uses. Centring a flex
       container whose content overflows pushes the overflow off BOTH ends,
       and the part that goes off the left is unreachable — scrollLeft 0 was
       already showing card two, with card one parked at x -492 where no
       amount of scrolling could bring it back. */
    justify-content: flex-start;
    height: auto;
    /* The gap has to be at least the gutter. At 16px the next card began
       4px inside the screen edge and left a visible sliver of its border —
       "only one card" means the neighbour is fully off, not nearly off. */
    gap: calc(var(--gutter) + 14px);
    margin-top: clamp(28px, 7vw, 44px);
    /* break out of the shell so a card can sit centred with its neighbours
       peeking, instead of being boxed in by the page gutter */
    margin-inline: calc(var(--gutter) * -1);
    padding-inline: var(--gutter);
    overflow-x: auto; overflow-y: hidden;
    scroll-snap-type: x mandatory;
    scroll-behavior: smooth;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
  }
  .wy-row::-webkit-scrollbar { display: none; }

  /* every card is open here — there is no hover to open one, and a card you
     have deliberately swiped to should not still be asking to be poked */
  /* One card on screen, no neighbour peeking — the card is exactly the
     width of the row's content box, so the others are fully off screen. */
  /* all four the same height, set by the tallest — a snap carousel where
     the card resizes as you swipe reads as a fault */
  .wy-row { align-items: stretch; }

  .wy-card,
  .wy-card.is-on {
    width: 100%;
    flex: 0 0 100%;
    scroll-snap-align: center;
    transition: none;
    /* THE RATIO IS GONE HERE, not capped. 372/441 is right while the card
       is 242 wide and wrong once it is the whole row — at 820 it resolved
       to 754x894 for about 210px of content, which is where the blank
       band came from. Capping that fixed the band but kept the real
       problem: a FIXED height under overflow:hidden clips whatever the
       copy actually needs, and the fourth card's description runs a line
       longer than the others, so its last line went under the edge.

       min-height keeps the card the shape it had at 375 and lets it grow
       when the words need it. Nothing is clipped at any width now. */
    aspect-ratio: auto;
    height: auto;
    min-height: 400px;
    max-height: none;
  }
  .wy-card .wy-num { opacity: 0; }
  .wy-card .wy-aura { opacity: 0; }
  .wy-card .wy-shot { opacity: 1; transform: none; filter: none; transition: none; }
  .wy-card .wy-media::after { opacity: 1; }
  .wy-card .wy-t { font-size: 21px !important; padding-top: 18px; }
  .wy-card .wy-d {
    width: auto; max-height: none; opacity: 1; filter: none;
    padding-top: 12px; font-size: 15px !important; transition: none;
  }

  /* the position indicator */
  .wy-dots {
    display: flex; justify-content: center; align-items: center; gap: 9px;
    margin: clamp(18px, 5vw, 26px) auto 0;
    padding: 9px 14px; width: fit-content;
    border-radius: 999px;
    background: #fff;
    box-shadow: 0 6px 20px -12px rgba(0, 0, 0, .35), 0 0 0 1px rgba(0, 0, 0, .05);
  }
  .wy-dot {
    width: 9px; height: 9px; padding: 0; border: 0; border-radius: 50%;
    background: #d9d6d1; cursor: pointer;
    transition: background .3s var(--ease), transform .3s var(--ease);
  }
  .wy-dot.is-on { background: var(--accent-a); transform: scale(1.18); }
}


/* reduced motion: the reveal transitions go too, everything is simply there */
@media (prefers-reduced-motion: reduce) {
  .wy-card, .wy-shot, .wy-card .wy-d, .wy-body, .wy-num { transition: none !important; }
}


/* ── mobile header: the burger is an icon, not a labelled button ─────────
   Matching the supplied reference: a plain three-line glyph in its own
   white card, no word beside it. The label is kept for assistive tech via
   the button's aria-label, which is already on it — it is only the visible
   word that goes. */
@media (max-width: 700px) {
  /* The logo and the burger were hard against the two edges at 20px. The
     hero behind them is a card with its own inset, so sitting flush to the
     glass read as two loose objects rather than a bar sitting on the card.
     28px brings both in a little — enough to sit inside the card's edge,
     not so far that the row starts to look centred. */
  .hdr-inner { padding-inline: 28px; }

  .site-header .burger .bg-tx { display: none; }
  .site-header .burger {
    /* 44px: the brand lockup opposite is 44 tall, so the pair reads as one
       row rather than two loose objects. Also the minimum touch target. */
    width: 44px; height: 44px; padding: 0;
    display: inline-flex; align-items: center; justify-content: center;
  }
  /* Three lines, not the plus the desktop button carries. The glyph in the
     markup is a cross that rotates to close; the reference shows a plain
     hamburger, so on a phone the svg is swapped for three bars drawn in
     CSS. The middle bar is the element, the outer two are its pseudos, and
     they fold into an X when the drawer opens — same affordance the cross
     had, in the shape the reference asks for. */
  .site-header .burger .bg-ic > svg { display: none; }
  .site-header .burger .bg-ic {
    position: relative; display: block; width: 20px; height: 2px;
    border-radius: 2px; background: currentColor;
    transition: background .2s var(--ease), transform .3s var(--ease);
  }
  .site-header .burger .bg-ic::before,
  .site-header .burger .bg-ic::after {
    content: ''; position: absolute; left: 0; width: 20px; height: 2px;
    border-radius: 2px; background: currentColor;
    transition: transform .3s var(--ease);
  }
  .site-header .burger .bg-ic::before { top: -6px; }
  .site-header .burger .bg-ic::after  { top: 6px; }

  /* The desktop rule turns .bg-ic 315deg so its plus glyph becomes a
     cross. Here the two bars fold into that cross themselves, so the
     parent's rotation lands on top of theirs and turns the X back into a
     plus — which is what the close button has been showing. Cancel it. */
  .site-header .burger[aria-expanded="true"] .bg-ic {
    background: transparent; transform: none;
  }
  .site-header .burger[aria-expanded="true"] .bg-ic::before { transform: translateY(6px) rotate(45deg); }
  .site-header .burger[aria-expanded="true"] .bg-ic::after  { transform: translateY(-6px) rotate(-45deg); }

  /* CLOSED, the supplied tile IS the button. It is a complete face — white
     square, rounded corners, three red bars — so the button gives up its
     own border and ground rather than framing a frame.

     OPEN, the tile goes and the CSS bars come back to fold into the X,
     because there is no closing state in the artwork and inventing one by
     rotating a hamburger would read as a spin, not a close. The button
     takes its border back at the same moment so the cross has something
     to sit on. */
  /* !important because the header restyles this button for five states —
     on-hero, past-hero, scrolled, light and dark — each at a higher
     specificity than anything scoped to a breakpoint can reach. The face
     is this tile in every one of them, so it is stated once and wins. */
  .site-header .burger[aria-expanded="false"] {
    border-color: transparent !important;
    box-shadow: none !important;
    background: url('assets/nav-menu.webp') center / 100% 100% no-repeat !important;
    -webkit-backdrop-filter: none !important; backdrop-filter: none !important;
  }
  .site-header .burger[aria-expanded="false"] .bg-ic { opacity: 0; }
  .site-header .burger[aria-expanded="false"] .ab { display: none; }
  .site-header .burger .bg-ic { transition: opacity .2s var(--ease), background .2s var(--ease), transform .3s var(--ease); }
}


/* ═══ HEADER ON WHITE ═════════════════════════════════════════════════════
   The bar was an orange gradient with white type — a strong band across the
   top of a page that is now entirely white. It reads as white too, with the
   ink flipped to match and the hairline underneath doing the separating.

   Written as one block at the end rather than by editing the orange rules
   in place: those are still what the bar looks like over a photograph, and
   the two treatments are easier to compare when they sit apart. Selectors
   are matched to the orange block's own specificity and win on order. */
:root:not([data-theme="dark"]) .site-header:not(.on-hero),
:root:not([data-theme="dark"]) .site-header:not(.on-hero).scrolled {
  background: #fff;
  color: var(--ink);
  box-shadow: 0 1px 0 0 rgba(20, 14, 10, .07);
}

/* the wordmark returns to its own colour */
:root:not([data-theme="dark"]) .site-header:not(.on-hero) .brand { color: var(--ink); }
:root:not([data-theme="dark"]) .site-header:not(.on-hero) .brand-mark { --logo-ink: var(--logo); }

/* navigation */
:root:not([data-theme="dark"]) .site-header:not(.on-hero) .nav-links > a,
:root:not([data-theme="dark"]) .site-header:not(.on-hero) .menu-trigger { color: #1C1C1C; }
:root:not([data-theme="dark"]) .site-header:not(.on-hero) .nav-links > a:hover,
:root:not([data-theme="dark"]) .site-header:not(.on-hero) .menu-trigger:hover,
:root:not([data-theme="dark"]) .site-header:not(.on-hero) .menu-trigger[aria-expanded="true"] {
  color: #141414; background: rgba(28, 18, 14, .06);
}
:root:not([data-theme="dark"]) .site-header:not(.on-hero) .nav-links > .menu + .menu,
:root:not([data-theme="dark"]) .site-header:not(.on-hero) .nav-links > .menu + a,
:root:not([data-theme="dark"]) .site-header:not(.on-hero) .nav-links > a + a,
:root:not([data-theme="dark"]) .site-header:not(.on-hero) .nav-links > a + .menu {
  border-left-color: rgba(28, 18, 14, .12);
}
:root:not([data-theme="dark"]) .site-header:not(.on-hero) .menu-trigger.is-empty:hover {
  color: #1C1C1C; background: none;
}

/* the action pair: Sign in outlined, Get Started keeps the brand */
:root:not([data-theme="dark"]) .site-header:not(.on-hero) .nav-signin {
  color: #1c120e;
  border: 1px solid rgba(28, 18, 14, .22);
  border-bottom-width: 2px;
  border-radius: 14px;
  background: transparent;
}
:root:not([data-theme="dark"]) .site-header:not(.on-hero) .nav-signin:hover {
  background: rgba(28, 18, 14, .05);
}
:root:not([data-theme="dark"]) .site-header:not(.on-hero) .nav-start {
  background: var(--brand); color: var(--on-brand);
}
:root:not([data-theme="dark"]) .site-header:not(.on-hero) .nav-start:hover {
  box-shadow: 0 8px 20px -8px var(--brand);
}
/* Desktop only. The phone gives the burger its own floating card — glass
   over the hero, light grey past it — and this rule sits later in the file,
   so unscoped it simply erased that. */
@media (min-width: 701px) {
  :root:not([data-theme="dark"]) .site-header:not(.on-hero) .burger {
    border-color: rgba(28, 18, 14, .16);
    background: rgba(28, 18, 14, .03);
    color: #1c120e;
  }
}


/* ═══ THE HERO CARD LANDS BELOW THE HEADER ════════════════════════════════
   The fold's destination changed. It used to hold the top edge flush and
   round only the base, so the finished shape was still tucked behind the
   bar. It now comes to rest as a complete box UNDER the header — clear of
   it on every side, all four corners, the white edge visible the whole way
   round. The hero reads as a panel the page is holding, not as a backdrop
   the header is sitting on.

   `--hdr` puts the top edge below the bar at whatever height the bar
   happens to be, rather than a number that would be wrong the moment the
   header changed. */
@media (min-width: 701px) {
  /* the same breathing room under the desktop fold */
  .cq { padding-bottom: clamp(20px, 3vh, 40px); }

}


/* ═══ MOBILE HERO HEADER — CLEAR OVER THE SKY ═════════════════════════════
   Matching the supplied reference: the bar itself carries nothing, the mark
   sits dark on the left, the burger sits in its own pale card on the right.
   No entrance, no fade — it is simply there from the first paint. */
@media (max-width: 700px) {
  .site-header:not(.past-hero),
  .site-header:not(.past-hero).scrolled,
  :root:not([data-theme="dark"]) .site-header:not(.past-hero),
  :root:not([data-theme="dark"]) .site-header:not(.past-hero).scrolled {
    background: transparent;
    box-shadow: none;
    border-color: transparent;
  }

  /* The mark is masked rather than painted, so its colour can follow the
     ground it is standing on. Over the sky it is the deep brand red the
     reference shows; past the hero it returns to the brand orange. */
  .site-header .brand-mark {
    background: var(--logo-ink, var(--logo));
    -webkit-mask: url('assets/logo-mark.webp') no-repeat center / contain;
    mask: url('assets/logo-mark.webp') no-repeat center / contain;
  }
  :root:not([data-theme="dark"]) .site-header:not(.past-hero) .brand-mark { --logo-ink: #6f1a0b; }
  :root:not([data-theme="dark"]) .site-header.past-hero .brand-mark { --logo-ink: var(--logo); }

  /* the burger keeps its pale card over the sky */
  :root:not([data-theme="dark"]) .site-header:not(.past-hero) .burger {
    border: 1px solid rgba(255, 255, 255, .85);
    background: linear-gradient(to bottom, rgba(255, 255, 255, .88), rgba(255, 255, 255, .62));
    color: #6f1a0b;
    -webkit-backdrop-filter: blur(10px); backdrop-filter: blur(10px);
    box-shadow: 0 8px 20px -12px rgba(90, 30, 12, .5);
  }

  /* ── the glyph: two full bars and a shorter third ────────────────────
     Straight from the reference — the bottom line stops a little short of
     the other two. Only a little: enough to read as drawn rather than
     stacked, not so much that it looks like a different icon. */
  .site-header .burger .bg-ic::after { width: 15px; }

  /* ── the dots are a control, not part of the card ────────────────────
     A white pill on a white page has no edge of its own, so at 30px below
     a card with a dark photograph in it the dots read as hanging off the
     card rather than as the thing that moves it. Two changes: real
     separation, and a ground the pill can be seen on. */
  .wy-dots {
    margin-top: clamp(44px, 11vw, 60px);
    background: var(--pr-chip);
    box-shadow: none;
    border: 1px solid var(--line);
  }
  .wy-dot { background: #fff; box-shadow: inset 0 0 0 1px rgba(0, 0, 0, .06); }
  .wy-dot.is-on { background: var(--accent-a); box-shadow: none; }
}


/* ═══════════════════════════════════════════════════════════════════════
   HOW THE WORK RUNS — pinned five-panel scrollytelling
   ─────────────────────────────────────────────────────────────────────
   Pin distance is 5 × 400px. Panel i owns p ∈ [i, i+1); everything below
   reads d = p − i − 0.5, so d = 0 is "this panel is centred" and |d| = .5
   is a handoff boundary. JS writes --p; CSS does the rest.

   The left column moves and the right column does not. Keeping the
   illustration perfectly still while the copy travels is what makes five
   separate diagrams read as one workbench being re-dressed.
   ══════════════════════════════════════════════════════════════════════ */

.pr {
  position: relative;
  background: var(--pr-ground);
  color: var(--pr-ink);
}
.pr-pin {
  position: sticky;
  top: 0;
  height: 100svh;
  display: grid;
  /* An implicit `auto` column sizes to max-content, and the content here
     is a 690px illustration frame — so the track inflated to 690 and the
     wrap's `min(…, 100% - 36px)` resolved against THAT, not the viewport.
     On a 375px phone the stage came out 640 wide. minmax(0, 1fr) pins the
     track to the pin's own width and the frame overflows into the clip,
     which is what it was always supposed to do. */
  grid-template-columns: minmax(0, 1fr);
  align-content: center;
  overflow: clip;
}
.pr-wrap {
  width: min(1180px, 100% - 48px);
  margin-inline: auto;
}

/* ── heading ─────────────────────────────────────────────────────────── */
/* the scrub lives on the track, not the section */
.pr-track {
  position: relative;
  height: calc(100svh + 2000px);
}

/* ── the heading rides with the animation ─────────────────────────────
   It used to sit in its own wrap ABOVE the pin, with 64–104px of padding
   between it and the stage, and it scrolled away for good on the first
   push. It is inside the pin now, directly on top of the stage, and it
   fades out as the scrub leaves the first panel and fades back in when
   you return to it — so the title is present exactly while you are at
   stage one and out of the way for the other four.

   It keeps its height while faded on purpose: collapsing it would shift
   the stage mid-scrub, and a diagram that jumps while you are reading it
   is worse than a little air above it. */
.pr-head {
  text-align: center;
  margin-bottom: clamp(14px, 2vh, 26px);
  opacity: var(--pr-head-a, 1);
  /* no transition: the value is scrubbed by scroll, and easing a scrubbed
     property makes every frame lag the scroll */
  will-change: opacity;
}
.pr-head[aria-hidden="true"] { pointer-events: none; }
.pr-kicker {
  display: flex; align-items: center; justify-content: center; gap: 8px;
  margin: 0 0 12px;
  font: 500 var(--text-small)/1 var(--font-sans);
  letter-spacing: .12em; text-transform: uppercase;
  color: var(--pr-ink-3);
}
.pr-kicker .ab-star { color: var(--accent-ink); font-size: 1.05em; }
.pr-h {
  margin: 0;
  font: 400 clamp(28px, 22px + 1.5vw, 44px)/1.1 var(--font-sans);
  letter-spacing: -.02em;
  color: var(--pr-ink);
  text-wrap: balance;
}
.pr-h em { font-family: var(--font-serif); font-style: italic; font-weight: 400; }

/* ── stage ───────────────────────────────────────────────────────────── */
.pr-stage {
  display: grid;
  grid-template-columns: 4px minmax(0, .92fr) minmax(0, 1.25fr);
  align-items: center;
  gap: clamp(24px, 3.4vw, 56px);
}

/* ── rail ─────────────────────────────────────────────────────────────
   It used to be a five-slot index: an 80px block (one fifth of the rail)
   driven by --pr-i, the INTEGER panel number, easing between five fixed
   stops. That is why it read as stuck — it sat still through most of a
   panel and then jumped, so it told you which slide you were on but
   never that you were moving.

   It now rides --p, the continuous scrub position, so it tracks the
   scroll frame for frame. Smaller too, so it reads as a marker sliding
   along a track rather than a block filling a fifth of it.

   No transition on purpose: the value is already scrubbed by scroll, and
   easing a scrubbed property makes every frame lag the scroll — the same
   reason the hero's header alpha carries none. */
.pr-rail {
  position: relative;
  width: 4px; height: 400px;
  border-radius: 2px;
  background: var(--pr-rail);
  justify-self: start;
}
.pr-thumb {
  position: absolute; left: 0; width: 4px; height: 56px;
  border-radius: 2px;
  background: linear-gradient(180deg, var(--accent-a), var(--accent-b));
  /* (--p − .5) runs 0 → 4 across the five panels, and (400 − 56) / 4 = 86
     is the travel per panel */
  transform: translateY(calc((var(--p, .5) - .5) * 86px));
  will-change: transform;
}

/* ── left: the filmstrip ─────────────────────────────────────────────── */
/* a masked window, not an open column: the outgoing panel has to
   dissolve at the edges rather than sail up into the section heading */
.pr-copy {
  position: relative;
  height: clamp(420px, 78svh, 660px);
  overflow: hidden;
  --m: linear-gradient(to bottom, transparent 0, #000 11%, #000 89%, transparent 100%);
  -webkit-mask-image: var(--m); mask-image: var(--m);
}
.pr-list {
  list-style: none; margin: 0; padding: 0; height: 100%;
  --pr-travel: clamp(300px, 46svh, 450px);
}
.pr-item {
  position: absolute; inset: 0;
  display: flex; flex-direction: column; justify-content: center;
  max-width: 30rem;
  /* d = distance from centred, in panels */
  --d: calc(var(--p, 0) - var(--i) - .5);
  --ad: max(var(--d), calc(var(--d) * -1));
  /* the reference travels ±450 in a stage with nothing above it; here a
     heading shares the pin, so travel is scaled to the masked window and
     only the distance changes — the tween shape is the same */
  transform:
    translateY(calc(var(--d) * -1 * var(--pr-travel, 200px)))
    scale(calc(1 - var(--ad) * .2));         /* .8 → 1 → .8 */
  /* linear over the full ±1 life: neighbours sum to 1 at every handoff,
     so the column never blinks empty the way a ±.5 life does */
  opacity: clamp(0, calc(1 - var(--ad)), 1);
  will-change: transform, opacity;
}
.pr-item[data-i="0"] { --i: 0 } .pr-item[data-i="1"] { --i: 1 }
.pr-item[data-i="2"] { --i: 2 } .pr-item[data-i="3"] { --i: 3 }
.pr-item[data-i="4"] { --i: 4 }

/* The reference has no display headline: the QUESTION is the headline,
   set large in the serif italic and in the deep red. Under it a bold
   lead-in runs straight into the body, then a dashed rule, then the
   payoff under a plain bold heading. This was already the treatment on
   the phone; it is the treatment everywhere now, and the breakpoint only
   changes the sizes. */
.pr-q {
  margin: 0 0 20px;
  font: 400 clamp(26px, 19px + 1.5vw, 36px)/1.14 var(--font-serif);
  font-style: italic;
  color: var(--accent-b);
  text-wrap: balance;
}
.pr-t {
  margin: 0 0 6px;
  font: 600 clamp(17px, 15px + .35vw, 19px)/1.45 var(--font-sans);
  letter-spacing: 0;
  color: var(--pr-ink);
}
.pr-t em { font-style: italic; font-weight: 600; }
.pr-p {
  margin: 0;
  padding-bottom: clamp(18px, 2vw, 26px);
  font: 400 clamp(16px, 14px + .4vw, 18px)/1.6 var(--font-sans);
  color: var(--pr-body);
}
/* the rule the reference sets between the body and the payoff */
.pr-lab {
  margin: 0 0 10px;
  padding-top: clamp(18px, 2vw, 26px);
  font: 600 clamp(16px, 14px + .35vw, 18px)/1.4 var(--font-sans);
  letter-spacing: 0; text-transform: none;
  color: var(--pr-ink);
  background-image: repeating-linear-gradient(to right,
    var(--pr-rail) 0 7px, transparent 7px 15px);
  background-size: 100% 1.5px;
  background-repeat: no-repeat;
  background-position: top left;
}
.pr-b {
  margin: 0; padding-left: 0;
  font: 400 clamp(16px, 14px + .4vw, 18px)/1.6 var(--font-sans);
  color: var(--pr-body);
}

/* ── right: cross-fade only, never moves ─────────────────────────────── */
.pr-art { position: relative; height: clamp(360px, 74svh, 585px); display: grid; place-items: center; }
.pr-frame {
  /* Centred by transform, NOT by the grid. A 690px frame always overflows
     its column once the column is narrower than that, and `place-items:
     center` deliberately degrades to start-edge alignment when an item
     overflows — so on a phone the whole illustration sat to the right of
     the column and ran off the screen. Absolute + translate(-50%) centres
     it on the true middle at any scale. */
  position: absolute;
  left: 50%; top: 50%;
  width: 690px; height: 585px;                 /* authored size; JS scales */
  transform: translate(-50%, -50%) scale(var(--pr-k, 1));
  transform-origin: 50% 50%;
}
.pr-fig {
  position: absolute; inset: 0; margin: 0;
  --d: calc(var(--p, 0) - var(--i) - .5);
  --ad: max(var(--d), calc(var(--d) * -1));
  /* A long plateau, then a fast wipe. Two diagrams held at 50% each read
     as mud, so the pair deliberately dips below a sum of 1 and lets the
     ground show through for a moment — a swap, not a dissolve. */
  opacity: clamp(0, calc((.54 - var(--ad)) / .16), 1);
  will-change: opacity;
}
.pr-fig[data-i="0"] { --i: 0 } .pr-fig[data-i="1"] { --i: 1 }
.pr-fig[data-i="2"] { --i: 2 } .pr-fig[data-i="3"] { --i: 3 }
.pr-fig[data-i="4"] { --i: 4 }

/* four edge fades, one element — same render, a quarter of the DOM */
.pr-fade {
  position: absolute; inset: -2px; z-index: 30; pointer-events: none;
  background:
    linear-gradient(to right,  var(--pr-ground), transparent 8%),
    linear-gradient(to left,   var(--pr-ground), transparent 8%),
    linear-gradient(to bottom, var(--pr-ground), transparent 8%),
    linear-gradient(to top,    var(--pr-ground), transparent 8%);
}

/* ── illustration primitives ─────────────────────────────────────────── */
.pr-el { position: absolute; }
.pr-card {
  background: var(--pr-card);
  border: 1px solid var(--pr-rail);
  border-radius: 14px;
  box-shadow: var(--pr-shadow), var(--pr-bevel);
  padding: 16px 18px;
}
.pr-chip {
  display: inline-flex; align-items: center; gap: 8px;
  padding: 9px 14px; border-radius: 999px;
  background: var(--pr-card);
  border: 1px solid var(--pr-rail);
  box-shadow: var(--pr-shadow), var(--pr-bevel);
  font: 500 13px/1.35 var(--font-sans);
  color: var(--pr-ink-2);
  white-space: nowrap;
}
.pr-dot {
  flex: none; width: 7px; height: 7px; border-radius: 50%;
  background: var(--c, var(--pr-idle));
}
.pr-card-h {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  padding-bottom: 12px; margin-bottom: 12px;
  border-bottom: 1px solid var(--pr-rail);
}
.pr-ttl { font: 500 14px/1.2 var(--font-sans); color: var(--pr-ink); }
.pr-tag {
  padding: 4px 9px; border-radius: 6px;
  font: 500 11px/1 var(--font-sans); letter-spacing: .04em;
  color: var(--c); background: color-mix(in srgb, var(--c) 10%, transparent);
}
.pr-mini {
  display: block; margin-bottom: 8px;
  font: 500 11px/1 var(--font-sans);
  letter-spacing: .12em; text-transform: uppercase; color: var(--pr-ink-3);
}
.pr-rows { list-style: none; margin: 0; padding: 0; display: grid; gap: 10px; }
.pr-rows li {
  display: flex; align-items: center; gap: 10px;
  font: 400 13px/1.3 var(--font-sans); color: var(--pr-ink-2);
}
.pr-rows li b {
  margin-left: auto; font: 500 11px/1 var(--font-sans);
  color: var(--pr-ink-3); font-variant-numeric: tabular-nums;
}
.pr-rows-lg li { font-size: 13.5px; }
   /* superseded by .pr-wire */
.pr-card-f {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  margin-top: 14px; padding-top: 12px; border-top: 1px solid var(--pr-rail);
}
.pr-card-f .pr-mini { margin: 0; }
/* "Cut from scope" — what the stage deliberately dropped. This used to
   be struck-through text, which at 11.5px reads as a rendering fault
   rather than as a decision. An explicit x carries the same meaning and
   leaves the words legible. */
.pr-strike {
  display: inline-flex; align-items: center; gap: 5px;
  padding: 3px 9px 3px 7px; border-radius: 5px; background: var(--pr-chip);
  font: 400 11.5px/1 var(--font-sans); color: var(--pr-ink-3);
}
.pr-strike::before {
  content: "\00d7";
  font-size: 12.5px; line-height: 1; color: var(--accent-a);
}

.pr-cmp { list-style: none; margin: 0; padding: 0; display: grid; gap: 16px; }
.pr-cmp li { display: grid; grid-template-columns: 148px 1fr auto; align-items: center; gap: 12px; }
.pr-nm { font: 400 13px/1.25 var(--font-sans); color: var(--pr-ink-2); }
.pr-cmp .is-pick .pr-nm { font-weight: 500; color: var(--pr-ink); }
.pr-bar { height: 7px; border-radius: 4px; background: var(--pr-chip); overflow: hidden; }
.pr-bar i { display: block; height: 100%; border-radius: 4px; background: var(--c); }
.pr-vd { font: 400 11.5px/1 var(--font-sans); color: var(--pr-ink-3); white-space: nowrap; }
.pr-quote {
  margin: 0;
  font: 400 14px/1.55 var(--font-serif); font-style: italic;
  color: var(--pr-ink-2);
}

.pr-log { list-style: none; margin: 0; padding: 0; display: grid; gap: 9px; }
.pr-log li { font: 400 12.5px/1.35 var(--font-sans); color: var(--pr-body); }
.pr-log b {
  display: inline-block; min-width: 42px;
  font: 500 11px/1 var(--font-sans); color: var(--pr-ink-3);
  font-variant-numeric: tabular-nums;
}

.pr-pack { display: grid; grid-template-columns: 196px 1fr; gap: 16px; }
.pr-files { list-style: none; margin: 0; padding: 0; display: grid; gap: 6px; align-content: start; }
.pr-files li {
  display: flex; align-items: center; gap: 9px;
  padding: 8px 10px; border-radius: 8px;
  font: 400 12.5px/1.2 var(--font-sans); color: var(--pr-ink-2);
}
.pr-files li.is-on { background: var(--pr-chip); font-weight: 500; color: var(--pr-ink); }
.pr-files li b {
  margin-left: auto; font: 500 10.5px/1 var(--font-sans); color: var(--pr-ink-3);
}
.pr-page {
  display: grid; gap: 9px; align-content: start;
  padding: 16px; border-radius: 10px;
  background: var(--pr-chip);
}
.pr-pl { height: 7px; border-radius: 4px; background: #dedede; }
.pr-pl.w40 { width: 40% } .pr-pl.w58 { width: 58% } .pr-pl.w70 { width: 70% }
.pr-pl.w76 { width: 76% } .pr-pl.w88 { width: 88% } .pr-pl.w92 { width: 92% }
.pr-pl.w95 { width: 95% }
.pr-pfig {
  height: 58px; margin: 4px 0; border-radius: 6px;
  background: repeating-linear-gradient(-45deg, #e4e4e4 0 6px, #dedede 6px 12px);
}

.pr-hub {
  left: 269px; top: 217px;
  display: grid; gap: 3px; justify-items: center;
  width: 152px; padding: 20px 12px;
  border-radius: 50%; aspect-ratio: 1; place-content: center;
  background: var(--pr-card);
  border: 1px solid var(--pr-rail);
  box-shadow: var(--pr-shadow), var(--pr-bevel);
}
.pr-hub-k {
  font: 500 10.5px/1 var(--font-sans);
  letter-spacing: .12em; text-transform: uppercase; color: var(--pr-ink-3);
}
.pr-hub-v { font: 400 21px/1 var(--font-serif); font-style: italic; color: var(--pr-ink); }

/* ── narrow: the SAME pin, stacked, with the rail across the top ───────
   The section used to unpin below 900 and become a static list. It stays
   pinned now — same scrub, same filmstrip, same cross-fade — and only the
   arrangement changes: the two columns become two rows, and the rail turns
   on its side.

   The rail is the one piece that is genuinely different rather than merely
   reflowed. On the desktop it is a vertical five-slot index; here it runs
   across the top and the thumb steps left to right, one slot per panel.
   Same idea, read along the axis a phone actually has room for. */
@media (max-width: 900px) {
  .pr-wrap { width: min(640px, 100% - 36px); }

  /* The stage centres in the pin, and the pin starts at the top of the
     viewport — which is where the sticky header already is. Without this
     the rail landed at y35, under a 65px bar, and was simply invisible. */
  .pr-pin { padding: calc(var(--hdr) + 14px) 0 12px; }

  /* rows, not columns — and the illustration leads, the reading follows */
  .pr-stage { display: flex; flex-direction: column; gap: clamp(8px, 1.4vh, 14px); }
  /* Copy first, illustration under it — the reference reads top-down:
     the question, the answer, then the picture of it. I had the picture
     leading, which put the diagram before the sentence explaining it. */
  .pr-rail { order: 0; }
  .pr-copy { order: 1; }
  .pr-art  { order: 2; }

  /* the five-slot index, laid on its side */
  .pr-rail {
    width: 100%; height: 8px;
    justify-self: stretch;
    border-radius: 4px;
  }
  .pr-thumb {
    left: 0; top: 0;
    width: 12%; height: 8px;
    background: linear-gradient(90deg, var(--accent-a), var(--accent-b));
    /* continuous, like the desktop rail. The thumb is 12% of the track,
       so the 88% of travel is 22% of the track per panel — which is
       183.333% of the thumb's own width. */
    transform: translateX(calc((var(--p, .5) - .5) * 183.333%));
    will-change: transform;
  }

  /* Both need an explicit width. `.pr-stage` inherits `align-items: center`
     from the base rule, so as a COLUMN flex container it sizes its items to
     their content — and every .pr-item is absolutely positioned, so the copy
     column had no in-flow content at all and measured 0 wide. */
  /* enough height that the frame is limited by WIDTH, not height — 585 ×
     (339/690) = 287, so anything above that is wasted on letterboxing */
  .pr-art  { width: 100%; height: clamp(180px, 31svh, 250px); }
  .pr-copy { width: 100%; height: clamp(300px, 46svh, 375px); }

  /* ── the heading, on a pin that has no spare height ─────────────────
     The title now lives INSIDE the pin so it can fade with the scrub.
     On a phone that is 109px the stage does not have: at 812 the pin
     holds 721 usable and the stage already wanted 718. The eyebrow goes
     (the h2 says the same thing in more words), the heading tightens,
     and the two boxes give back the rest. Measured, not guessed. */
  .pr-kicker { display: none; }
  .pr-head { margin-bottom: clamp(8px, 1.2vh, 12px); }
  .pr-stage { gap: clamp(6px, 1vh, 10px); }
  .pr-item { max-width: none; }

  /* The panels are set at desktop sizes in a 339px column, where the
     tallest ran to 367px against a 357px window — taller than the box that
     was meant to hold it. Tightened here so a whole panel is readable at
     once, which is the only thing the filmstrip is for. */
  /* the phone keeps the same structure — only the sizes come down */
  .pr-q { margin-bottom: 16px; }
  .pr-t { margin-bottom: 6px; }

  /* a narrower fade band: on a short window an 11% bite off each end eats
     into the panel itself rather than just its neighbours */
  /* a 5% bite instead of 8%: the panels are taller now and an 8% fade
     was eating the top and bottom lines of the panel itself */
  .pr-copy { --m: linear-gradient(to bottom, transparent 0, #000 5%, #000 95%, transparent 100%); }

  /* travel has to be at least the panel height or the outgoing and incoming
     panels overlap at the handoff — that is what made the desktop column
     unreadable before it was measured */
  /* travel must be at least the tallest panel or the outgoing and incoming
     panels overlap at the handoff — the bigger type pushed that to 351 */
  .pr-list { --pr-travel: clamp(300px, 46svh, 400px); }
}

@media (prefers-reduced-motion: reduce) {
  .pr-thumb { transition: none; }
  /* the panels still change on scroll — they just stop travelling */
  .pr-item { transform: none; }
}


/* ═══════════════════════════════════════════════════════════════════════
   THE FIGURES — measured off the reference at 375px
   (prefixed .nums, not .st: `.st` is already the story stage, which is
    position:absolute and opacity:0 — the section inherited both.)
   ─────────────────────────────────────────────────────────────────────
   kicker  14/21  w400  #696969, in a pill
   heading 28/32.2 w500, the accent word serif italic
   sub     16/19.68 w400 #696969, centred
   number  48/48  w400 serif, gradient-filled
   label   18/27  w600 #212121, right, sharing the number's baseline
   note    16/24  w400 #696969, left, on its own line
   rule    1.5px, 7px dash / 8px gap, #E7E7E7, 24px beneath

   The row is `space-between` with `align-items: flex-end`, which is what
   drops the small label onto the big number's baseline instead of its
   box centre. No entry animation — the reference has none, verified at
   opacity 1 / transform none through the whole scroll-in.
   ══════════════════════════════════════════════════════════════════════ */

.nums {
  background: var(--pr-ground);
  padding: var(--sec-y) 0;
}
/* wide enough for three columns of prose; the heading and lede keep a
   narrow measure of their own so they do not stretch with it */
.nums-wrap { width: min(1060px, 100% - 40px); margin-inline: auto; }

.nums-kicker {
  display: block; width: fit-content; margin: 0 auto 18px;
  padding: 7px 18px; border-radius: 999px;
  background: var(--pr-chip);
  font: 400 14px/21px var(--font-sans);
  color: var(--pr-body);
  text-align: center;
}
.nums-h {
  margin: 0 0 14px;
  font: 500 clamp(28px, 22px + 1.6vw, 42px)/1.15 var(--font-sans);
  letter-spacing: -.02em;
  color: var(--pr-ink);
  text-align: center;
  text-wrap: balance;
  max-width: 20ch;
  margin-inline: auto;
}
.nums-h em {
  font-family: var(--font-serif); font-style: italic; font-weight: 400;
  background: linear-gradient(to right, var(--accent-a) 0%, var(--accent-b) 50%, var(--accent-a) 100%);
  -webkit-background-clip: text; background-clip: text;
  -webkit-text-fill-color: transparent; color: transparent;
}
.nums-sub {
  margin: 0 auto clamp(26px, 3.4vw, 40px);
  max-width: 34rem;
  font: 400 16px/1.5 var(--font-sans);
  color: var(--pr-body);
  text-align: center;
  text-wrap: pretty;
}

.nums-rows { list-style: none; margin: 0; padding: 0; }
.nums-rows li { display: grid; }
/* the rule sits ABOVE each row but the first, so the list never ends on one */
.nums-rows li + li::before {
  content: ""; display: block;
  height: 1.5px; margin: 0 0 24px;
  /* 7px on, 8px off — a CSS `dashed` border cannot be pinned to that */
  background-image: repeating-linear-gradient(to right,
    var(--pr-rail) 0 7px, transparent 7px 15px);
}
.nums-rows b {
  grid-column: 1;
  font: 400 clamp(48px, 40px + 2.2vw, 66px)/1 var(--font-serif);   /* 48px at 375, as measured */
  background: linear-gradient(to right, var(--accent-a) 0%, var(--accent-b) 55%, var(--accent-a) 100%);
  -webkit-background-clip: text; background-clip: text;
  -webkit-text-fill-color: transparent; color: transparent;
  font-variant-numeric: lining-nums tabular-nums;
}
.nums-rows span {
  grid-column: 2; justify-self: end; align-self: end;
  font: 600 clamp(18px, 16px + .55vw, 22px)/1.5 var(--font-sans);  /* 18/27 at 375 */
  color: var(--pr-ink);
  white-space: nowrap;
}
/* The labels are held on one line, which was safe while the figures were
   "24" and "9". They are thousands now, so on a 320px screen the value and
   a nowrap label no longer share a row — three of the four ran past their
   cell. Below 420 the label is allowed to wrap instead. */
@media (max-width: 420px) {
  .nums-rows span { white-space: normal; text-align: right; max-width: 58%; }
}
.nums-rows em {
  grid-column: 1 / -1;
  margin: 10px 0 24px;
  font: 400 16px/1.5 var(--font-sans); font-style: normal;
  color: var(--pr-body);
  text-wrap: pretty;
}
.nums-rows li { grid-template-columns: 1fr auto; align-items: end; column-gap: 10px; }
.nums-rows li + li::before { grid-column: 1 / -1; }

@media (min-width: 901px) {
  /* two across once there is room; the rule still separates the rows */
  .nums-rows { display: grid; grid-template-columns: 1fr 1fr; column-gap: clamp(32px, 5vw, 72px); }
  .nums-rows li + li::before { display: none; }
  .nums-rows li { padding-top: 26px; }
  .nums-rows li::after {
    content: ""; grid-column: 1 / -1; order: -1;
    height: 1.5px; margin-bottom: 22px;
    background-image: repeating-linear-gradient(to right,
      var(--pr-rail) 0 7px, transparent 7px 15px);
  }
  .nums-rows em { margin-bottom: 4px; }
}


/* ═══════════════════════════════════════════════════════════════════════
   THE ILLUSTRATIONS, WIRED AND MOVING
   ─────────────────────────────────────────────────────────────────────
   The panels were static card compositions — correct, but inert, and the
   cards had nothing joining them so they read as a pile rather than a
   system. What was missing is the connective tissue: orthogonal dashed
   runs in the brand red, each starting at a ring node on the card it
   leaves, with the dashes travelling along the run so the diagram shows
   something moving THROUGH it rather than just sitting there.

   Motion is gated two ways: `is-live` is set by about.js on the panel
   actually on screen, so four hidden figures are not animating into a
   compositor nobody is looking at; and the whole thing stops under
   prefers-reduced-motion.
   ══════════════════════════════════════════════════════════════════════ */

.pr-wire {
  position: absolute; inset: 0; z-index: 2;
  width: 100%; height: 100%;
  pointer-events: none;
  overflow: visible;
}
.pr-wire path {
  fill: none;
  /* FEWER, FATTER, BRIGHTER. Thin dashes on a long pitch read as a
     hairline at a glance; a short run only fits three or four of them,
     so each one has to carry. 4.2 wide with a 5-long segment and a 10
     gap means about half as many marks at nearly twice the weight —
     you see them as objects that move, not as a broken line.

     The colour steps up too: --accent-a is the ink colour, right for
     type on white and muddy for a 4px mark. --pr-wire is a brighter
     red that holds against the page without going pink. */
  stroke: var(--pr-wire);
  stroke-width: 4.2;
  stroke-dasharray: 5 10;
  /* The frame is scaled to 0.49 on a phone, which took the stroke to
     0.79px — under one device pixel, so the wires simply were not there.
     non-scaling-stroke renders the width in screen pixels whatever the
     transform does, which is the whole reason it exists. */
  vector-effect: non-scaling-stroke;
  stroke-linecap: round;
  opacity: 1;
}
.pr-wire circle { fill: none; stroke: var(--pr-dash); stroke-width: 1.4;
  vector-effect: non-scaling-stroke; }
.pr-wire .pr-node {
  fill: var(--pr-card);
  stroke: var(--pr-wire);
  stroke-width: 3;
  vector-effect: non-scaling-stroke;
  transform-box: fill-box;
  transform-origin: 50% 50%;
}

/* the golden refraction, behind the mark on the last panel */
.pr-glow {
  position: absolute; z-index: 0;
  left: 50%; top: 293px;
  width: 620px; height: auto;
  transform: translate(-50%, -50%);
  opacity: .9;
  pointer-events: none;
  /* the source fades to near-white, not to alpha 0, so on a white ground
     its rectangle was still visible — feathered here instead */
  --gm: radial-gradient(closest-side, #000 45%, rgba(0,0,0,.55) 72%, transparent 100%);
  -webkit-mask-image: var(--gm); mask-image: var(--gm);
}

@media (prefers-reduced-motion: no-preference) {
  /* dashes travel the run; one cycle is exactly the dasharray sum, so it
     loops with no seam */
  .pr-fig.is-live .pr-wire path { animation: pr-flow 1.5s linear infinite; }
  .pr-fig.is-live .pr-p5-spokes circle { animation: pr-flow 9s linear infinite; }
  .pr-fig.is-live .pr-wire .pr-node { animation: pr-pulse 2.4s ease-in-out infinite; }
  .pr-fig.is-live .pr-glow { animation: pr-breathe 6s ease-in-out infinite; }
  /* the loose chips drift, the dense cards stay put — everything moving at
     once reads as drift rather than as a diagram */
  .pr-fig.is-live > .pr-chip { animation: pr-drift 7s ease-in-out infinite; }
  .pr-fig.is-live > .pr-chip:nth-of-type(2) { animation-delay: -2.3s; }
  .pr-fig.is-live > .pr-chip:nth-of-type(3) { animation-delay: -4.6s; }
}

@keyframes pr-flow { to { stroke-dashoffset: -15; } }   /* one dash pitch */
@keyframes pr-in   { from { opacity: 0 } to { opacity: 1 } }
@keyframes pr-pulse  { 0%, 100% { transform: scale(1); opacity: 1; }
                       50%      { transform: scale(1.28); opacity: .78; } }
@keyframes pr-breathe{ 0%, 100% { opacity: .78; transform: translate(-50%, -50%) scale(1); }
                       50%      { opacity: 1;   transform: translate(-50%, -50%) scale(1.06); } }
@keyframes pr-drift  { 0%, 100% { translate: 0 0; } 50% { translate: 0 -5px; } }


/* The mid-route label: a pale plate sitting ON the wire, as the reference
   does — it names what happens BETWEEN two cards rather than inside one.
   The 15% edge fade that used to squeeze every composition into the middle
   is down to 8%, which is what let the cards spread far enough apart for a
   run to be long enough to read as a connection. */
.pr-relay {
  width: 240px; padding: 9px 14px; border-radius: 10px;
  background: var(--pr-card);
  border: 1px solid var(--pr-rail);
  box-shadow: var(--pr-shadow-s);
  font: 400 12.5px/1.4 var(--font-sans);
  color: var(--pr-ink-3);
  text-align: center;
  z-index: 3;
}


/* ── panel geometry ───────────────────────────────────────────────────
   690×585 frame, safe box x 55–635 / y 47–538 (the fade is 8%).
   Two loose columns: the spine on the left carries the main card, the
   right column feeds into it. Every wire in the markup is routed against
   these numbers, so moving a card here means moving its wire there. */






/* 5 · defend and publish — the exit. Questions on the inner ring, the
   publication routes on the outer corners, the mark in the middle. */
.pr-hub       { left: 269px; top: 217px; }

/* the checks card on panel 4 is narrow — its rows wrap otherwise */
.pr-p4-check .pr-rows li { font-size: 12.5px; }

/* ── arrival ──────────────────────────────────────────────────────────
   The panel's parts settle in rather than appearing whole: each card
   rises a few pixels and resolves, staggered, so the eye is led through
   the graph in the order the wires run rather than meeting all of it at
   once. Only ever on the live panel, and never under reduced motion. */
@media (prefers-reduced-motion: no-preference) {
  .pr-fig.is-live > .pr-el { animation: pr-arrive .62s cubic-bezier(.2, .7, .3, 1) backwards; }
  .pr-fig.is-live > .pr-el:nth-child(2)  { animation-delay: .02s }
  .pr-fig.is-live > .pr-el:nth-child(3)  { animation-delay: .09s }
  .pr-fig.is-live > .pr-el:nth-child(4)  { animation-delay: .16s }
  .pr-fig.is-live > .pr-el:nth-child(5)  { animation-delay: .23s }
  .pr-fig.is-live > .pr-el:nth-child(6)  { animation-delay: .30s }
  .pr-fig.is-live > .pr-el:nth-child(7)  { animation-delay: .37s }
  .pr-fig.is-live > .pr-el:nth-child(8)  { animation-delay: .44s }
  .pr-fig.is-live > .pr-el:nth-child(9)  { animation-delay: .51s }
  .pr-fig.is-live > .pr-el:nth-child(10) { animation-delay: .58s }
  /* A wire cannot arrive before the two things it joins. These all fired
     at 0s, so every line was on screen before its cards were — which is
     the flow reading backwards. Each one now waits for its source card,
     and only starts flowing once it has finished drawing. */
  /* The draw-in animated stroke-dasharray to a SOLID line, then dropped
     back to the dashed base when it finished — which is the "lines that
     turn into dots" you get on screen. The run now fades in as dots and
     never stops being dots; only the arrival is animated. */
  .pr-fig.is-live .pr-wire path {
    animation: pr-in .45s ease-out var(--wd, .34s) backwards,
               pr-flow 1.5s linear var(--wd, .34s) infinite;
  }
  /* The base is per PANEL, because they do not hold the same number of
     cards: the stagger is 70ms apart from 20ms, so a six-card panel is
     still landing at 0.37s and a seven-card one at 0.51s. A fixed 0.34s
     base had the wires on those two drawing before their own cards. */
  .pr-fig .pr-wire path              { --b: .34s }
  .pr-fig[data-i="1"] .pr-wire path  { --b: .45s }   /* 6 cards */
  .pr-fig[data-i="4"] .pr-wire path  { --b: .60s }   /* 7 cards */
  .pr-wire path:nth-of-type(1) { --wd: var(--b) }
  .pr-wire path:nth-of-type(2) { --wd: calc(var(--b) + .22s) }
  .pr-wire path:nth-of-type(3) { --wd: calc(var(--b) + .44s) }
  .pr-wire path:nth-of-type(4) { --wd: calc(var(--b) + .66s) }
  .pr-wire path:nth-of-type(5) { --wd: calc(var(--b) + .88s) }
}
@keyframes pr-arrive {
  from { opacity: 0; translate: 0 10px; scale: .985; }
  to   { opacity: 1; translate: 0 0;    scale: 1; }
}

@media (min-width: 1000px) {
  /* Four across once the wrap is wide enough, as the reference does.

     minmax(0, 1fr), not 1fr. A bare 1fr is minmax(auto, 1fr), and the auto
     minimum is the item's max-content — which here is a nowrap label like
     "Undergraduate through doctoral". Four of those could not shrink, so
     the track set measured 1490px inside a 1060px wrap and the fourth
     statistic sat off the right edge of the page entirely. It was reachable
     only by dragging the page sideways, which is the same overflow the
     viewport was clipping. */
  .nums-rows {
    grid-template-columns: repeat(4, minmax(0, 1fr));
    column-gap: clamp(24px, 3vw, 44px);
  }

  /* Stacked inside each column, not side by side.

     The two-column arrangement (figure left, label right) is right at two
     across, where a column is ~500px. At four across it is ~232px, and a
     66px numeral beside a wrapping label does not fit in that: the label
     ran under the next figure and five pairs of text overlapped —
     "Projects published" sat beneath "3,974+". Reading down instead of
     across is also the shape the reference uses for a four-up row. */
  .nums-rows li { grid-template-columns: minmax(0, 1fr); align-items: start; row-gap: 4px; }
  .nums-rows b, .nums-rows span, .nums-rows em { grid-column: 1; }
  .nums-rows span { justify-self: start; align-self: start; text-align: left; white-space: normal; }
}


/* ═══════════════════════════════════════════════════════════════════════
   ILLUSTRATION COMPONENTS — the denser vocabulary
   ─────────────────────────────────────────────────────────────────────
   The panels were cards, rows and dots. The reference carries a great
   deal more: an icon tile on every source card, real tables with column
   headers and progress bars, circular dials with a figure in the middle,
   a sparkline, two-column strength/gap lists, and small action buttons.
   Those are what make a diagram look like a product rather than a
   wireframe, so they are built here and used across the five panels.
   ══════════════════════════════════════════════════════════════════════ */

/* an icon tile — the pale rounded square every source card leads with */
.pr-ico {
  flex: none; width: 34px; height: 34px; border-radius: 9px;
  background: color-mix(in srgb, var(--accent-a) 9%, #fff);
  display: grid; place-items: center;
}
.pr-ico::before {
  content: ""; width: 17px; height: 17px;
  background: var(--accent-a);
  -webkit-mask: var(--g) center / contain no-repeat;
  mask: var(--g) center / contain no-repeat;
}
.pr-lead { display: flex; align-items: center; gap: 11px; }
.pr-lead .pr-tx { display: grid; gap: 2px; min-width: 0; }
.pr-lead .pr-h1 { font: 500 14px/1.25 var(--font-sans); color: var(--pr-ink); }
.pr-lead .pr-h2 { font: 400 11.5px/1.3 var(--font-sans); color: var(--pr-ink-3); }

/* a table: header row, then rows with a bar in the last column */
.pr-tbl { width: 100%; border-collapse: collapse; }
.pr-tbl th {
  text-align: left; padding: 0 0 7px;
  font: 500 9.5px/1 var(--font-sans); letter-spacing: .1em;
  text-transform: uppercase; color: var(--pr-ink-3);
  border-bottom: 1px solid var(--pr-rail);
}
.pr-tbl td { padding: 8px 0 0; vertical-align: middle; }
.pr-tbl .t-n  { font: 500 11.5px/1.25 var(--font-sans); color: var(--pr-ink-2); }
.pr-tbl .t-s  { font: 400 10px/1.25 var(--font-sans); color: var(--pr-ink-3); }
.pr-tbl .t-d  { font: 400 10.5px/1 var(--font-sans); color: var(--c, var(--pr-ink-3));
                white-space: nowrap; font-variant-numeric: tabular-nums; }
.pr-tbl .t-b  { width: 54px; }
.pr-tbl .t-b i { display: block; height: 5px; border-radius: 3px; background: var(--pr-chip); }
.pr-tbl .t-b i b { display: block; height: 100%; border-radius: 3px; background: var(--c); }

/* a dial with a figure in the middle */
.pr-dial { position: relative; width: var(--s, 40px); height: var(--s, 40px); flex: none; }
.pr-dial svg { width: 100%; height: 100%; transform: rotate(-90deg); }
.pr-dial circle { fill: none; stroke-width: 4; stroke-linecap: round; }
.pr-dial .d-bg { stroke: var(--pr-chip); }
.pr-dial .d-fg { stroke: var(--c); }
.pr-dial span {
  position: absolute; inset: 0; display: grid; place-items: center;
  font: 600 11px/1 var(--font-sans); color: var(--c);
  font-variant-numeric: tabular-nums;
}

/* two columns of ticked / crossed points */
.pr-two { display: grid; grid-template-columns: 1fr 1fr; gap: 0 18px; }
.pr-two h5 {
  margin: 0 0 7px; padding-bottom: 6px;
  font: 500 10.5px/1 var(--font-sans); color: var(--pr-ink-3);
  letter-spacing: .06em; text-transform: uppercase;
  border-bottom: 1px solid var(--pr-rail);
}
.pr-two ul { list-style: none; margin: 0; padding: 0; display: grid; gap: 6px; }
.pr-two li {
  display: flex; align-items: center; gap: 7px;
  font: 400 11.5px/1.25 var(--font-sans); color: var(--pr-ink-2);
}
.pr-two li::before {
  content: ""; flex: none; width: 11px; height: 11px;
  background: var(--c, var(--pr-idle));
  -webkit-mask: var(--g) center / contain no-repeat;
  mask: var(--g) center / contain no-repeat;
}

/* a sparkline */
.pr-spark { width: 100%; height: 46px; display: block; }
.pr-spark path { fill: none; stroke: var(--accent-a); stroke-width: 1.6;
                 vector-effect: non-scaling-stroke; }
.pr-spark .s-fill { fill: color-mix(in srgb, var(--accent-a) 9%, transparent); stroke: none; }
.pr-spark .s-grid { stroke: var(--pr-rail); stroke-width: 1; stroke-dasharray: 3 4;
                    vector-effect: non-scaling-stroke; }

/* a small dark action button, and a status pill with a caret */
.pr-btn {
  padding: 6px 11px; border-radius: 7px; background: var(--pr-ink);
  font: 500 10.5px/1 var(--font-sans); color: #fff; white-space: nowrap;
}
.pr-state {
  display: inline-flex; align-items: center; gap: 5px;
  padding: 4px 8px; border-radius: 6px;
  background: color-mix(in srgb, var(--c) 12%, transparent);
  font: 500 10px/1 var(--font-sans); color: var(--c); white-space: nowrap;
}
.pr-state::after {
  content: ""; width: 8px; height: 8px; background: currentColor;
  -webkit-mask: var(--i-chev) center / contain no-repeat;
  mask: var(--i-chev) center / contain no-repeat;
}

/* document rows: icon, two lines of label, an action at the end */
.pr-docs { list-style: none; margin: 0; padding: 0; display: grid; gap: 7px; }
.pr-docs li {
  display: flex; align-items: center; gap: 9px;
  padding: 7px 9px; border-radius: 8px; border: 1px dashed var(--pr-rail);
}
.pr-docs .d-i {
  flex: none; width: 22px; height: 22px; border-radius: 6px;
  background: color-mix(in srgb, var(--c, var(--accent-a)) 10%, #fff);
  display: grid; place-items: center;
}
.pr-docs .d-i::before {
  content: ""; width: 11px; height: 11px; background: var(--c, var(--accent-a));
  -webkit-mask: var(--i-doc) center / contain no-repeat;
  mask: var(--i-doc) center / contain no-repeat;
}
.pr-docs .d-t { display: grid; gap: 1px; margin-right: auto; min-width: 0; }
.pr-docs .d-t b { font: 500 11.5px/1.2 var(--font-sans); color: var(--pr-ink); }
.pr-docs .d-t b i { font: 400 10.5px/1 var(--font-sans); color: var(--pr-ink-3); font-style: normal; }
.pr-docs .d-t span { font: 400 10px/1.2 var(--font-sans); color: var(--pr-ink-3); }

/* named chips, for a cluster of short labels */
.pr-names { display: flex; flex-wrap: wrap; gap: 5px; }
.pr-names span {
  padding: 4px 8px; border-radius: 6px; background: var(--pr-chip);
  font: 400 10px/1.1 var(--font-sans); color: var(--pr-ink-3); white-space: nowrap;
}


/* ── panel geometry ───────────────────────────────────────────────────
   Two columns inside the 8% safe box (x 55–635, y 47–538): a wide spine
   on the left carrying the dense card, a narrower feeder column on the
   right. Wire paths in the markup are routed against these numbers. */

/* 1 · scope */
.pr-p1-idea { left: 58px;  top: 47px;  width: 300px; }
.pr-p1-titles { left: 388px; top: 47px;  width: 244px; }
.pr-p1-relay { left: 58px;  top: 163px; width: 300px; }
.pr-p1-card { left: 58px;  top: 255px; width: 340px; }
.pr-p1-due { left: 418px; top: 255px; width: 214px; }

/* 2 · method */
.pr-p2-src { left: 58px;  top: 47px;  width: 300px; }
.pr-p2-train { left: 388px; top: 47px;  width: 244px; }
.pr-p2-relay { left: 58px;  top: 163px; width: 300px; }
.pr-p2-card { left: 58px;  top: 272px; width: 340px; }
.pr-p2-pick { left: 418px; top: 272px; width: 214px; }
.pr-p2-note   { left: 130px; top: 504px; width: 420px; }

/* 3 · build */
.pr-p3-repro { left: 58px;  top: 47px;  width: 300px; }
.pr-p3-cad { left: 388px; top: 47px;  width: 244px; }
.pr-p3-tools { left: 388px; top: 196px; width: 244px; }
.pr-p3-card { left: 58px;  top: 163px; width: 320px; }
.pr-p3-log { left: 168px; top: 433px; width: 400px; }
/* Panel 3 is the tallest stack on the section — three boxes in a column
   that is only 491 units deep. Everything here is bought back from the
   two dense boxes so both connector runs get the same ~64px of clear
   air every other panel has. */
.pr-p3-log { padding: 10px 14px 5px; }
.pr-p3-card { padding-bottom: 10px; }
.pr-p3-card .pr-docs { gap: 3px; }
.pr-p3-card .pr-docs li { padding: 3px 8px; }
.pr-p3-log .pr-spark { height: 16px; margin-top: 2px; }
.pr-p3-log .pr-names { margin-top: 3px; }

/* 4 · document */
.pr-p4-fmt { left: 58px;  top: 47px;  width: 300px; }
.pr-p4-card { left: 58px;  top: 163px; width: 320px; }
.pr-p4-check { left: 424px; top: 163px; width: 208px; }
/* narrow column: the two lists stack rather than sit side by side */
.pr-p4-check .pr-two { grid-template-columns: 1fr; gap: 14px 0; }

/* 5 · defend and publish — the exit */
.pr-p5-spokes { left: 0; top: 0; width: 690px; height: 585px; }
.pr-hub       { left: 269px; top: 217px; }
.pr-p5-a { left: 50%; top: 118px; transform: translateX(-50%); }
.pr-p5-c { left: 50%; top: 430px; transform: translateX(-50%); }
.pr-p5-b { left: 58px;  top: 268px; max-width: 150px; white-space: normal; }
.pr-p5-d { right: 58px; top: 268px; max-width: 150px; white-space: normal; }
.pr-p5-j      { left: 58px;  top: 60px;  width: 196px; }

/* the lead cards are a single row, so they need no inner padding stack */
.pr-card .pr-lead { min-width: 0; }
.pr-p1-idea, .pr-p1-titles, .pr-p2-src, .pr-p2-train,
.pr-p3-repro, .pr-p3-cad, .pr-p4-fmt { padding: 13px 15px; }


/* ── the standoff ─────────────────────────────────────────────────────
   A connector that starts flush against a card, with its node half
   sunk into the edge, reads as a mistake. Every run begins 10px clear
   of the card it leaves and stops 10px short of the one it meets, with
   a node at BOTH ends — the run links two boxes, so both boxes get the
   terminal ring.

   Rows sit 54px apart on every panel, which leaves 34px of run.

   A ring marks where a connector MEETS A CARD — nothing else. A run
   that leaves a card gets one at its origin and one where it arrives.
   A floating relay label that the line simply passes does NOT get one:
   the line breaks around it and carries on, which is how the reference
   reads. Putting a ring at every segment end turned the middle of a
   chain into a terminus it is not. */
.pr-node { r: 6; }
.pr-wire .pr-node { stroke-width: 1.6; }


/* ── the bar over the hero ────────────────────────────────────────────
   cloudseq.js writes --hdr-a: 1 while the glassy bar sits over the sky,
   0 through the chrome-free middle of the run, and back to 1 in step
   with the card's top band opening. Every other page leaves the variable
   unset, so the fallback keeps the bar solid.

   No transition on purpose: the value is already scrubbed by scroll, and
   easing a scrubbed property makes every frame lag the scroll. .is-mute
   takes it out of the tab order while it is invisible, and :focus-within
   overrides both so a keyboard user can always reach it. */
.site-header { opacity: var(--hdr-a, 1); }
.site-header.is-mute { pointer-events: none; }
.site-header.is-mute:focus-within { opacity: 1; pointer-events: auto; }


/* ═══════════════════════════════════════════════════════════════════════
   CONTACT — the supplied ContactCard, in this codebase's own CSS
   ─────────────────────────────────────────────────────────────────────
   Same composition: a bordered card, plus marks sitting OUTSIDE the four
   corners, a 2/3 content column and a 1/3 tinted form panel with a rule
   between them. The component's Tailwind tokens map onto ours —
   bg-card → --pr-card, muted-foreground → --pr-body, bg-muted/40 → a
   light tint of --pr-chip.
   ══════════════════════════════════════════════════════════════════════ */

.ct-card {
  position: relative;
  display: grid;
  background: var(--pr-card);
  border: 1px solid var(--line);
  box-shadow: 0 1px 3px rgba(0, 0, 0, .06), 0 8px 24px -18px rgba(0, 0, 0, .3);
}

/* the four plus marks, offset outside the corners as the component has them */
.ct-plus {
  position: absolute; width: 22px; height: 22px; z-index: 1;
  background: var(--pr-ink-3);
  -webkit-mask: var(--i-plus) center / contain no-repeat;
  mask: var(--i-plus) center / contain no-repeat;
  opacity: .5;
}
.ct-tl { top: -11px; left: -11px; }
.ct-tr { top: -11px; right: -11px; }
.ct-bl { bottom: -11px; left: -11px; }
.ct-br { bottom: -11px; right: -11px; }

.ct-body { padding: clamp(24px, 4vw, 40px); display: grid; align-content: start; gap: 14px; }
.ct-h {
  margin: 0;
  font: 600 clamp(28px, 22px + 1.8vw, 44px)/1.1 var(--font-sans);
  letter-spacing: -.02em;
  color: var(--pr-ink);
}
.ct-d {
  margin: 0; max-width: 34rem;
  font: 400 clamp(15px, 14px + .4vw, 18px)/1.6 var(--font-sans);
  color: var(--pr-body);
  text-wrap: pretty;
}

.ct-info { list-style: none; margin: 10px 0 0; padding: 0; display: grid; gap: 4px; }
.ct-info li { display: flex; align-items: center; gap: 12px; padding: 10px 0; }
.ct-ic {
  flex: none; width: 44px; height: 44px; border-radius: 10px;
  background: var(--pr-chip);
  display: grid; place-items: center;
}
.ct-ic::before {
  content: ""; width: 20px; height: 20px; background: var(--pr-ink-2);
  -webkit-mask: var(--g) center / contain no-repeat;
  mask: var(--g) center / contain no-repeat;
}
.ct-tx { display: grid; gap: 2px; min-width: 0; }
.ct-tx b { font: 500 15px/1.3 var(--font-sans); color: var(--pr-ink); }
.ct-tx a, .ct-tx em {
  font: 400 13px/1.4 var(--font-sans); font-style: normal; color: var(--pr-body);
}
.ct-tx a { text-decoration: underline; text-underline-offset: 2px; }
.ct-tx a:hover { color: var(--accent-a); }

/* the tinted panel the form sits on */
.ct-panel {
  padding: clamp(20px, 3vw, 28px);
  background: color-mix(in srgb, var(--pr-chip) 45%, transparent);
  border-top: 1px solid var(--line);
  display: flex; align-items: center;
}
.ct-form { width: 100%; display: grid; gap: 14px; }
.ct-field { margin: 0; display: grid; gap: 6px; }
.ct-field label {
  font: 500 13.5px/1.3 var(--font-sans); color: var(--pr-ink);
}
.ct-field label span { font-weight: 400; color: var(--pr-ink-3); }
.ct-field input, .ct-field textarea {
  width: 100%; padding: 10px 12px;
  border: 1px solid var(--line-strong); border-radius: 8px;
  background: var(--pr-card);
  font: 400 15px/1.4 var(--font-sans); color: var(--pr-ink);
}
.ct-field textarea { resize: vertical; min-height: 88px; }
.ct-field input:focus-visible, .ct-field textarea:focus-visible {
  outline: 2px solid var(--accent-a); outline-offset: 1px; border-color: transparent;
}
.ct-field .is-bad { border-color: var(--accent-a); }

.ct-go {
  width: 100%; padding: 12px 16px; border: 0; border-radius: 8px;
  background: var(--pr-ink); color: #fff; cursor: pointer;
  font: 500 15px/1.3 var(--font-sans);
}
.ct-go:hover { background: #000; }
.ct-go:disabled { opacity: .55; cursor: default; }
.ct-note {
  margin: 0; min-height: 1.3em;
  font: 400 13px/1.4 var(--font-sans); color: var(--pr-body);
}
.ct-note.is-bad { color: var(--accent-a); }

@media (min-width: 761px) {
  .ct-card { grid-template-columns: 2fr 1fr; }
  .ct-panel { border-top: 0; border-left: 1px solid var(--line); }
  .ct-info { grid-template-columns: 1fr 1fr; gap: 4px 20px; }
  .ct-wide { grid-column: 1 / -1; }
}


/* the standalone contact page: the same card, given a page to sit on */
.ct-page { padding-block: clamp(24px, 4vw, 48px) 0; }
.ct-kicker {
  margin: 0 0 clamp(14px, 2vw, 22px);
  font: 500 var(--text-small)/1 var(--font-sans);
  letter-spacing: .12em; text-transform: uppercase;
  color: var(--accent-ink);
}


/* The spam trap. Off-screen rather than display:none — a bot that reads
   the stylesheet skips anything hidden, but happily fills a field it can
   see in the DOM. Out of the tab order and hidden from screen readers, so
   nobody real ever meets it. */
.ct-trap {
  position: absolute; left: -9999px; width: 1px; height: 1px;
  overflow: hidden; margin: 0;
}


/* ── the contact page, split ──────────────────────────────────────────
   Globe on the left half, the card on the right. One column below the
   breakpoint — a 500px canvas beside a form on a phone helps nobody. */
.ct-split { padding-block: clamp(20px, 3vw, 40px); }
.ct-grid { display: grid; gap: clamp(28px, 4vw, 56px); align-items: center; }

.ct-globe { position: relative; }
.ct-globe canvas {
  display: block; width: 100%; aspect-ratio: 1 / 1;
  max-height: 62svh; margin-inline: auto;
  touch-action: pan-y;            /* a vertical swipe still scrolls the page */
  cursor: grab;
}
.ct-globe canvas:active { cursor: grabbing; }

@media (min-width: 981px) {
  /* the card leads and takes three quarters; the globe follows at a
     quarter — half of the half it had */
  .ct-grid { grid-template-columns: minmax(0, 3fr) minmax(0, 1fr); }
}
@media (max-width: 980px) {
  /* the globe reads as an accent here, not a centrepiece */
  .ct-globe canvas { max-width: 420px; max-height: 46svh; }
}


/* ═══════════════════════════════════════════════════════════════════════
   THE SUMMIT BANNER — to the supplied reference
   ─────────────────────────────────────────────────────────────────────
   Copy on the left over a pale warm wash, the photograph bleeding off the
   right, the two meeting in a soft gradient rather than on a hard edge.
   The supplied artwork already fades to transparent on its left, so the
   blend is the image's own alpha doing the work — no mask needed.
   ══════════════════════════════════════════════════════════════════════ */

.pk {
  position: relative; isolation: isolate;
  border-radius: clamp(18px, 2vw, 28px);
  overflow: hidden;
  background:
    linear-gradient(105deg,
      color-mix(in srgb, var(--accent-a) 7%, #fff) 0%,
      #fff 46%,
      color-mix(in srgb, var(--accent-a) 4%, #fff) 100%);
  box-shadow: 0 1px 3px rgba(0,0,0,.05), 0 24px 60px -40px rgba(0,0,0,.35);
  display: grid;
  align-items: center;
  min-height: clamp(300px, 34vw, 430px);
}
.pk-art {
  grid-area: 1 / 1;
  justify-self: end; align-self: stretch;
  width: min(64%, 900px); height: 100%;
  object-fit: cover; object-position: center;
  z-index: -1;                     /* the copy always wins the overlap */
}
.pk-copy {
  grid-area: 1 / 1;
  padding: clamp(28px, 4vw, 56px);
  max-width: 34rem;
  display: grid; gap: clamp(12px, 1.4vw, 18px); justify-items: start;
}
.pk-h {
  margin: 0;
  font: 400 clamp(26px, 18px + 2vw, 44px)/1.14 var(--font-sans);
  letter-spacing: -.022em;
  color: var(--pr-ink);
  text-wrap: balance;
}
.pk-h em {
  display: block;
  font-family: var(--font-serif); font-style: italic; font-weight: 400;
  color: var(--accent-b);
}
.pk-d {
  margin: 0; max-width: 30rem;
  font: 400 clamp(15px, 14px + .4vw, 18px)/1.55 var(--font-sans);
  color: var(--pr-body);
  text-wrap: pretty;
}
.pk-go {
  margin-top: clamp(6px, 1vw, 12px);
  padding: 14px 26px; border-radius: 10px;
  background: linear-gradient(180deg, #3a3a3a 0%, #141414 100%);
  color: #fff; text-decoration: none;
  font: 500 clamp(14px, 13px + .3vw, 16px)/1.2 var(--font-sans);
  box-shadow: 0 1px 2px rgba(0,0,0,.3), inset 0 1px 0 rgba(255,255,255,.14);
}
.pk-go:hover { background: #000; }

@media (max-width: 760px) {
  /* the photograph goes underneath rather than behind: at this width the
     copy would sit on the mountain and neither would be readable */
  .pk { min-height: 0; grid-template-rows: auto auto; }
  .pk-art {
    grid-area: 2 / 1; width: 100%; height: clamp(180px, 46vw, 240px);
    z-index: 0; object-position: center 30%;
  }
  .pk-copy { grid-area: 1 / 1; max-width: none; padding-bottom: clamp(20px, 4vw, 28px); }
}


/* ═══════════════════════════════════════════════════════════════════════
   FAQ — to the supplied reference
   ─────────────────────────────────────────────────────────────────────
   A pill label, a two-tone centred heading, then SEPARATE soft white
   cards on a light band with real air between them. No dividers, no
   accent rail, no tint on the open row: in the reference an open card
   is the same card, just taller. The only thing that changes is the
   marker, + to −.

   No size or weight is set here. The role layer above is authoritative
   on type ("do not set font-size in a component") and already assigns
   every string in this section. The one exception is the <em>, which no
   role selector reaches. */

:is(#faq, #cfq) {
  /* No band. The page is one continuous ground, so the cards earn their
     separation from a real border and a soft shadow rather than from a
     tinted strip behind them. */
  --fq-card:  #ffffff;
  --fq-edge:  rgba(0, 0, 0, .085);
  background: none;
  color: var(--pr-ink);
}

/* ── header ───────────────────────────────────────────────────────────
   One centred column. It was a flex ROW, left over from when a mode
   control sat opposite the heading — which left the standfirst stranded
   on the far right with nothing to belong to. */
.fq-head {
  display: flex; flex-direction: column; align-items: center;
  text-align: center; gap: 14px;
  max-width: 46rem; margin-inline: auto;
}
.fq-pill {
  display: inline-block;
  padding: 7px 18px; border-radius: 999px;
  background: rgba(0, 0, 0, .05);
  color: var(--pr-body);
  font-family: var(--font); font-size: 13px; font-weight: 500;
  line-height: 1.3; letter-spacing: 0;
}
.fq-head h2 { margin: 0; color: var(--pr-ink); text-wrap: balance; }
/* the emphasised clause: serif italic in the accent, exactly as .pr-h em
   does it. The role layer pins the h2 but does not reach <em>, so this
   is the one type declaration the component is allowed to make. */
.fq-head h2 em {
  font-family: var(--font-serif); font-style: italic; font-weight: 400;
  color: var(--accent-a);
}
.fq-head .lede { margin: 0; max-width: 38rem; color: var(--pr-body); }

/* ── the cards ────────────────────────────────────────────────────────
   Detached, generously rounded, and spaced. */
.faq {
  list-style: none; padding: 0;
  max-width: 960px; margin: clamp(28px, 3.2vw, 44px) auto 0;
  display: grid; gap: clamp(12px, 1.3vw, 17px);
  background: none; border: 0; border-radius: 0; overflow: visible;
}

.fq {
  position: relative; isolation: isolate; overflow: hidden;
  background: var(--fq-card);
  border: 1px solid var(--fq-edge);
  border-radius: clamp(15px, 1.5vw, 20px);
  box-shadow: 0 1px 3px rgba(0, 0, 0, .04);
  transition: box-shadow .3s var(--ease), border-color .3s;
}
.fq:hover, .fq:focus-within { transform: none; }
.fq:hover { box-shadow: 0 4px 14px rgba(0, 0, 0, .07); border-color: rgba(0, 0, 0, .13); }
/* the open card is the same card, just taller — the reference gives it
   no rail, no wash and no border change */
.fq[data-open="true"] { background: var(--fq-card); }

.fq h3 { margin: 0; font: inherit; }
.fq > h3 > button {
  display: flex; align-items: center; gap: clamp(16px, 1.8vw, 24px);
  width: 100%;
  padding: clamp(20px, 2vw, 27px) clamp(22px, 2.5vw, 34px);
  background: none; border: 0; cursor: pointer; text-align: left;
  color: var(--pr-ink);
}
.fq > h3 > button:focus-visible { outline: 2px solid var(--accent-a); outline-offset: -4px; }

.fq-tx { order: 1; flex: 1; min-width: 0; display: block; }
.fq-q { color: var(--pr-ink); text-wrap: pretty; }
.fq-meta { display: none; }            /* the reference shows the question alone */

/* ── the marker ───────────────────────────────────────────────────────
   A small red ring with a drawn +, becoming − when the card opens. The
   reference keeps it hollow in both states. */
.fq-ic {
  order: 2; flex: none;
  /* height:auto is load-bearing — the original control set an explicit
     48px, and an explicit height beats aspect-ratio, so the ring drew
     as an ellipse. */
  width: clamp(21px, 1.6vw, 24px); height: auto; aspect-ratio: 1;
  border-radius: 50%;
  border: 1.5px solid var(--accent-a);
  background: none; position: relative;
  display: grid; place-items: center;
  transition: transform .34s var(--ease);
}
.fq-ic i { display: none; }            /* the glyph is drawn, not typed */
.fq-ic::before, .fq-ic::after {
  content: ""; position: absolute;
  background: var(--accent-a); border-radius: 1px;
  transition: opacity .24s var(--ease);
}
.fq-ic::before { width: 46%; height: 1.5px; }
.fq-ic::after  { width: 1.5px; height: 46%; }
.fq[data-open="true"] .fq-ic::after { opacity: 0; }

.fq-glow { display: none; }

/* ── the answer ───────────────────────────────────────────────────────
   Aligned with the question, held to a readable measure. */
.fq-p { display: grid; grid-template-rows: 0fr; transition: grid-template-rows .4s var(--ease); }
.fq[data-open="true"] .fq-p { grid-template-rows: 1fr; }
.fq-p > div { overflow: hidden; min-height: 0; }
.fq-p p {
  margin: 0;
  padding: 0 clamp(22px, 2.5vw, 34px) clamp(21px, 2.2vw, 29px);
  max-width: 78ch;
  color: var(--pr-body);
}
.fq-p a { color: var(--accent-a); }

@media (prefers-reduced-motion: reduce) {
  .fq, .fq-ic, .fq-p { transition: none; }
}
@media (max-width: 560px) {
  .fq > h3 > button { gap: 14px; padding: 18px 19px; }
  .fq-p p { padding: 0 19px 20px; }
}

/* ═══ TESTIMONIALS — the header, to the supplied reference ═══════════════
   Same shape as the FAQ header: a pill, a two-tone headline whose second
   clause is the display serif in the accent, then one standfirst line.
   The two sections are the same kind of thing — a label, a claim, a line
   of context — so they are set the same way. */
/* ── what the work produces ─────────────────────────────────
   Rebuilt to the reference the founder supplied. Each card reads top to
   bottom: the title where the reference put a university crest, a
   two-column comparison split by a hairline that fades out at both ends,
   then the photograph filling the lower half behind a scrim that dissolves
   its top edge into the card.

   No background of its own. Every other section on the landing page sits
   straight on the page white and this was the one tinted band interrupting
   that run, which is what made it read as a separate slab rather than part
   of the page. The cards carry a shadow instead — that is what separates
   them now that card and ground are the same white. */
.io { padding: var(--sec-y) 0; background: var(--io-ground); }

.io-head {
  display: flex; flex-direction: column; align-items: center; text-align: center;
  max-width: 52rem; margin: 0 auto clamp(24px, 3vw, 38px);
}
.io-pill {
  display: inline-block; padding: 6px 16px; border-radius: 36px;
  border: 1.5px solid var(--line);
  background: linear-gradient(180deg, var(--io-pill), var(--surface));
  font-size: 14px; line-height: 21px; color: var(--muted);
}
.io-head h2 { margin: 10px 0 0; color: var(--ink); text-wrap: balance; }
.io-head h2 em {
  font-family: var(--font-serif); font-style: italic; font-weight: 400;
  background: linear-gradient(to right, var(--brand), #8a0007, var(--brand));
  -webkit-background-clip: text; background-clip: text; color: transparent;
  padding-right: 4px; -webkit-box-decoration-break: clone; box-decoration-break: clone;
}
.io-sub { margin: 8px 0 0; font-size: 18px; color: var(--muted); }

/* ── the segmented control ───────────────────────────────── */
.io-tabs { display: flex; justify-content: center; margin-bottom: clamp(22px, 2.6vw, 34px); }
.io-tabrow {
  position: relative; display: flex; align-items: center; width: fit-content;
  border: 1px solid var(--line); border-radius: 10px; background: var(--surface-2);
}
.io-tab {
  position: relative; z-index: 2;
  display: flex; align-items: baseline; gap: 8px;
  padding: 10px clamp(14px, 1.8vw, 20px);
  border: 0; background: none; cursor: pointer;
  font-family: var(--font); font-size: 14px; color: var(--faint);
  transition: color .25s var(--ease);
}
.io-tab:hover { color: var(--ink); }
.io-tab.is-on { color: var(--ink); font-weight: 600; }
.io-lvl { font-weight: 600; }
.io-who { font-size: 12px; color: var(--faint); }
.io-tab.is-on .io-who { color: var(--muted); }
.io-tab:focus-visible { outline: 2px solid var(--brand); outline-offset: -3px; border-radius: 10px; }

/* the pill. left/width are written by about.js from the active button. */
.io-pillslide {
  position: absolute; inset-block: 0; z-index: 1; left: 0; width: 0;
  border: 1px solid var(--line); border-radius: 10px; background: var(--surface);
  box-shadow:
    0 1px 2px 0 rgba(43,48,59,.06), 0 2px 4px 0 rgba(43,48,59,.04),
    0 4px 8px -2px rgba(43,48,59,.06), 0 0 0 1px rgba(43,48,59,.05);
  transition: left .5s cubic-bezier(.175,.885,.32,1.1),
              width .5s cubic-bezier(.175,.885,.32,1.1);
}

/* ── the cards ────────────────────────────────────────── */
/* Explicit counts rather than auto-fit: auto-fit landed on three columns
   at common laptop widths and left the fourth card orphaned on its own row.
   1 / 2 / 4 never strands one. */
.io-panels { overflow-x: clip; }
@media (max-width: 620px) { .io-panels { overflow-x: visible; } }
.io-panel {
  display: grid; gap: 10px;
  grid-template-columns: repeat(2, minmax(0, 1fr));
}
@media (min-width: 1040px) { .io-panel { grid-template-columns: repeat(4, minmax(0, 1fr)); } }
@media (max-width: 620px)  { .io-panel { grid-template-columns: minmax(0, 1fr); } }
.io-panel[hidden] { display: none; }

.io-card {
  container-type: inline-size;
  display: flex; flex-direction: column;
  overflow: hidden; border-radius: 12px; background: var(--surface);
  min-height: 420px;
}

.io-top { padding: clamp(22px, 2.4vw, 30px) clamp(16px, 1.8vw, 22px) clamp(18px, 2vw, 24px); }
.io-ttl {
  margin: 0; text-align: center; text-wrap: balance;
  font-size: clamp(15px, 1.5vw, 17px); font-weight: 600; line-height: 1.35;
  min-height: 2.7em;                       /* two lines, held open */
  color: var(--ink);
}

/* the comparison. The divider is a hairline that fades to nothing at both
   ends rather than a flat rule — the same treatment the rest of the page
   uses where two things sit side by side. */
.io-cmp {
  position: relative; display: grid; grid-template-columns: 1fr 1fr;
  margin-top: clamp(18px, 2.2vw, 26px);
}
.io-cmp::before {
  content: ""; position: absolute; left: 50%; top: 2px; bottom: 2px; width: 1px;
  background: linear-gradient(to bottom,
    rgba(207,203,192,.02), var(--line-strong), rgba(207,203,192,.02));
}
/* grid rather than flow: the value cell stretches and the label is pinned
   to the bottom, so the two sides of a card align on their label row even
   when one value wraps to a line the other does not */
.io-side {
  display: grid; grid-template-rows: 1fr auto;
  padding: 0 clamp(6px, .8vw, 12px); text-align: center;
}
.io-val {
  margin: 0 0 12px; font-family: var(--font); font-weight: 600;
  font-size: clamp(14px, 7cqi, 24px); line-height: 1.2;
  min-height: 2.4em;                       /* two lines, held open */
  letter-spacing: -.015em; text-wrap: balance; overflow-wrap: break-word;
  -webkit-background-clip: text; background-clip: text; color: transparent;
  -webkit-box-decoration-break: clone; box-decoration-break: clone;
}
.io-was .io-val { background-image: linear-gradient(180deg, var(--io-grey-a), var(--io-grey-b)); }
.io-now .io-val { background-image: linear-gradient(180deg, var(--io-red-a),  var(--io-red-b));  }
.io-lbl { margin: 0; font-size: 15px; line-height: 1.4; color: var(--faint); }
.io-lbl span { display: block; font-weight: 600; }
.io-was .io-lbl span { color: var(--muted); }
.io-now .io-lbl span { color: var(--brand); }

.io-shot { position: relative; flex: 1 1 auto; min-height: 268px; overflow: hidden; }
.io-shot::before {
  content: ""; position: absolute; inset-inline: 0; top: 0; z-index: 10; height: 11px;
  background: linear-gradient(180deg, var(--surface), transparent);
  pointer-events: none;
}
.io-shot img {
  width: 100%; height: 100%; object-fit: cover; display: block;
  transform: scale(1); transition: transform .5s cubic-bezier(.4,0,.2,1);
}
.io-card:hover .io-shot img { transform: scale(1.1); }
/* The level alone carries the tab on a phone. With the second label in, the
   three tabs measured 448px against a 411px screen and were clipped at both
   ends — the first and last tab lost their text. B.Tech / M.Tech / PhD is
   the part that does the choosing anyway. */
@media (max-width: 620px) {
  .io-who { display: none; }
  .io-tab { padding-inline: 20px; }

  /* one card per screen, edge to edge, swiped horizontally */
  .io-panel {
    grid-template-columns: none;
    grid-auto-flow: column; grid-auto-columns: 100%;
    gap: 0;
    margin-inline: calc(var(--gutter) * -1);
    overflow-x: auto; overscroll-behavior-x: contain;
    scroll-snap-type: x mandatory;
    scrollbar-width: none; -ms-overflow-style: none;
  }
  .io-panel::-webkit-scrollbar { display: none; }
  .io-card { scroll-snap-align: start; border-radius: 0; min-height: 496px; }
  .io-shot { min-height: 0; flex: 0 0 60%; }   /* the reference gives the photo 60% */
}

/* 10px round, 8px apart, with the active one carrying a brand ramp — the
   reference's measurements. Its inactive dots are pure white, which on the
   #fafafa ground is a 1.02:1 contrast and effectively invisible; ours take
   a light neutral instead so the row actually reads as a position. */
.io-dots {
  display: none; justify-content: center; align-items: center; gap: 8px;
  margin-top: clamp(16px, 2vw, 22px);
}
.io-dot {
  width: 10px; height: 10px; padding: 0; border: 0; border-radius: 999px;
  background: var(--line-strong); cursor: pointer;
  transition: background .3s var(--ease), transform .3s var(--ease);
}
.io-dot[aria-current="true"] {
  background: linear-gradient(180deg, var(--brand), var(--io-dot-b));
}
.io-dot:focus-visible { outline: 2px solid var(--brand); outline-offset: 3px; }
@media (max-width: 620px) { .io-dots { display: flex; } }
@media (prefers-reduced-motion: reduce) { .io-dot { transition: none; } }

/* Cards arrive from the right on a tab switch — no exit, the data swaps and
   the new set enters. Staggered by index, set inline by about.js. */
.io-panel.is-swap .io-card {
  animation: ioIn .6s cubic-bezier(.2,.7,.3,1) backwards;
  animation-delay: calc(var(--c, 0) * .09s);
}
@keyframes ioIn { from { opacity: 0; transform: translateX(60px); } }

@media (prefers-reduced-motion: reduce) {
  .io-pillslide { transition: none; }
  .io-shot img { transition: none; }
  .io-panel.is-swap .io-card { animation: none; }
}

/* ── what changes: the before/after diagram ───────────────────────────
   Replaced a twenty-quote marquee. The point of the row is the ARROW: two
   states of the same project, and the turn between them. So the two sides
   are deliberately unequal — the "before" is recessed, dashed and grey
   because it is the state you are leaving; the "after" is a solid card in
   the brand tint because it is the one you are paying for. Reading it
   left to right should tell the story without reading the words.

   Below 760px the row stacks and the arrow rotates to point down, so the
   direction of travel survives the reflow. */
.sh {
  list-style: none; margin: 0; padding: 0;
  display: grid; gap: clamp(10px, 1.2vw, 16px);
  max-width: 66rem; margin-inline: auto;
}
.sh-row {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto minmax(0, 1fr);
  align-items: center;
  gap: clamp(12px, 1.6vw, 22px);
}
.sh-n {
  font-family: var(--font); font-size: 12px; font-weight: 700;
  letter-spacing: .1em; color: var(--faint);
  font-variant-numeric: tabular-nums;
}
.sh-from, .sh-to {
  margin: 0; padding: clamp(14px, 1.6vw, 20px) clamp(16px, 1.8vw, 22px);
  border-radius: 14px; font-size: clamp(14px, 1.4vw, 16px); line-height: 1.45;
}
.sh-from {
  border: 1px dashed var(--line-strong);
  color: var(--muted);
  background: transparent;
}
.sh-to {
  border: 1px solid transparent;
  background: var(--brand-soft);
  color: var(--ink); font-weight: 600;
}
.sh-arrow { display: grid; place-items: center; color: var(--brand); }
.sh-arrow svg { width: 26px; height: 16px; display: block; }

@media (max-width: 760px) {
  .sh-row {
    grid-template-columns: auto minmax(0, 1fr);
    grid-template-areas:
      "n    from"
      "arr  to";
    gap: 10px clamp(12px, 3vw, 18px);
  }
  .sh-n     { grid-area: n; align-self: start; padding-top: 16px; }
  .sh-from  { grid-area: from; }
  .sh-arrow { grid-area: arr; transform: rotate(90deg); }
  .sh-to    { grid-area: to; }
}

.rv-head {
  display: flex; flex-direction: column; align-items: center;
  text-align: center; gap: 14px;
  max-width: 58rem; margin: 0 auto clamp(28px, 3.2vw, 44px);
}
.rv-head h2 { margin: 0; color: var(--ink); text-wrap: balance; }
.rv-head h2 em {
  font-family: var(--font-serif); font-style: italic; font-weight: 400;
  color: var(--accent-a);
}
.rv-head .lede { margin: 0; max-width: 44rem; color: var(--muted); }

/* ── "all questions" reads as a control, not a stray link ──────────────
   It was a bare red line of text under a stack of white cards on a white
   page — nothing marked it as something to press. The reference gives it
   an outlined pill, which is what it now is. */
:is(#faq, #cfq) .fq-all { text-align: center; margin-top: clamp(24px, 2.6vw, 34px); }
:is(#faq, #cfq) .fq-all a {
  display: inline-flex; align-items: center; gap: 9px;
  padding: 14px 30px;
  border: 1.5px solid var(--ink);
  border-radius: 12px;
  background: var(--surface);
  color: var(--ink);
  text-decoration: none;
  transition: background .25s var(--ease), color .25s var(--ease), border-color .25s;
}
:is(#faq, #cfq) .fq-all a:hover { background: var(--ink); color: var(--bg); border-color: var(--ink); }
:is(#faq, #cfq) .fq-all a:focus-visible { outline: 2px solid var(--accent-a); outline-offset: 3px; }
:is(#faq, #cfq) .fq-all a span { transition: transform .18s var(--ease); }
:is(#faq, #cfq) .fq-all a:hover span { transform: translateX(3px); }




/* ═══════════════════════════════════════════════════════════════════════
   /schedule-call/ — the booking page
   ─────────────────────────────────────────────────────────────────────
   Built to the supplied reference: a centred two-tone heading over one
   soft, generously rounded card. Fields are tall and lightly filled with
   pill-ish corners; the two answer rows are chips rather than selects,
   because four short options read faster as a row than as a dropdown.

   No type size is set here — the role layer above is authoritative and
   already covers every string on the page. The exceptions are the two it
   does not reach: the heading's <em> and the chips, which are a control
   size the layer does not carry.
   ══════════════════════════════════════════════════════════════════════ */

.sch { padding: clamp(30px, 4vw, 52px) 0 clamp(48px, 6vw, 84px); }

.sch-head {
  display: flex; flex-direction: column; align-items: center;
  text-align: center; gap: 13px;
  max-width: 48rem; margin: 0 auto clamp(26px, 3vw, 40px);
}
.sch-head :is(h1, h2) { margin: 0; color: var(--ink); text-wrap: balance; }
.sch-head :is(h1, h2) em {
  font-family: var(--font-serif); font-style: italic; font-weight: 400;
  color: var(--accent-a);
}
.sch-head .lede { margin: 0; max-width: 42rem; color: var(--muted); }

/* ── the card ─────────────────────────────────────────────────────── */
.sch-card {
  max-width: 620px; margin-inline: auto;
  padding: clamp(24px, 3vw, 40px) clamp(20px, 3vw, 40px) clamp(26px, 3vw, 40px);
  background: var(--surface);
  border: 1px solid rgba(0, 0, 0, .085);
  border-radius: clamp(18px, 2vw, 26px);
  box-shadow: 0 1px 3px rgba(0, 0, 0, .04), 0 18px 44px -30px rgba(0, 0, 0, .3);
}
.sch-card-h { margin: 0 0 clamp(20px, 2.4vw, 28px); text-align: center; color: var(--ink); }

.sch-form { display: grid; gap: clamp(16px, 1.8vw, 21px); }
.sch-field { margin: 0; display: grid; gap: 8px; border: 0; padding: 0; min-width: 0; }
.sch-field > label, .sch-field > legend {
  padding: 0; color: var(--ink);
  font-family: var(--font); font-size: 14px; font-weight: 600; line-height: 1.3;
}
/* A <legend> is NOT a grid item — the browser lifts it into the
   fieldset's border box, so the container's `gap` never reaches it. The
   two chip legends were sitting flush against their rows while every
   other label had 8px. It carries the spacing itself. */
.sch-field > legend { margin-bottom: 8px; }
.sch-field > label span {
  margin-left: 6px; color: var(--faint); font-weight: 400;
}

/* inputs: tall, softly filled, rounded — as the reference has them */
.sch-form input[type="text"],
.sch-form input[type="email"],
.sch-form input[type="tel"],
.sch-form textarea {
  width: 100%; min-width: 0;
  padding: 15px 18px;
  border: 1px solid var(--line);
  border-radius: 13px;
  background: var(--surface-2);
  color: var(--ink);
  font: inherit;
  transition: border-color .2s, background .2s, box-shadow .2s;
}
.sch-form textarea {
  resize: vertical; min-height: 92px;
  font: inherit; line-height: 1.55;
}
.sch-form input::placeholder,
.sch-form textarea::placeholder { color: var(--faint); }
.sch-form input:hover, .sch-form textarea:hover { border-color: var(--line-strong); }
.sch-form input:focus, .sch-form textarea:focus {
  outline: none;
  background: var(--surface);
  border-color: var(--accent-a);
  box-shadow: 0 0 0 3px rgba(174, 14, 32, .1);
}
.sch-form .is-bad { border-color: var(--accent-a); background: rgba(174, 14, 32, .04); }

/* ── phone: one control, two parts ─────────────────────────────────
   The <select> is a real select laid transparently over the flag — so it
   opens the platform's own picker on a phone rather than a bespoke menu
   that has to reimplement scrolling, search and keyboard support. */
.sch-tel {
  display: flex; align-items: stretch; gap: 0;
  border: 1px solid var(--line); border-radius: 13px;
  background: var(--surface-2);
  transition: border-color .2s, background .2s, box-shadow .2s;
}
.sch-tel:focus-within {
  background: var(--surface);
  border-color: var(--accent-a);
  box-shadow: 0 0 0 3px rgba(174, 14, 32, .1);
}
.sch-tel input {
  flex: 1; min-width: 0;
  border: 0 !important; background: none !important;
  box-shadow: none !important; border-radius: 0 13px 13px 0 !important;
}
/* ── the country combobox ──────────────────────────────────────────
   A filter field over a listbox, because 238 options in a <select> is a
   wheel nobody can use. The trigger shows flag + dial code; the popup
   carries the search and the list. */
.sch-cc { position: relative; flex: none; display: flex; align-items: stretch; }
.sch-cc-face {
  display: flex; align-items: center; gap: 6px;
  padding: 0 11px 0 16px; margin: 0;
  border: 0; border-right: 1px solid var(--line);
  background: none; cursor: pointer;
  color: var(--ink);
  font-family: var(--font); font-size: 14.5px; font-weight: 500; line-height: 1;
}
.sch-cc-face:hover { color: var(--accent-a); }
.sch-cc-face:focus-visible { outline: 2px solid var(--accent-a); outline-offset: -2px; border-radius: 12px 0 0 12px; }
.sch-flag { font-size: 18px; line-height: 1; }
.sch-chev { width: 11px; height: 11px; color: var(--muted); transition: transform .2s; }
.sch-cc-face[aria-expanded="true"] .sch-chev { transform: rotate(180deg); }

.sch-ccpop {
  position: absolute; z-index: 40; top: calc(100% + 7px); left: 0;
  width: min(340px, 78vw);
  display: grid; grid-template-rows: auto 1fr auto;
  background: var(--surface);
  border: 1px solid var(--line-strong);
  border-radius: 14px;
  box-shadow: 0 18px 44px -18px rgba(0, 0, 0, .34), 0 2px 6px rgba(0, 0, 0, .06);
  overflow: hidden;
}
.sch-ccpop[hidden] { display: none; }

.sch-ccq {
  display: flex; align-items: center; gap: 9px;
  padding: 11px 13px;
  border-bottom: 1px solid var(--line);
  color: var(--muted);
}
.sch-ccq svg { width: 15px; height: 15px; flex: none; }
.sch-ccq input {
  flex: 1; min-width: 0;
  border: 0 !important; background: none !important; padding: 0 !important;
  box-shadow: none !important; outline: none;
  color: var(--ink);
  font-family: var(--font); font-size: 14.5px; line-height: 1.3;
}
.sch-ccq input::placeholder { color: var(--faint); }

.sch-cclist {
  list-style: none; margin: 0; padding: 5px;
  max-height: 260px; overflow-y: auto; overscroll-behavior: contain;
}
.sch-cclist li {
  display: grid; grid-template-columns: 22px minmax(0, 1fr) auto;
  align-items: center; gap: 10px;
  padding: 9px 9px; border-radius: 9px;
  cursor: pointer;
  font-family: var(--font); font-size: 14px; line-height: 1.3;
  color: var(--ink);
}
/* highlight follows the keyboard AND the pointer, so the two never
   disagree about which row Enter would pick */
.sch-cclist li.is-on { background: var(--surface-2); }
.sch-cclist li[aria-selected="true"] { color: var(--accent-a); font-weight: 600; }
.cc-f { font-size: 17px; line-height: 1; }
.cc-n { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cc-d { color: var(--muted); font-variant-numeric: tabular-nums; }
.sch-cclist li.is-on .cc-d { color: var(--ink); }

.sch-ccnone {
  margin: 0; padding: 16px 13px; text-align: center; color: var(--muted);
  font-family: var(--font); font-size: 13.5px;
}
.sch-ccnone[hidden] { display: none; }

@media (max-width: 460px) { .sch-ccpop { width: min(320px, calc(100vw - 44px)); } }
@media (prefers-reduced-motion: reduce) { .sch-chev { transition: none; } }

/* ── chips ─────────────────────────────────────────────────────────── */
.sch-chiprow { display: flex; flex-wrap: wrap; gap: 10px; border-radius: 13px; }
/* an unanswered group has no single field to outline, so the row itself
   takes the error state */
.sch-chiprow.is-bad {
  /* 3px, not 5: .sch-field's gap is 8, so a wider offset puts the ring
     through the descenders of the legend above it */
  outline: 1.5px solid var(--accent-a);
  outline-offset: 3px;
}
.sch-chiprow button {
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  padding: 12px 20px;
  border: 1px solid transparent;
  border-radius: 12px;
  background: var(--surface-2);
  color: var(--muted);
  cursor: pointer;
  font-family: var(--font); font-size: 15px; font-weight: 500; line-height: 1.25;
  transition: background .2s, color .2s, border-color .2s, box-shadow .2s;
}
.sch-chiprow button:hover { color: var(--ink); border-color: var(--line-strong); }
.sch-chiprow button:focus-visible { outline: 2px solid var(--accent-a); outline-offset: 2px; }
/* selected: a real outline, as the reference marks it — not a fill, so the
   row still reads as a set of options rather than as one filled button */
.sch-chiprow button[aria-checked="true"] {
  background: var(--surface);
  border-color: var(--ink);
  color: var(--ink);
  box-shadow: 0 1px 2px rgba(0, 0, 0, .06);
}
.sch-ic {
  width: 17px; height: 17px; flex: none;
  background: currentColor;
  -webkit-mask: var(--g) center / contain no-repeat;
          mask: var(--g) center / contain no-repeat;
}
.sch-modes button[aria-checked="true"] .sch-ic { color: var(--accent-a); }

/* ── the calendar slot ─────────────────────────────────────────────── */
.sch-slot { display: grid; gap: 10px; }
.sch-slot[hidden] { display: none; }
.sch-slot-h {
  margin: 0; color: var(--ink);
  font-family: var(--font); font-size: 14px; font-weight: 600; line-height: 1.3;
}
.sch-cal {
  min-height: 120px;
  border: 1px solid var(--line); border-radius: 14px;
  background: var(--surface-2);
  overflow: hidden;
}
.sch-cal-fall { margin: 0; padding: 24px 18px; text-align: center; color: var(--muted); }

.sch-consent { margin: 4px 0 0; color: var(--muted); }
.sch-consent a { color: var(--accent-a); }

.sch-go { width: 100%; margin-top: 4px; }

.sch-note { margin: 0; min-height: 1.4em; color: var(--muted); text-align: center; }
.sch-note.is-bad { color: var(--accent-a); }

/* ── the three reassurance lines under the card ────────────────────── */
.sch-assure {
  list-style: none; margin: clamp(20px, 2.4vw, 30px) auto 0; padding: 0;
  display: flex; flex-wrap: wrap; justify-content: center;
  gap: 10px clamp(16px, 2.4vw, 30px);
  max-width: 620px; text-align: center;
}
.sch-assure li {
  display: flex; align-items: center; gap: 8px;
  color: var(--muted);
  font-family: var(--font); font-size: 13px; line-height: 1.4;
}
.sch-assure li::before {
  content: ""; flex: none; width: 5px; height: 5px; border-radius: 50%;
  background: var(--accent-a);
}

@media (max-width: 520px) {
  .sch-chiprow { gap: 8px; }
  .sch-chiprow button { flex: 1 1 auto; padding: 12px 14px; }
  .sch-assure { flex-direction: column; align-items: center; }
}
@media (prefers-reduced-motion: reduce) {
  .sch-form input, .sch-tel, .sch-chiprow button { transition: none; }
}


/* ── /schedule-call/ : the mode panels and the slot picker ───────────── */

.sch-panel { display: grid; gap: clamp(14px, 1.6vw, 18px); }
.sch-panel[hidden] { display: none; }
.sch-hint {
  color: var(--muted);
  font-family: var(--font); font-size: 12.5px; line-height: 1.45;
}

/* The mandatory marker, shared by every form on the site. aria-hidden on
   the span, because a screen reader already gets this from required on the
   input — announcing "star" as well is noise, not information. */
.req {
  color: var(--brand);
  font-weight: 600;
  margin-left: 1px;
}

/* With the cal.com embed live, the calendar completes the booking — so
   the form's own submit would be a second, conflicting way to finish.
   It is hidden rather than disabled: a disabled button invites a click
   that does nothing. */
.sch-form.is-embedded .sch-go,
.sch-form.is-embedded .sch-consent { display: none; }
.sch-form.is-embedded .sch-cal { min-height: 480px; background: var(--surface); }

.sch-slot-n {
  margin: -2px 0 0; color: var(--muted);
  font-family: var(--font); font-size: 12.5px; line-height: 1.5;
}

/* the picker: a week of days, each a row of times.
   Real availability lives on cal.com; this offers the opening hours and
   nothing it cannot know, so it never claims a slot is free. */
.pk-nav {
  display: flex; align-items: center; justify-content: space-between;
  gap: 12px; padding: 12px 14px;
  border-bottom: 1px solid var(--line);
  background: var(--surface);
}
.pk-nav b {
  font-family: var(--font); font-size: 14.5px; font-weight: 600; color: var(--ink);
}
.pk-arrow {
  width: 32px; height: 32px; flex: none;
  display: grid; place-items: center;
  border: 1px solid var(--line); border-radius: 9px;
  background: var(--surface); color: var(--ink);
  font-size: 17px; line-height: 1; cursor: pointer;
  transition: border-color .2s, background .2s;
}
.pk-arrow:hover:not(:disabled) { border-color: var(--ink); background: var(--surface-2); }
.pk-arrow:disabled { opacity: .35; cursor: not-allowed; }
.pk-arrow:focus-visible { outline: 2px solid var(--accent-a); outline-offset: 2px; }

.pk-days { max-height: 340px; overflow-y: auto; padding: 4px 14px; background: var(--surface); }
.pk-day { padding: 12px 0; border-bottom: 1px solid var(--line-soft); }
.pk-day:last-child { border-bottom: 0; }
.pk-dh { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; }
.pk-dh h4 {
  margin: 0; color: var(--ink);
  font-family: var(--font); font-size: 13.5px; font-weight: 600; line-height: 1.3;
}
.pk-dh span {
  color: var(--faint);
  font-family: var(--font); font-size: 12.5px; line-height: 1.3;
}
.pk-slots { display: flex; flex-wrap: wrap; gap: 7px; margin-top: 9px; }
.pk-slot {
  padding: 7px 12px;
  border: 1px solid var(--line); border-radius: 9px;
  background: var(--surface-2); color: var(--ink);
  cursor: pointer;
  font-family: var(--font); font-size: 12.5px; font-weight: 500; line-height: 1.3;
  transition: border-color .18s, background .18s, color .18s, transform .18s;
}
.pk-slot:hover { border-color: var(--ink); transform: translateY(-1px); }
.pk-slot:focus-visible { outline: 2px solid var(--accent-a); outline-offset: 2px; }
.pk-slot[aria-pressed="true"] {
  background: var(--accent-a); border-color: var(--accent-a); color: #fff;
}
.pk-tz {
  margin: 0; padding: 10px 14px;
  border-top: 1px solid var(--line);
  background: var(--surface-2); color: var(--muted);
  font-family: var(--font); font-size: 12px; line-height: 1.4; text-align: center;
}

@media (prefers-reduced-motion: reduce) { .pk-slot, .pk-arrow { transition: none; } }


/* ═══════════════════════════════════════════════════════════════════════
   /schedule-call/ — the states
   ─────────────────────────────────────────────────────────────────────
   Three tiers, by how much they should interrupt:

     field error   under the field it is about, no interruption
     banner        a whole-form condition (offline, service down), dismissible
     dialog        a terminal state — it worked, it did not, one step left

   Colour never carries the meaning alone: each tier has a glyph and its
   own words, so it still reads without it.
   ══════════════════════════════════════════════════════════════════════ */

/* ── field errors ─────────────────────────────────────────────────── */
.sch-err {
  display: flex; align-items: flex-start; gap: 6px;
  color: var(--accent-a);
  font-family: var(--font); font-size: 12.5px; line-height: 1.45;
}
.sch-err[hidden] { display: none; }
.sch-err::before {
  content: "!"; flex: none;
  width: 14px; height: 14px; margin-top: 2px;
  border: 1.4px solid currentColor; border-radius: 50%;
  font-size: 10px; font-weight: 700; line-height: 12px; text-align: center;
}

/* ── the banner ───────────────────────────────────────────────────── */
.sch-banner {
  display: flex; align-items: flex-start; gap: 10px;
  padding: 12px 12px 12px 14px;
  border: 1px solid; border-radius: 12px;
  font-family: var(--font); font-size: 13.5px; line-height: 1.5;
}
.sch-banner[hidden] { display: none; }
.sch-banner[data-kind="warn"] {
  border-color: rgba(180, 120, 0, .3); background: rgba(214, 150, 0, .07); color: #6b4c00;
}
.sch-banner[data-kind="bad"] {
  border-color: rgba(174, 14, 32, .28); background: rgba(174, 14, 32, .05); color: #7d0a17;
}
.sch-banner-ic { flex: none; width: 17px; height: 17px; margin-top: 1px; background: currentColor;
  -webkit-mask: var(--i-warn) center / contain no-repeat;
          mask: var(--i-warn) center / contain no-repeat; }
.sch-banner-tx { flex: 1; min-width: 0; }
.sch-banner-x {
  flex: none; width: 22px; height: 22px; margin: -2px -2px 0 0;
  border: 0; border-radius: 6px; background: none;
  color: inherit; opacity: .55; cursor: pointer;
  font-size: 17px; line-height: 1;
}
.sch-banner-x:hover { opacity: 1; background: rgba(0, 0, 0, .06); }

/* ── the button, working ──────────────────────────────────────────── */
.sch-go { position: relative; }
.sch-go .sch-spin { display: none; }
.sch-go.is-busy { cursor: progress; }
.sch-go.is-busy .sch-spin {
  display: block; flex: none;
  width: 15px; height: 15px; margin-left: 10px;
  border: 2px solid rgba(255, 255, 255, .28);
  border-top-color: #fff; border-radius: 50%;
  animation: sch-spin .7s linear infinite;
}
@keyframes sch-spin { to { transform: rotate(360deg); } }
.sch-go:disabled { opacity: .82; }

/* ── the dialog ───────────────────────────────────────────────────── */
.sch-dialog {
  width: min(460px, calc(100vw - 32px));
  padding: 0; border: 0; border-radius: 20px;
  background: var(--surface); color: var(--ink);
  box-shadow: 0 30px 80px -30px rgba(0, 0, 0, .45), 0 2px 8px rgba(0, 0, 0, .08);
}
.sch-dialog::backdrop { background: rgba(20, 16, 15, .42); backdrop-filter: blur(2px); }
.sch-dialog[open] { animation: sch-pop .26s cubic-bezier(.2, .8, .3, 1); }
.sch-dialog[open]::backdrop { animation: sch-fade .26s ease-out; }
@keyframes sch-pop  { from { opacity: 0; transform: translateY(10px) scale(.97); } }
@keyframes sch-fade { from { opacity: 0; } }

.sch-dlg-in { padding: clamp(24px, 3vw, 34px); text-align: center; }

/* the mark: a drawn tick, clock or cross — never colour on its own */
.sch-dlg-ic {
  display: grid; place-items: center;
  width: 46px; height: 46px; margin: 0 auto 16px;
  border-radius: 50%; position: relative;
}
.sch-dlg-ic::before {
  content: ""; position: absolute;
  background: currentColor;
  -webkit-mask: var(--g) center / contain no-repeat;
          mask: var(--g) center / contain no-repeat;
  width: 24px; height: 24px;
}
.sch-dialog[data-kind="ok"]   .sch-dlg-ic { background: rgba(16, 122, 66, .1);  color: #107a42; --g: var(--i-tick); }
.sch-dialog[data-kind="wait"] .sch-dlg-ic { background: rgba(176, 120, 0, .12); color: #8a5e00; --g: var(--i-clock2); }
.sch-dialog[data-kind="bad"]  .sch-dlg-ic { background: rgba(174, 14, 32, .09); color: var(--accent-a); --g: var(--i-warn); }

.sch-dlg-t { margin: 0; color: var(--ink); text-wrap: balance; }
.sch-dlg-lead { margin: 10px 0 0; color: var(--muted); text-wrap: pretty; }
.sch-dlg-lead[hidden], .sch-dlg-note[hidden], .sch-dlg-rows[hidden] { display: none; }

/* what was actually booked, so the dialog is a receipt and not just a mood */
.sch-dlg-rows {
  margin: 20px 0 0; padding: 14px 16px;
  border: 1px solid var(--line); border-radius: 13px;
  background: var(--surface-2);
  text-align: left;
}
.sch-dlg-rows > div {
  display: flex; align-items: baseline; justify-content: space-between; gap: 14px;
  padding: 5px 0;
}
.sch-dlg-rows > div + div { border-top: 1px dashed var(--line); }
.sch-dlg-rows dt {
  flex: none; color: var(--muted);
  font-family: var(--font); font-size: 12.5px; line-height: 1.4;
}
.sch-dlg-rows dd {
  margin: 0; min-width: 0; text-align: right; color: var(--ink);
  font-family: var(--font); font-size: 13px; font-weight: 500; line-height: 1.4;
  overflow-wrap: anywhere;
}

.sch-dlg-note {
  margin: 16px 0 0; color: var(--faint);
  font-family: var(--font); font-size: 12.5px; line-height: 1.5;
}

.sch-dlg-acts {
  display: flex; flex-wrap: wrap; gap: 10px; justify-content: center;
  margin-top: 22px;
}
.sch-dlg-b {
  flex: 1 1 auto; min-width: 120px;
  padding: 12px 20px;
  border: 1px solid var(--line-strong); border-radius: 11px;
  background: var(--surface); color: var(--ink);
  cursor: pointer; text-decoration: none; text-align: center;
  font-family: var(--font); font-size: 14.5px; font-weight: 500; line-height: 1.3;
  transition: background .2s, border-color .2s;
}
.sch-dlg-b:hover { background: var(--surface-2); border-color: var(--ink); }
.sch-dlg-b.is-primary {
  border-color: var(--ink); background: var(--ink); color: var(--bg);
}
.sch-dlg-b.is-primary:hover { filter: brightness(1.25); }
.sch-dlg-b:focus-visible { outline: 2px solid var(--accent-a); outline-offset: 2px; }

@media (prefers-reduced-motion: reduce) {
  .sch-dialog[open], .sch-dialog[open]::backdrop { animation: none; }
  .sch-go.is-busy .sch-spin { animation-duration: 2s; }
  .sch-dlg-b { transition: none; }
}


/* ═══════════════════════════════════════════════════════════════════════
   /schedule-call/ — the margins
   ─────────────────────────────────────────────────────────────────────
   The card is 620px in the middle of a 1400px page, which leaves a lot of
   nothing either side. This fills it with the brand rather than with a
   pasted logo: the mark's own halftone — the dot field that fades across
   its face — rebuilt as a CSS tile, densest at the outer edges and gone
   before it reaches the form.

   Nothing here moves the card. The decor is absolutely positioned in the
   section's own margins, capped at (100vw − the card's column) / 2 so it
   cannot reach the form even on an ultrawide screen, and it is inert:
   aria-hidden, pointer-events none, behind everything.

   It only exists above 1080px. Below that the margins are gone and a
   decorative layer would be competing for the reading column.
   ══════════════════════════════════════════════════════════════════════ */

.sch { position: relative; }
.sch > .wrap { position: relative; z-index: 1; }
.sch-decor { display: none; }

@media (min-width: 1080px) {
  .sch-decor {
    display: block;
    position: absolute; inset: 0; z-index: 0;
    overflow: hidden; pointer-events: none;
    /* the outer edge of the safe margin: half the page, less half the
       card, less a breathing gap */
    --gutter: calc((100% - 620px) / 2 - 40px);
  }
  .sch-decor > span { position: absolute; display: block; }

  /* ── the halftone ───────────────────────────────────────────────────
     One dot per 19px cell, masked twice: across, so the field thins as it
     approaches the form, and down, so it does not butt into the header or
     the footer. That double fade is what the mark does across its own
     face — this is the same idea at page scale. */
  .sch-dots {
    top: 0; bottom: 0; width: var(--gutter);
    color: var(--accent-a);
    opacity: .17;
    background-image: radial-gradient(circle at center, currentColor 1.6px, transparent 1.7px);
    background-size: 19px 19px;
  }
  .sch-dots-l {
    left: 0;
    --m: linear-gradient(to right, #000 0 22%, transparent 88%),
         linear-gradient(to bottom, transparent 0, #000 16%, #000 74%, transparent 100%);
    -webkit-mask-image: var(--m); mask-image: var(--m);
    -webkit-mask-composite: source-in; mask-composite: intersect;
  }
  .sch-dots-r {
    right: 0;
    --m: linear-gradient(to left, #000 0 22%, transparent 88%),
         linear-gradient(to bottom, transparent 0, #000 24%, #000 82%, transparent 100%);
    -webkit-mask-image: var(--m); mask-image: var(--m);
    -webkit-mask-composite: source-in; mask-composite: intersect;
  }

  /* ── the mark ───────────────────────────────────────────────────────
     Large and cropped by the left edge, so it reads as a piece of the
     brand the page is standing on rather than as a sticker placed on it.
     Bottom-anchored: it sits under the form's centre of gravity instead
     of competing with the heading. */
  .sch-mark {
    left: clamp(-190px, -9vw, -90px);
    bottom: clamp(-60px, -3vw, -20px);
    width: clamp(280px, 27vw, 460px);
    aspect-ratio: 1;
    background: url('/assets/logo-mark.webp') no-repeat center / contain;
    opacity: .12;
  }

  /* The counterweight. The big mark sits bottom-left, so the page leans
     that way; this puts a smaller one top-right. The pair makes a rising
     diagonal — which is the gesture the arrow inside the mark is already
     making, at page scale. Fainter and turned, so it reads as an echo of
     the first rather than as the same sticker twice. */
  .sch-mark-2 {
    left: auto; bottom: auto;
    right: clamp(-70px, -3.5vw, -30px);
    top: clamp(-40px, -2vw, -10px);
    width: clamp(150px, 14vw, 240px);
    opacity: .085;
    transform: rotate(-14deg);
  }

  /* ── the ascent ─────────────────────────────────────────────────────
     The mark's arrow, abstracted to a single rising rule on the opposite
     side — the one gesture the logo is actually making, at page scale.
     Dotted, so it belongs to the same family as the field it crosses. */
  .sch-rise {
    right: calc(var(--gutter) * 0.42);
    top: 14%; bottom: 12%;
    width: 1.5px;
    background: repeating-linear-gradient(to bottom,
                var(--accent-a) 0 3px, transparent 3px 11px);
    opacity: .26;
    transform: rotate(11deg);
    transform-origin: 50% 100%;
  }
  .sch-rise::after {
    content: ""; position: absolute; left: 50%; top: -1px;
    width: 9px; height: 9px; margin-left: -4.5px;
    border: 1.5px solid var(--accent-a); border-radius: 50%;
    background: var(--bg);
  }
}

/* wider still: the gutters are big enough for the mark to be a presence
   rather than a hint */
@media (min-width: 1500px) {
  .sch-mark { opacity: .15; width: clamp(400px, 30vw, 540px); }
  .sch-mark-2 { opacity: .1; width: clamp(210px, 16vw, 280px); }
  .sch-dots { opacity: .2; }
}


/* ── the two consents ─────────────────────────────────────────────────
   One is locked on, one must be given. They sit together so the person
   reads both before the button, and the required one is visually the
   live control of the pair. */
.sch-consents { gap: 10px; }
.sch-optin { margin: 0; }
.sch-optin label {
  display: flex; align-items: flex-start; gap: 10px;
  color: var(--muted); cursor: pointer;
  font-family: var(--font); font-size: 13.5px; line-height: 1.5;
}
.sch-optin input {
  flex: none; width: 16px; height: 16px; margin-top: 1px;
  /* the tick only — the label keeps its own colour */
  accent-color: #1a73e8; cursor: pointer;
}
.sch-optin input:focus-visible { outline: 2px solid var(--accent-a); outline-offset: 2px; }

/* ── the locked line ──────────────────────────────────────────────────
   Not a control. The tick is drawn, at the size and colour of the real
   checkbox beside it, so the pair reads as one group — but there is no
   input here to toggle. */
.sch-optin.is-locked { display: flex; align-items: flex-start; gap: 10px; }
.sch-optin.is-locked > span:last-child {
  color: var(--muted);
  font-family: var(--font); font-size: 13.5px; line-height: 1.5;
}
.sch-tick {
  flex: none; position: relative;
  width: 16px; height: 16px; margin-top: 1px;
  border-radius: 3px; background: #1a73e8;
}
.sch-tick::after {
  content: ""; position: absolute;
  left: 5px; top: 2px; width: 4px; height: 8px;
  border: solid #fff; border-width: 0 2px 2px 0;
  transform: rotate(43deg);
}
/* the required one carries the link, so it must not read as small print */
.sch-optin a { color: var(--accent-a); }
.sch-optin input[required] { accent-color: #1a73e8; }
.sch-optin input.is-bad { outline: 2px solid var(--accent-a); outline-offset: 2px; }

/* ── contact verification, shared by every form ───────────────────────── */
/* The Verify button sits inside the field's own box, so the control and the
   thing it acts on read as one object rather than two. */
.otp-wrap { position: relative; display: block; flex: 1 1 auto; min-width: 0; }
.otp-wrap > input { width: 100%; padding-right: 84px; }

.otp-send, .otp-tickmark {
  position: absolute; right: 6px; top: 50%; transform: translateY(-50%);
  font-family: var(--font); font-size: 12px; font-weight: 600;
  line-height: 1; white-space: nowrap;
}
.otp-send {
  padding: 7px 12px; border-radius: 7px; cursor: pointer;
  border: 1px solid var(--line); background: #fff; color: var(--ink);
  transition: border-color .15s ease, color .15s ease;
}
.otp-send:hover:not(:disabled) { border-color: var(--brand); color: var(--brand); }
.otp-send:disabled { opacity: .5; cursor: default; }

.otp-tickmark { color: #2f7d55; font-size: 15px; padding-right: 2px; }
.otp-tickmark::before { content: "\2713"; }
/* Once verified the button is gone and only a tick remains, so the field
   gets nearly all its width back. A chip wide enough to read "Verified" was
   clipping the very address it vouched for. */
.otp-wrap.is-done > input { padding-right: 26px; }

.otp { display: grid; gap: 6px; margin-top: 6px; }
.otp-state { font-size: 12.5px; color: var(--muted); }
/* The status takes over the field's own hint line, so the hint needs the
   same two states the old status element had. */
.sch-hint.is-bad, .gsf-hint.is-bad, .ct-hint.is-bad { color: var(--brand); }
.sch-hint.is-good, .gsf-hint.is-good, .ct-hint.is-good { color: #2f7d55; font-weight: 600; }
.otp-state:empty { display: none; }
.otp-state.is-bad { color: var(--brand); }
.otp-state.is-good { color: #2f7d55; font-weight: 600; }

.otp-lab {
  display: block; font-size: 12.5px; color: var(--muted);
  margin-bottom: 10px; letter-spacing: .01em;
}

/* Six boxes that SHARE the row rather than each claiming a fixed width.
   flex:1 1 0 with min-width:0 is what stops the row overflowing a narrow
   column — the earlier fixed 2.4ch could not shrink, so on the contact
   card it simply ran off the edge. max-width keeps them from becoming
   absurd on a wide form. */
.otp .otp-cells {
  display: flex; gap: clamp(7px, 2.2vw, 12px);
  width: 100%; max-width: 340px;
}
.otp .otp-cells .otp-cell {
  flex: 1 1 0; min-width: 0; max-width: 52px;
  height: 52px; padding: 0;
  text-align: center;
  font-family: var(--font); font-size: 19px; font-weight: 600;
  font-variant-numeric: tabular-nums;
  color: var(--ink); background: #fff;
  border: 1px solid var(--line); border-radius: 11px;
  transition: border-color .14s ease, box-shadow .14s ease, transform .14s ease;
  -moz-appearance: textfield;
}
.otp .otp-cells .otp-cell::-webkit-outer-spin-button,
.otp .otp-cells .otp-cell::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }

.otp .otp-cells .otp-cell:focus {
  outline: none;
  border-color: var(--brand);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--brand) 15%, transparent);
  transform: translateY(-1px);
}
/* A filled box should read as done before the row is complete. */
.otp .otp-cells .otp-cell.is-set { border-color: #a8a09b; background: #fcfbfb; }
.otp .otp-cells .otp-cell:disabled { opacity: .5; }

/* ── the OTP controls inside the Get Started card ──────────────────────
   That card inverts on purpose: --fld-* flips so a WHITE card sits on the
   dark /get-started/ page, which pins data-theme="dark" for its ground.
   The controls above were written against the PAGE's tokens and hardcode
   #fff for their own background, so inside the card they took --ink —
   near-white, because the page is dark — and painted it on that white.
   Measured 1.10:1: the Verify chip was invisible, and so were the six code
   cells, which meant a code you typed could not be read back.

   Inside the card they follow the card. A control that hardcodes its
   background cannot take its ink from a token that moves independently. */
.gsf .otp-send,
.gsf .otp .otp-cells .otp-cell {
  color: var(--fld-ink);
  background: var(--fld-in);
  border-color: var(--fld-line);
}
.gsf .otp .otp-cells .otp-cell.is-set {
  border-color: var(--fld-hint);
  background: var(--fld-in);
}
.gsf .otp-lab, .gsf .otp-state { color: var(--fld-hint); }
.gsf .otp-send:hover:not(:disabled) { border-color: var(--brand); color: var(--brand); }

/* A wrong code is worth feeling, not only reading. */
.otp.is-wrong .otp-cells .otp-cell { border-color: var(--brand); }
@media (prefers-reduced-motion: no-preference) {
  .otp.is-wrong .otp-cells { animation: otpShake .38s ease; }
}
@keyframes otpShake {
  0%, 100% { transform: translateX(0); }
  20% { transform: translateX(-5px); }
  40% { transform: translateX(4px); }
  60% { transform: translateX(-3px); }
  80% { transform: translateX(2px); }
}

/* Room to breathe. The code row is a moment of its own, not another field
   crammed against the one above it. */
.otp { display: grid; gap: 10px; margin-top: 10px; }
.otp-enter { padding: 16px 0 4px; }
.otp-hint {
  font-size: 12px; color: var(--muted); margin-top: 10px;
}
.otp-paste {
  font-family: var(--font); font-size: 12px; font-weight: 600;
  padding: 6px 12px; border-radius: 8px; cursor: pointer;
  border: 1px dashed var(--line); background: #fff; color: var(--ink);
}
.otp-paste:hover { border-color: var(--brand); color: var(--brand); }


/* ── page header ──────────────────────────────────────────────────────
   The band naming the page. Ground is plain white on request; what carries
   it instead is the site's own backdrop vocabulary — a lattice masked to
   nothing at the edges, a brand bloom beneath it, a short gradient rule,
   and the trail set in a pill rather than loose text.

   No font-size here — .pgh-h is an <h1> and the role layer in this sheet is
   authoritative on type. */
:root {
  --pgh-line: rgba(20, 19, 16, .10);
  /* Header glass. Without backdrop-filter support this is what shows, so it
     stays legible on its own; the @supports block below thins it once the
     blur is doing the work of separating the bar from the page. */
  --hdr-glass: rgba(250, 250, 249, .92);
  --hdr-glass-thin: rgba(250, 250, 249, .60);
}
:root[data-theme="dark"] { --hdr-glass: rgba(20, 20, 19, .92); --hdr-glass-thin: rgba(20, 20, 19, .58); }
:root[data-theme="dark"] { --pgh-line: rgba(255, 255, 255, .09); }

.pgh {
  position: relative; isolation: isolate; overflow: hidden;
  padding: clamp(56px, 7vw, 108px) 0 clamp(44px, 5.4vw, 76px);
  background: var(--bg);
  text-align: center;
}
/* a hairline that fades to nothing at both ends, not a rule ruled edge to
   edge — the same close the dividers elsewhere on the site use */
.pgh::after {
  content: ""; position: absolute; inset-inline: 0; bottom: 0; height: 1px;
  background: linear-gradient(to right, transparent, var(--line-strong) 24%,
                              var(--line-strong) 76%, transparent);
}

.pgh-fx { position: absolute; inset: 0; z-index: -1; pointer-events: none; overflow: hidden; }
.pgh-fx i { position: absolute; display: block; }
.pgh-grid {
  inset: 0;
  background-image:
    linear-gradient(to right, var(--pgh-line) 1px, transparent 1px),
    linear-gradient(to bottom, var(--pgh-line) 1px, transparent 1px);
  background-size: 34px 34px;
  -webkit-mask-image: radial-gradient(70% 76% at 50% 44%, #000 0, transparent 76%);
          mask-image: radial-gradient(70% 76% at 50% 44%, #000 0, transparent 76%);
}
.pgh-bloom {
  left: 50%; top: 50%; width: min(780px, 88%); height: 320px;
  transform: translate(-50%, -60%);
  background:
    radial-gradient(52% 58% at 50% 50%, var(--brand-soft) 0, transparent 72%),
    radial-gradient(40% 46% at 70% 80%, rgba(226, 120, 70, .10) 0, transparent 74%);
  filter: blur(8px);
}

.pgh-h { position: relative; margin: 0; color: var(--ink); text-wrap: balance; }
.pgh-rule {
  display: block; width: 76px; height: 3px; border-radius: 3px;
  margin: clamp(14px, 1.6vw, 20px) auto 0;
  background: linear-gradient(to right, transparent, var(--brand) 28%,
                              var(--brand) 72%, transparent);
}

.pgh-crumbs { margin: clamp(16px, 1.8vw, 22px) 0 0; }
.pgh-crumbs ol {
  list-style: none; margin: 0;
  display: inline-flex; flex-wrap: wrap; justify-content: center; align-items: center;
  gap: 8px; padding: 7px 18px;
  border: 1px solid var(--line); border-radius: 999px;
  background: var(--surface);
  font-size: 14px; line-height: 1.5; color: var(--muted);
}
.pgh-crumbs li { display: flex; align-items: center; gap: 8px; }
.pgh-crumbs li + li::before { content: "\203A"; color: var(--faint); }
.pgh-crumbs a { color: var(--muted); text-decoration: none; transition: color .2s var(--ease); }
.pgh-crumbs a:hover { color: var(--ink); }
.pgh-crumbs [aria-current="page"] { color: var(--ink); font-weight: 600; }
.pgh-crumbs a:focus-visible { outline: 2px solid var(--brand); outline-offset: 3px; border-radius: 3px; }


/* ── the header over a subpage ────────────────────────────────────────
   Frosted rather than solid: a white-grey wash thin enough that whatever
   scrolls beneath reads through it, blurred. The landing page is excluded
   — #ascent is its cloud hero and exists nowhere else — because there the
   bar is deliberately absent and the three controls float over the sky.

   Two layers on purpose. The first is nearly opaque and is what a browser
   without backdrop-filter gets, so the bar stays readable there. The
   second thins it once the blur is available to do the separating, which
   is what makes the page visible through the glass. */
:root:not(:has(#ascent)) .site-header,
:root:not(:has(#ascent)) .site-header.scrolled {
  background: var(--hdr-glass);
  border-bottom: 1px solid var(--line);
  box-shadow: 0 1px 3px rgba(0, 0, 0, .04);
}
@supports ((backdrop-filter: blur(2px)) or (-webkit-backdrop-filter: blur(2px))) {
  :root:not(:has(#ascent)) .site-header,
  :root:not(:has(#ascent)) .site-header.scrolled {
    background: var(--hdr-glass-thin);
    -webkit-backdrop-filter: blur(18px) saturate(180%);
            backdrop-filter: blur(18px) saturate(180%);
  }
}

/* ...but not while the drawer is open.

   backdrop-filter makes an element a containing block for position:fixed
   descendants, and .mobile-nav is a descendant: it lives inside the bar.
   With the filter up, the drawer's `inset: var(--hdr) 0 0` resolved against
   the 65px bar instead of the viewport, so it computed to a negative height,
   clamped to 0, and the burger opened nothing you could see. It applies to
   both header states and on every page without #ascent, which is all of them
   but the landing page, so the drawer was dead site-wide on touch widths.

   Dropping the blur here costs nothing: a full-screen drawer is already over
   the page, and the only thing left to frost is the strip behind the bar.
   That puts us in the no-backdrop-filter case, so the bar takes the
   nearly-opaque first layer that case is already written for.

   Keyed on :not([hidden]) rather than [data-nav="open"] because the close
   sets data-nav="closed" immediately and only unhides 440ms later, when the
   panels have finished leaving — the filter must stay off for that exit or
   the drawer collapses to 0 halfway through it. */
:root:not(:has(#ascent)) .site-header:has(.mobile-nav:not([hidden])) {
  background: var(--hdr-glass);
  -webkit-backdrop-filter: none;
          backdrop-filter: none;
}


/* ── page transitions ─────────────────────────────────────────────────
   Moving between pages used to be a hard cut: the old page vanished, the
   next one appeared, and on a slow connection there was a white gap in
   between. This makes the change readable.

   It is the browser's own cross-document view transition, so there is no
   router, no link interception and no fetching ahead. The navigation is
   the ordinary one it always was — the browser holds the outgoing page on
   screen, prepares the next one, and animates between the two. That also
   means the back button, bfcache, middle-click and Open in new tab all keep
   working exactly as before, which is the part a hand-rolled version gets
   wrong. Firefox has not shipped it yet and simply navigates as it does
   today; nothing is conditional on support.

   Naming the header is what makes the change feel like one site rather than
   two documents. A named element is lifted out of the root snapshot and
   handed across the navigation as the same object, so the bar stays put
   while the page underneath it moves. It also means the bar animates its own
   difference: arriving from a scrolled page, the condensed 1300px bar eases
   back out to its full width instead of snapping. */
@view-transition { navigation: auto; }

.site-header { view-transition-name: site-header; }

/* the page beneath: out quickly, in with a little more room, so the new
   page reads as arriving rather than the old one as being erased */
::view-transition-old(root) { animation: pt-leave .2s var(--ease) both; }
::view-transition-new(root) { animation: pt-enter .42s var(--ease) both; }
@keyframes pt-leave { to   { opacity: 0; transform: translateY(-6px); } }
@keyframes pt-enter { from { opacity: 0; transform: translateY(14px); } }

/* ── navigation progress ──────────────────────────────────────────────
   The transition covers the wait but says nothing about it. This is the
   only thing on screen between the tap and the next page on a slow
   connection, so it holds short of the end rather than completing: a bar
   that fills before the page arrives is a lie, and the second time someone
   sees it they stop believing the first. nav.js adds and removes it. */
.nav-load {
  position: fixed; top: 0; left: 0; z-index: 90;
  display: block; width: 100%; height: 2px;
  background: var(--brand);
  transform: scaleX(0); transform-origin: 0 50%;
  pointer-events: none;
}
.nav-load[data-run] {
  transform: scaleX(.9);
  transition: transform 8s cubic-bezier(.05, .7, .1, 1);
}

@media (prefers-reduced-motion: reduce) {
  ::view-transition-old(root), ::view-transition-new(root) { animation: none; }
  .nav-load[data-run] { transition: none; transform: scaleX(.9); }
}

/* ── the waiting state ────────────────────────────────────────────────
   The founder's loader component, ported from React. The grid geometry and
   the ripple are the component's own; the panel, the reason line and the
   error state are additions, because a loading screen that will not say
   what it is waiting for is worse than no loading screen. */
.shv-load {
  position: fixed; inset: 0; z-index: 200;
  display: grid; place-items: center;
  background: rgba(250, 250, 249, .58);
  -webkit-backdrop-filter: blur(14px) saturate(160%);
          backdrop-filter: blur(14px) saturate(160%);
}
.shv-load[hidden] { display: none; }
.shv-loading, .shv-loading body { overflow: hidden; }

.shv-load-in {
  display: flex; flex-direction: column; align-items: center; gap: 24px;
  width: min(468px, calc(100vw - 32px));
  padding: clamp(42px, 5vw, 54px) clamp(24px, 3vw, 34px) clamp(30px, 4vw, 42px);
  text-align: center;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 20px;
  box-shadow: 0 1px 2px rgba(var(--sh), .04), 0 18px 50px -16px rgba(var(--sh), .22);
  animation: shv-rise .32s var(--ease) both;
  position: relative;
}
/* The mark straddles the card's top border — half of it above the edge,
   half inside — so the panel is signed rather than anonymous. It rides the
   same border it interrupts, which is why it carries the card's own
   hairline and ground rather than floating free. */
.shv-load-in::before {
  content: "";
  position: absolute; top: 0; left: 50%;
  width: 62px; height: 62px;
  transform: translate(-50%, -50%);
  border-radius: 50%;
  border: 1px solid var(--line);
  background: var(--surface) url('/assets/logo-mark.webp') no-repeat center / 36px 36px;
  box-shadow: 0 4px 14px -6px rgba(var(--sh), .26);
}
@keyframes shv-rise {
  from { opacity: 0; transform: translateY(10px) scale(.985); }
}
@media (prefers-reduced-motion: reduce) { .shv-load-in { animation: none; } }

.shv-load-grid {
  --cell-size: 52px;
  --cell-spacing: 1px;
  --cells: 3;
  --total-size: calc(var(--cells) * (var(--cell-size) + 2 * var(--cell-spacing)));
  display: flex; flex-wrap: wrap;
  width: var(--total-size); height: var(--total-size);
}
.shv-cell {
  --cell-rest: rgba(20, 19, 16, .055);
  flex: 0 0 var(--cell-size);
  margin: var(--cell-spacing);
  background-color: var(--cell-rest);
  box-sizing: border-box;
  border-radius: 4px;
  animation: 1.5s shv-ripple ease infinite;
}
.shv-cell.d-1 { animation-delay: 100ms; }
.shv-cell.d-2 { animation-delay: 200ms; }
.shv-cell.d-3 { animation-delay: 300ms; }
.shv-cell.d-4 { animation-delay: 400ms; }

.shv-cell:nth-child(1) { --cell-color: #8A0007; }
.shv-cell:nth-child(2) { --cell-color: #9B0A16; }
.shv-cell:nth-child(3) { --cell-color: #AE0E20; }
.shv-cell:nth-child(4) { --cell-color: #BC1A28; }
.shv-cell:nth-child(5) { --cell-color: #C93030; }
.shv-cell:nth-child(6) { --cell-color: #D6483A; }
.shv-cell:nth-child(7) { --cell-color: #E26046; }
.shv-cell:nth-child(8) { --cell-color: #EB7A52; }
.shv-cell:nth-child(9) { --cell-color: #F2945F; }

@keyframes shv-ripple {
  0%   { background-color: var(--cell-rest); }
  30%  { background-color: var(--cell-color); }
  60%  { background-color: var(--cell-rest); }
  100% { background-color: var(--cell-rest); }
}

/* the reason. No font-size on the <b>: it is a card title, and the role
   layer above owns that. The note under it is meta text, which this file
   sizes directly wherever it appears. */
/* the reason sits on its own glass, so it reads as the thing being said
   rather than as a caption under the animation */
.shv-load-why {
  margin: 0; width: 100%; max-width: 30rem;
  padding: 18px 20px 20px;
  border: 1px solid var(--line);
  border-radius: 16px;
  background: rgba(255, 255, 255, .55);
  -webkit-backdrop-filter: blur(10px) saturate(150%);
          backdrop-filter: blur(10px) saturate(150%);
}
.shv-load-why b { display: flex; justify-content: center; }
.shv-load-why b > span {
  display: inline-flex; align-items: center; gap: 9px;
  padding: 7px 15px 7px 12px; border-radius: 999px;
  border: 1px solid var(--line); background: var(--surface);
  -webkit-backdrop-filter: blur(6px); backdrop-filter: blur(6px);
  font-size: 12px; font-weight: 600; line-height: 1.4;
  letter-spacing: 0.1em; text-transform: uppercase;
  color: var(--muted);
}
/* the failure reads in the brand, and its border follows */
.shv-load.is-bad .shv-load-why b > span { color: var(--brand); border-color: var(--brand-line); }

/* the dot, and the ring it sends out — the hero's .ping, same 7px centre
   and same 2.4s travel, so the two badges breathe together */
.shv-dot {
  position: relative; flex: none;
  width: 7px; height: 7px; border-radius: 50%;
  background: var(--brand);
}
.shv-dot::after {
  content: ""; position: absolute; inset: -4px;
  border-radius: 50%; border: 1.5px solid var(--brand); opacity: 0;
  animation: ping 2.4s var(--ease) infinite;
}
/* a failure is not still working, so the ring stops travelling and holds */
.shv-load.is-bad .shv-dot::after { animation: none; opacity: .4; transform: scale(1.2); }
@media (prefers-reduced-motion: reduce) { .shv-dot::after { animation: none; } }
.shv-load-why > span:not(.shv-load-act):not(.shv-load-bar) {
  display: block; margin-top: 6px;
  font-size: 15px; line-height: 1.5; color: var(--muted);
}

/* the failed state — the ripple stops, because nothing is happening */
.shv-load.is-bad .shv-load-grid { opacity: .85; }

/* the two ways out of the failed state */
.shv-load-act {
  display: flex; flex-wrap: wrap; gap: 10px; justify-content: center;
  margin-top: 18px;
}
/* below 420px there is no honest way to fit them side by side */
@media (max-width: 420px) { .shv-load-act .btn { width: 100%; } }
.shv-load-act .btn { cursor: pointer; white-space: nowrap; }
.shv-load-act { align-items: center; }
/* the seconds left, inside the button. tabular figures so the label does not
   jitter as the count goes 10 → 9 */
.shv-tick { display: inline; font-variant-numeric: tabular-nums; opacity: .8; }

/* the same countdown as a bar, so it reads without counting digits */
.shv-load-bar {
  display: block;
  width: min(260px, 70vw); height: 3px; margin: 16px auto 0;
  border-radius: 3px; background: var(--line); overflow: hidden;
}
.shv-load-bar i {
  display: block; height: 100%; width: 100%;
  border-radius: 3px; transform-origin: left center;
  background: linear-gradient(to right, var(--brand), #F2945F);
  transition: transform 1s linear;
}
@media (prefers-reduced-motion: reduce) { .shv-load-bar i { transition: none; } }
.shv-load.is-bad .shv-load-why b { color: var(--brand); }

/* A looping animation is exactly what reduced motion is about. The cells
   hold their colour instead of pulsing, so it still reads as busy. */
@media (prefers-reduced-motion: reduce) {
  .shv-cell { animation: none; background-color: var(--cell-color); opacity: .55; }
}

@media (max-width: 480px) { .shv-load-grid { --cell-size: 40px; } }


/* ── /about/ — how the work runs ──────────────────────────────────────
   Ported from the supplied React component. Its motion was framer-motion:
   a scroll-triggered container with a per-child stagger, floating blobs on
   a parallax, and spring-counted figures. All of it is CSS here except the
   observer that adds .in — the same approach section.about already takes,
   and the reason neither framer-motion nor a bundler is needed.

   The reference's palette (#88734C brass on #F2F2EB) is replaced by the
   site's own tokens; its four invented figures by the real STATS block. */
.au {
  position: relative; isolation: isolate;
  margin: clamp(28px, 3vw, 44px) 0 0;
  display: flex; flex-direction: column; justify-content: center;
}
/* fills what is left of the viewport under the header and the page banner */
@media (min-width: 900px) and (min-height: 720px) {
  .au { min-height: calc(100svh - 320px); }
}

.au-fx { position: absolute; inset: -40px 0; z-index: -1; pointer-events: none; overflow: hidden; }
.au-fx i { position: absolute; display: block; border-radius: 50%; filter: blur(60px); }
.au-b1 { top: 4%; left: -4%; width: 320px; height: 320px; background: var(--brand-soft); }
.au-b2 { bottom: 6%; right: -6%; width: 380px; height: 380px; background: rgba(226, 120, 70, .10); }

.au-head { display: flex; flex-direction: column; align-items: center; text-align: center;
           max-width: 46rem; margin: 0 auto clamp(26px, 3vw, 42px); gap: 12px; }
.au-head h2 { margin: 0; text-wrap: balance; }
.au-head h2 em {
  font-family: var(--font-serif); font-style: italic; font-weight: 400;
  background: linear-gradient(to right, var(--brand), #8a0007, var(--brand));
  -webkit-background-clip: text; background-clip: text; color: transparent;
  padding-right: 4px; -webkit-box-decoration-break: clone; box-decoration-break: clone;
}
.au-head .lede { margin: 0; }
.au-rule { display: block; width: 76px; height: 3px; border-radius: 3px;
           background: linear-gradient(to right, transparent, var(--brand) 28%, var(--brand) 72%, transparent); }

/* three columns: services, the centre spine, services.

   The reference hangs its two columns off a tall photograph. Ours carries the
   figures under that photograph as well, so the spine runs image → call to
   action → figures and the side columns spread to meet its height. */
.au-grid { display: grid; gap: clamp(22px, 3vw, 40px); align-items: stretch; }
@media (min-width: 900px) { .au-grid { grid-template-columns: 1fr minmax(0, clamp(330px, 30vw, 460px)) 1fr; } }

.au-col { list-style: none; margin: 0; padding: 0; display: grid; gap: clamp(18px, 2vw, 28px); }
/* the spine leads on a phone, the way the reference orders it */
@media (max-width: 899px) {
  .au-mid { order: -1; max-width: 460px; margin-inline: auto; }
}
@media (min-width: 900px) { .au-col { align-content: center; gap: clamp(26px, 3.2vw, 46px); } }

.au-link {
  display: flex; gap: 14px; text-decoration: none; color: inherit;
  border-radius: 14px; padding: 4px;
  transition: transform .3s var(--ease);
}
.au-link:hover { transform: translateY(-3px); }
.au-link:focus-visible { outline: 2px solid var(--brand); outline-offset: 4px; }

.au-ic {
  position: relative; flex: none; display: grid; place-items: center;
  width: 46px; height: 46px; border-radius: 12px;
  background: var(--brand-soft); color: var(--brand);
  transition: background .3s var(--ease), transform .45s var(--ease);
}
.au-ic svg { width: 22px; height: 22px; }
/* the reference pins a small mark to the corner of every icon tile; a
   four-point star drawn in CSS saves six more inline SVGs */
.au-ic::after {
  content: ""; position: absolute; top: -5px; right: -5px;
  width: 13px; height: 13px; background: var(--brand); opacity: .34;
  clip-path: polygon(50% 0, 60% 40%, 100% 50%, 60% 60%, 50% 100%, 40% 60%, 0 50%, 40% 40%);
  transition: opacity .3s var(--ease), transform .45s var(--ease);
}
.au-link:hover .au-ic { background: rgba(174, 14, 32, .14); transform: rotate(-6deg); }
.au-link:hover .au-ic::after { opacity: 1; transform: rotate(90deg) scale(1.15); }

.au-tx b { display: block; color: var(--ink); margin-bottom: 5px; }
.au-tx > span { display: block; font-size: 15px; line-height: 1.55; color: var(--muted); }
.au-tx em {
  display: inline-flex; align-items: center; gap: 5px; margin-top: 8px;
  font-style: normal; font-size: 13px; font-weight: 600; color: var(--brand);
  opacity: 0; transform: translateX(-4px);
  transition: opacity .3s var(--ease), transform .3s var(--ease);
}
.au-link:hover .au-tx em, .au-link:focus-visible .au-tx em { opacity: 1; transform: none; }

/* ── the centre spine ─────────────────────────────────────────────────── */
.au-mid { display: flex; flex-direction: column; align-items: center; }

/* the framed photograph — the reference offsets a border behind it */
.au-art { position: relative; margin: 0; width: 100%; }
.au-art img { display: block; width: 100%; height: auto; border-radius: 14px;
              aspect-ratio: 3 / 4; object-fit: cover; }
.au-art::before {
  content: ""; position: absolute; inset: -12px; z-index: -1;
  border: 3px solid var(--brand-line); border-radius: 18px;
}
/* the caption moves to the head of the frame, because the call to action now
   sits where it used to */
.au-art figcaption {
  position: absolute; left: 12px; top: 12px;
  padding: 7px 12px; border-radius: 999px;
  background: rgba(255, 255, 255, .82);
  -webkit-backdrop-filter: blur(8px); backdrop-filter: blur(8px);
  font-size: 11px; font-weight: 600; letter-spacing: .06em; text-transform: uppercase;
  color: var(--ink);
}

/* straddles the foot of the frame, as the reference's does */
.au-cta { margin-top: -22px; position: relative; z-index: 1; box-shadow: 0 10px 26px rgba(20, 19, 16, .18); }

/* ── the story ─────────────────────────────────────────────────────────
   Set across the page rather than down a centred column: the heading takes a
   left rail and holds position while the matter scrolls past it. */
.abst { margin-top: clamp(52px, 6vw, 96px); }
.abst-grid { display: grid; gap: clamp(26px, 3.4vw, 56px); }
@media (min-width: 980px) {
  .abst-grid { grid-template-columns: minmax(0, 27rem) minmax(0, 1fr); align-items: start; }
  .abst-head { position: sticky; top: 104px;   /* the sticky header, plus air */ }
}
.abst-head { display: flex; flex-direction: column; align-items: flex-start;
             text-align: left; gap: 13px; }
.abst-head h2 { margin: 0; text-wrap: balance; }
.abst-head h2 em {
  font-family: var(--font-serif); font-style: italic; font-weight: 400;
  background: linear-gradient(to right, var(--brand), #8a0007, var(--brand));
  -webkit-background-clip: text; background-clip: text; color: transparent;
  padding-right: 4px; -webkit-box-decoration-break: clone; box-decoration-break: clone;
}
.abst-stand { margin: 2px 0 0; color: var(--muted); }

.abst-body { display: grid; gap: 1.15em; max-width: 68ch; }
.abst-body > p { margin: 0; color: var(--muted); line-height: 1.78; }
.abst-body b { color: var(--ink); font-weight: 600; }
/* a capital on the paragraph that turns from what we do to who we are */
.abst-drop::first-letter {
  float: left; font-family: var(--font-serif); font-size: 3.1em; line-height: .82;
  padding: .06em .1em 0 0; color: var(--brand);
}
.abst-pull {
  margin: .5em 0; padding: 2px 0 2px clamp(18px, 2vw, 26px);
  border-left: 3px solid var(--brand-line);
}
.abst-pull p { margin: 0; font-style: italic; color: var(--ink); }
.abst-pull b { font-style: normal; font-weight: 600; }

/* ── the rest of the section, parked in the rail ──────────────────────
   The heading column runs out of copy long before the prose beside it does.
   Rather than leave the gap, it carries the section's other pages. */
.abnav {
  margin-top: clamp(22px, 2.6vw, 34px); width: 100%; max-width: 27rem;
  padding: 6px; border: 1px solid var(--line); border-radius: 16px;
  background: rgba(255, 255, 255, .6);
  -webkit-backdrop-filter: blur(8px); backdrop-filter: blur(8px);
}
.abnav-k { margin: 10px 12px 8px; color: var(--muted); }
.abnav ul { list-style: none; margin: 0; padding: 0; display: grid; gap: 2px; }
.abnav a {
  display: flex; align-items: center; gap: 12px;
  padding: 11px 12px; border-radius: 11px;
  text-decoration: none; color: inherit;
  transition: background .2s, transform .2s var(--ease);
}
.abnav a:hover { background: var(--brand-soft); transform: translateX(2px); }
.abnav a:focus-visible { outline: 2px solid var(--brand); outline-offset: -2px; }
.abnav b { display: block; color: var(--ink); }
.abnav em { display: block; margin-top: 1px; font-style: normal; color: var(--muted); }
.abnav i {
  flex: none; margin-left: auto; font-style: normal; color: var(--brand);
  opacity: 0; transform: translateX(-4px);
  transition: opacity .2s var(--ease), transform .2s var(--ease);
}
.abnav a:hover i, .abnav a:focus-visible i { opacity: 1; transform: none; }

/* ── the maxim, given its own band so it reads as a statement ─────────── */
.abstand { margin-top: clamp(46px, 5.5vw, 84px); }
.abstand-k { margin: 0 0 12px; color: var(--muted); }
.abstand-say { margin: 0 0 clamp(26px, 3vw, 40px); max-width: 26ch; color: var(--ink); }
.abstand-say span {
  font-family: var(--font-serif); font-style: italic; font-weight: 400;
  background: linear-gradient(to right, var(--brand), #8a0007, var(--brand));
  -webkit-background-clip: text; background-clip: text; color: transparent;
  -webkit-box-decoration-break: clone; box-decoration-break: clone;
}
.abstand-list { list-style: none; margin: 0; padding: 0;
                display: grid; gap: clamp(14px, 1.7vw, 22px); }
@media (min-width: 820px) { .abstand-list { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
.abstand-list li {
  padding: clamp(22px, 2.6vw, 32px);
  border: 1px solid var(--line); border-radius: 18px; background: #fff;
  border-top: 3px solid var(--brand-line);
  transition: border-color .3s, transform .3s var(--ease);
}
.abstand-list li:hover { border-color: var(--brand-line); transform: translateY(-3px); }
.abstand-list b { display: block; margin-bottom: 8px; color: var(--ink); }
.abstand-list p { margin: 0; color: var(--muted); }
.abstand-close { margin: clamp(24px, 2.8vw, 38px) 0 0; max-width: 62ch; color: var(--muted); }
.abstand-close b { color: var(--ink); font-weight: 600; }

/* ── the figures: their own ground, well clear of the photograph ──────── */
.abfig {
  margin-top: clamp(46px, 5.5vw, 84px);
  padding: clamp(30px, 3.6vw, 52px) clamp(18px, 2.4vw, 34px);
  border: 1px solid var(--line); border-radius: 22px;
  background: var(--io-ground, #fafafa);
}
.abfig-h { margin: 0 0 clamp(20px, 2.2vw, 30px); text-align: center; color: var(--muted); }
.au-stats {
  list-style: none; margin: 0; padding: 0;
  display: grid; gap: clamp(12px, 1.4vw, 18px);
  grid-template-columns: repeat(2, minmax(0, 1fr));
}
@media (min-width: 860px) { .au-stats { grid-template-columns: repeat(4, minmax(0, 1fr)); } }
.au-stats li {
  padding: clamp(20px, 2.4vw, 30px) 14px; text-align: center;
  border: 1px solid var(--line); border-radius: 16px; background: #fff;
  transition: transform .3s var(--ease), border-color .3s;
}
.au-stats li:hover { transform: translateY(-3px); border-color: var(--brand-line); }
.au-stats b { display: block; font-family: var(--font-display); font-size: clamp(28px, 3.4vw, 42px);
              font-weight: 600; letter-spacing: -.02em; color: var(--ink); font-variant-numeric: tabular-nums; }
.au-stats span { display: block; margin-top: 3px; font-size: 12px; font-weight: 600;
                 letter-spacing: .07em; text-transform: uppercase; color: var(--brand); }
.au-stats em { display: block; margin-top: 9px; font-style: normal;
               font-size: 13px; line-height: 1.5; color: var(--muted); }

/* ── vision and mission, the pair the reference sets side by side ─────── */
.abvm { margin-top: clamp(46px, 5.5vw, 84px); }
.abvm-list { list-style: none; margin: 0; padding: 0; display: grid; gap: clamp(16px, 1.8vw, 24px); }
@media (min-width: 780px) { .abvm-list { grid-template-columns: 1fr 1fr; } }
.abvm-list li {
  padding: clamp(24px, 2.8vw, 36px);
  border: 1px solid var(--line); border-radius: 20px; background: #fff;
  transition: border-color .3s, transform .3s var(--ease);
}
.abvm-list li:hover { border-color: var(--brand-line); transform: translateY(-3px); }
.abvm-k { display: inline-block; margin-bottom: 12px; padding: 5px 12px; border-radius: 999px;
          background: var(--brand-soft); color: var(--brand); }
.abvm-list b { display: block; margin-bottom: 8px; color: var(--ink); }
.abvm-list p { margin: 0; color: var(--muted); line-height: 1.65; }
.abvm-pledge {
  margin: clamp(28px, 3.2vw, 44px) auto 0; max-width: 40ch; text-align: center;
  font-style: italic; color: var(--ink);
}

/* ── contact, split in half ────────────────────────────────────────────
   The supplied reference centres a single column. Halved, the invitation
   takes one side and the form the other; they stack under 900px. */
.ctx { margin-top: clamp(46px, 5.5vw, 84px); }
.ctx-grid { display: grid; gap: clamp(28px, 3.4vw, 56px); align-items: start; }
@media (min-width: 900px) { .ctx-form { position: sticky; top: 104px; } }
@media (min-width: 900px) { .ctx-grid { grid-template-columns: 1fr 1fr; } }

.ctx-faq h2 { margin: 0; text-wrap: balance; }
.ctx-faq h2 em {
  font-family: var(--font-serif); font-style: italic; font-weight: 400;
  background: linear-gradient(to right, var(--brand), #8a0007, var(--brand));
  -webkit-background-clip: text; background-clip: text; color: transparent;
  padding-right: 4px; -webkit-box-decoration-break: clone; box-decoration-break: clone;
}
.ctx-faq .pill-note { margin-bottom: 14px; }
.ctx-sub { margin: 12px 0 0; color: var(--muted); }
.ctx-sub a { color: var(--brand); text-decoration: none; border-bottom: 1px solid var(--brand-line); }
.ctx-sub a:hover { border-bottom-color: var(--brand); }

/* The accordion is built for a full-width band; in half a column the cards
   need their padding pulled in and the Help Centre link left-aligned. */
.ctx-faq .faq { margin-top: clamp(18px, 2vw, 26px); }
.ctx-faq .fq-q { padding-right: 8px; }
.ctx-faq .fq-all { text-align: left; }

/* ── the form ─────────────────────────────────────────────────────────── */
.ctx-form {
  padding: clamp(22px, 2.8vw, 34px);
  border: 1px solid var(--line); border-radius: 20px; background: #fff;
}
.ctx-f { margin: 0 0 16px; }
.ctx-f label { display: block; margin-bottom: 7px; color: var(--ink); }
.ctx-f .req { color: var(--brand); }

/* The reference's rounded field with a leading icon. otp.js re-parents the
   input into an injected span.otp-wrap, so the wrapper is styled to flex the
   same way — otherwise the verified fields collapse the moment it runs. */
.ctx-ip {
  display: flex; align-items: center; gap: 8px;
  padding-left: 13px; height: 44px;
  border: 1px solid var(--line); border-radius: 999px;
  background: #fff; overflow: hidden;
  transition: border-color .2s, box-shadow .2s;
}
.ctx-ip:focus-within { border-color: var(--brand); box-shadow: 0 0 0 3px var(--brand-soft); }
.ctx-ip > svg { width: 19px; height: 19px; flex: none; color: var(--muted); }
.ctx-ip .otp-wrap { display: flex; align-items: center; flex: 1 1 auto; min-width: 0; height: 100%; }
.ctx-ip input {
  flex: 1 1 auto; min-width: 0; height: 100%;
  padding: 0 6px; border: 0; outline: none; background: transparent; color: var(--ink);
}
.ctx-ip input.is-bad, .ctx-form textarea.is-bad { color: var(--brand); }
.ctx-f:has(.is-bad) .ctx-ip { border-color: var(--brand); }
/* the Verify button sits inside the pill, so it loses its own rounding */
.ctx-ip .otp-send { flex: none; margin-right: 4px; }

.ctx-form textarea {
  display: block; width: 100%; padding: 11px 13px; resize: vertical;
  border: 1px solid var(--line); border-radius: 14px;
  background: #fff; color: var(--ink); outline: none;
  transition: border-color .2s, box-shadow .2s;
}
.ctx-form textarea:focus { border-color: var(--brand); box-shadow: 0 0 0 3px var(--brand-soft); }
.ctx-form .ct-go {
  display: flex; align-items: center; justify-content: center; gap: 7px;
  width: 100%; margin-top: 20px; padding: 12px 18px;
  border: 0; border-radius: 999px; cursor: pointer;
  background: var(--brand); color: #fff;
  transition: background .2s, transform .2s var(--ease);
}
.ctx-form .ct-go svg { width: 19px; height: 19px; }
.ctx-form .ct-go:hover:not(:disabled) { background: #8a0007; transform: translateY(-1px); }
.ctx-form .ct-go:disabled { opacity: .6; cursor: default; }
.ctx-form .ct-note { margin: 12px 0 0; text-align: center; color: var(--muted); min-height: 1.2em; }
.ctx-form .ct-note.is-bad { color: var(--brand); }
/* the honeypot must be reachable by a bot and invisible to a person */
.ctx-form .ct-trap { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }

/* ── the reveal ───────────────────────────────────────────────────────
   One class on the section, the index carried in --n. That is the whole of
   the reference's staggerChildren, without the library. */
.au [style*="--n"], .au-head, .au-art, .au-cta {
  opacity: 0; transform: translateY(16px);
  transition: opacity .6s var(--ease), transform .6s var(--ease);
  transition-delay: calc(var(--n, 0) * .09s);
}
.au.in [style*="--n"], .au.in .au-head, .au.in .au-art, .au.in .au-cta {
  opacity: 1; transform: none;
}
.au.in .au-art { transition-delay: .15s; }
.au.in .au-cta { transition-delay: .3s; }
/* the story, the figures' heading and the vision pair each wait for their own
   arrival — one observer keyed on [data-reveal] drives all three */
[data-reveal] .abst-head, [data-reveal] .abst-body > p,
[data-reveal] .abfig-h, [data-reveal] .abstand-k,
[data-reveal] .abstand-say, [data-reveal] .abstand-close,
[data-reveal] .abstand-list li, [data-reveal] .ctx-faq, [data-reveal] .ctx-form,
[data-reveal] .abvm-list li, [data-reveal] .abvm-pledge {
  opacity: 0; transform: translateY(16px);
  transition: opacity .6s var(--ease), transform .6s var(--ease),
              border-color .3s;
  transition-delay: calc(var(--n, 0) * .09s);
}
[data-reveal].in .abst-head, [data-reveal].in .abst-body > p,
[data-reveal].in .abfig-h, [data-reveal].in .abstand-k,
[data-reveal].in .abstand-say, [data-reveal].in .abstand-close,
[data-reveal].in .abstand-list li, [data-reveal].in .ctx-faq, [data-reveal].in .ctx-form,
[data-reveal].in .abvm-list li, [data-reveal].in .abvm-pledge {
  opacity: 1; transform: none;
}
.ctx.in .ctx-form { transition-delay: .12s; }
.abst.in .abst-body > p:nth-child(1) { transition-delay: .06s; }
.abst.in .abst-body > p:nth-child(2) { transition-delay: .12s; }
.abst.in .abst-body > p:nth-child(3) { transition-delay: .18s; }
.abst.in .abst-body > p:nth-child(4) { transition-delay: .24s; }
.abvm.in .abvm-pledge { transition-delay: .24s; }
.abstand.in .abstand-say { transition-delay: .08s; }
.abstand.in .abstand-close { transition-delay: .3s; }

/* The figures wait for their own turn. The section is taller than the
   viewport, so its own observer fires while the figures are still well below
   the fold — counting them there means the count is over before it is seen.
   They reveal and count when they themselves arrive. */
.au-stats li {
  opacity: 0; transform: translateY(16px);
  transition: opacity .6s var(--ease), transform .6s var(--ease),
              border-color .3s;
}
.au-stats.in li { opacity: 1; transform: none; }
.au-stats.in li:nth-child(2) { transition-delay: .09s; }
.au-stats.in li:nth-child(3) { transition-delay: .18s; }
.au-stats.in li:nth-child(4) { transition-delay: .27s; }

@media (prefers-reduced-motion: reduce) {
  .au [style*="--n"], .au-head, .au-art, .au-cta, .au-stats li {
    opacity: 1; transform: none; transition: none;
  }
  .au-link, .au-ic, .au-ic::after, .au-stats li,
  .abvm-list li, .abstand-list li { transition: none; }
  [data-reveal] .abst-head, [data-reveal] .abst-body > p,
  [data-reveal] .abfig-h, [data-reveal] .abstand-k,
  [data-reveal] .abstand-say, [data-reveal] .abstand-close,
  [data-reveal] .abstand-list li, [data-reveal] .ctx-faq, [data-reveal] .ctx-form,
  [data-reveal] .abvm-list li, [data-reveal] .abvm-pledge {
    opacity: 1; transform: none; transition: none;
  }
}


/* The page banner carries a breadcrumb trail now — Home > About Us > page —
   which does the Back link's job and does it better, because it says where
   "back" goes. Two ways up the same page is one too many, so the link stands
   down wherever the trail is comfortably readable and stays on the narrow
   screens where the trail is smallest. */
@media (min-width: 700px) { .abpage-back { display: none; } }

/* ── one constraint across all three dark skins ───────────────────────
   Grounds are neutral: black and greys, no tint in any background. Each
   skin keeps exactly one accent — Nova's gold, Atlas's red, Lab's teal —
   and it is only ever allowed on headings, marks and buttons. Never on a
   ground, a card, a tag or a wash.

   That is why the gradients on .ap-h1 em and .ap-say em survive while the
   ones on .ap-glow and .ap-cell-brand do not: the first two are
   background-clip:text on a heading, so they ARE the heading's colour; the
   last two were washes sitting behind content. */

/* ══ NOVA SKIN ════════════════════════════════════════════════════════
   One page only: /services/academic-project-development/, built to the
   founder's supplied reference (nova.ambitio.club) — near-black ground,
   amber accent, white display type. page() stamps data-skin="nova" on
   <html>, so this is a token flip plus the handful of rules that hardcode
   a light value rather than reading one.

   The rest of the site stays light. This page is deliberately a different
   room, which is the founder's call, made explicitly. */
:root[data-skin="nova"] {
  --bg: #0d0d0d;
  --surface: #171717;
  --surface-2: #141414;
  --ink: #ffffff;
  --muted: #e3ded8;          /* 16.3:1 — body copy, near-white by design */
  --faint: #b9b2aa;          /* 10.0:1 — captions and de-emphasised rows */
  --line: rgba(255, 255, 255, .11);
  --line-soft: rgba(255, 255, 255, .07);
  --line-strong: rgba(255, 255, 255, .20);

  --brand: #e8930c;          /* 8.0:1 on the ground */
  --brand-2: #f0a522;
  --brand-soft: rgba(255, 255, 255, .07);
  --sh: 0, 0, 0;
  --brand-line: rgba(232, 147, 12, .38);
  --on-brand: #14100a;       /* dark on amber — white would be 2.4:1 */

  --io-ground: #121212;
  --io-pill: #1c1c1c;
  --grid: rgba(255, 255, 255, .06);
  --grid-hi: rgba(255, 255, 255, .16);
  --dot: rgba(255, 255, 255, .16);
  --hover: rgba(255, 255, 255, .06);

  /* The FAQ, the process rail and the planner read a second family. Left
     unflipped these paint #212121 on a #171717 card — 1.11:1, invisible. */
  --pr-ground: #0d0d0d;
  --pr-card:   #171717;
  --pr-chip:   #1e1e1e;
  --pr-ink:    #ffffff;
  --pr-ink-2:  #ece8e3;
  --pr-ink-3:  #d5cfc8;
  --pr-body:   #e3ded8;
  --pr-rail:   rgba(255, 255, 255, .11);
  --pr-dash:   rgba(255, 255, 255, .22);
  --pr-wire:   #e8930c;
  --pr-bevel:  inset 0 1px 0 rgba(255, 255, 255, .06);
  --pr-shadow: 0 4px 14px rgba(0, 0, 0, .5);
  --pr-shadow-s: 0 1px 2px rgba(0, 0, 0, .4);
  --hdr-glass: rgba(16, 16, 16, .92);
  --hdr-glass-thin: rgba(16, 16, 16, .62);
  --logo: #ffffff;
  color-scheme: dark;
}
:root[data-skin="nova"] body { background: var(--bg); color: var(--ink); }

/* the five families that write a literal white rather than a token */
:root[data-skin="nova"] .ap-progress { background: #101010; }
:root[data-skin="nova"] .site-header .brand-mark { --logo-ink: #ffffff; }
:root[data-skin="nova"] .site-header:not(.on-hero):not(.past-hero) .brand-mark {
  background: #ffffff;
}
:root[data-skin="nova"] .foot { background: #080808; }
:root[data-skin="nova"] .foot-glow { opacity: .35; }
:root[data-skin="nova"] .btn:not(.btn-primary) {
  border: 1px solid var(--line-strong); color: var(--ink); background: rgba(255, 255, 255, .03);
}
:root[data-skin="nova"] .btn:not(.btn-primary):hover {
  border-color: var(--brand); color: var(--brand); background: rgba(255, 255, 255, .05);
}
:root[data-skin="nova"] .btn-primary { background: var(--brand); color: var(--on-brand); }
:root[data-skin="nova"] .btn-primary:hover { background: var(--brand-2); color: var(--on-brand); }

/* surfaces that were painted white directly by the blocks I added */
:root[data-skin="nova"] .abx-card,
:root[data-skin="nova"] .abstand-list li,
:root[data-skin="nova"] .abvm-list li,
:root[data-skin="nova"] .au-stats li,
:root[data-skin="nova"] .fq { background: var(--surface); border-color: var(--line); }
:root[data-skin="nova"] .abfig { background: var(--io-ground); border-color: var(--line); }
:root[data-skin="nova"] .abnav { background: rgba(255, 255, 255, .04); }

/* the page banner's lattice and bloom were tuned for a white ground */
:root[data-skin="nova"] .pgh { background: var(--bg); }
:root[data-skin="nova"] .pgh-bloom { opacity: .5; }

/* Nova leads on scale: the display line is larger and the eyebrow is a
   bordered pill on a dark ground rather than a tinted one. */
:root[data-skin="nova"] .pgh-h { letter-spacing: -.03em; }
:root[data-skin="nova"] .pill-note {
  background: rgba(255, 255, 255, .05);
  border-color: var(--line-strong);
  color: var(--muted);
}
:root[data-skin="nova"] .abx-rule {
  background: linear-gradient(to right, var(--brand), rgba(232, 147, 12, 0));
}
/* the amber glow behind the banner, the one flourish borrowed wholesale */
:root[data-skin="nova"] .pgh-fx::after {
  content: ""; position: absolute; left: 50%; top: -30%;
  width: min(760px, 90vw); aspect-ratio: 1; transform: translateX(-50%);
  background: radial-gradient(circle, rgba(255, 255, 255, .06), transparent 62%);
  pointer-events: none;
}

/* The header's own controls write literal dark inks rather than reading
   --ink/--muted, so on this skin every one landed dark-on-dark: "Sign in"
   at 1.03:1, "Menu" at 1.00:1, the five triggers at 1.87:1 — a navigation
   bar you could not see.

   The prefix carries :not(.on-hero):not(.past-hero) purely for weight: the
   rules being overridden are :root:not([data-theme="dark"]) .site-header
   :not(.on-hero) X, which is (0,5,0), and a plainer selector here loses on
   class count no matter that it comes later in the file. */
:root[data-skin="nova"] .site-header:not(.on-hero):not(.past-hero) :is(
  .menu-trigger, .nav-links > a, .mp-plain > a, .nav-signin, .burger
) { color: var(--ink); }

:root[data-skin="nova"] .site-header:not(.on-hero):not(.past-hero)
  :is(.menu-trigger, .nav-links > a):hover { color: var(--brand); }
:root[data-skin="nova"] .site-header:not(.on-hero):not(.past-hero) .nav-signin { border-color: var(--line-strong); }
:root[data-skin="nova"] .site-header:not(.on-hero):not(.past-hero) .nav-signin:hover {
  border-color: var(--brand); color: var(--brand);
}
:root[data-skin="nova"] .site-header:not(.on-hero):not(.past-hero) .burger .ab {
  background: rgba(255, 255, 255, .08);
}
:root[data-skin="nova"] .site-header:not(.on-hero):not(.past-hero) .burger:hover .ab {
  background: rgba(255, 255, 255, .14);
}
:root[data-skin="nova"] .site-header:not(.on-hero):not(.past-hero) :is(.burger .bg-tx, .burger .bg-ic) {
  background: none; color: var(--ink);
}
:root[data-skin="nova"] .site-header:not(.on-hero):not(.past-hero) .burger svg { color: var(--ink); }

/* the dropdown and account panels are their own surface */
:root[data-skin="nova"] :is(.menu-panel, .menu-panel.mega) {
  background: #141414; border-color: var(--line); color: var(--ink);
}
:root[data-skin="nova"] :is(.mm-tx b, .mm-col h3, .mi-tx b) { color: var(--ink); }
:root[data-skin="nova"] :is(.mm-tx em, .mi-tx em) { color: var(--muted); }

/* ══ ACADEMIC PROJECTS ════════════════════════════════════════════════
   Its own composition, not the shared block stack. Scoped to the nova skin
   so none of it can reach another page. Layout, colour and spacing only —
   every type size is registered in the role layer above. */

/* ── hero ─────────────────────────────────────────────────────────────── */
.ap-hero { position: relative; isolation: isolate; overflow: hidden;
           padding: clamp(56px, 9vw, 132px) 0 clamp(40px, 5vw, 68px); text-align: center; }
.ap-hero-fx { position: absolute; inset: 0; z-index: -1; pointer-events: none; }
.ap-glow {
  position: absolute; left: 50%; top: -46%; transform: translateX(-50%);
  width: min(1100px, 130vw); aspect-ratio: 1; border-radius: 50%;
  background: radial-gradient(circle, rgba(255,255,255,.07), rgba(255,255,255,.02) 42%, transparent 66%);
}
.ap-grid {
  position: absolute; inset: 0;
  background:
    linear-gradient(var(--grid) 1px, transparent 1px) 0 0 / 100% 68px,
    linear-gradient(90deg, var(--grid) 1px, transparent 1px) 0 0 / 68px 100%;
  -webkit-mask: radial-gradient(ellipse 70% 62% at 50% 34%, #000 30%, transparent 74%);
          mask: radial-gradient(ellipse 70% 62% at 50% 34%, #000 30%, transparent 74%);
}
.ap-eyebrow {
  display: inline-flex; align-items: center; gap: 9px; margin: 0 auto;
  padding: 8px 18px 8px 14px; border-radius: 999px;
  border: 1px solid var(--line-strong); background: rgba(255,255,255,.04);
  color: var(--muted);
}
.ap-spark { width: 7px; height: 7px; border-radius: 50%; background: var(--brand);
            box-shadow: 0 0 0 4px rgba(232,147,12,.16); }
.ap-h1 { margin: clamp(20px, 2.6vw, 30px) 0 0; text-wrap: balance; }
.ap-h1 em {
  font-family: var(--font-serif); font-style: italic; font-weight: 400;
  background: linear-gradient(96deg, #f7b53d, var(--brand) 58%, #d97b06);
  -webkit-background-clip: text; background-clip: text; color: transparent;
  -webkit-box-decoration-break: clone; box-decoration-break: clone;
}
.ap-sub { margin: clamp(16px, 2vw, 24px) auto 0; max-width: 60ch; color: var(--muted); }
.ap-cta { display: flex; flex-wrap: wrap; gap: 12px; justify-content: center;
          margin: clamp(24px, 3vw, 36px) 0 0; }
.ap-stats {
  list-style: none; margin: clamp(38px, 5vw, 64px) auto 0; padding: 0; max-width: 62rem;
  display: grid; gap: 1px; background: var(--line);
  border: 1px solid var(--line); border-radius: 18px; overflow: hidden;
}
@media (min-width: 720px) { .ap-stats { grid-template-columns: repeat(3, 1fr); } }
.ap-stats li { padding: clamp(20px, 2.4vw, 28px) 20px; background: #101010; text-align: center; }
.ap-stats b { display: block; color: var(--brand); font-variant-numeric: tabular-nums; }
.ap-stats span { display: block; margin-top: 6px; color: var(--muted); }
.ap-crumbs { margin-top: clamp(28px, 3.4vw, 40px); }
.ap-crumbs ol { list-style: none; margin: 0; padding: 0;
                display: flex; flex-wrap: wrap; gap: 8px; justify-content: center; }
.ap-crumbs li + li::before { content: "›"; margin-right: 8px; color: var(--faint); }
.ap-crumbs a { color: var(--muted); text-decoration: none; }
.ap-crumbs a:hover { color: var(--brand); }
.ap-crumbs span { color: var(--ink); }

/* ── shared section furniture ─────────────────────────────────────────── */
.ap-sec { margin-top: clamp(56px, 8vw, 116px); }
.ap-head { max-width: 46rem; margin-bottom: clamp(28px, 3.6vw, 48px); }
.ap-sec-k { margin: 0 0 10px; color: var(--brand); }
.ap-sec-h { margin: 0; text-wrap: balance; }
.ap-sec-d { margin: 14px 0 0; color: var(--muted); max-width: 54ch; }

/* ── the thesis line ──────────────────────────────────────────────────── */
.ap-intent-grid { display: grid; gap: clamp(24px, 3.4vw, 56px); align-items: start; }
@media (min-width: 980px) { .ap-intent-grid { grid-template-columns: 1.15fr 1fr; } }
.ap-say { margin: 0; text-wrap: balance; }
.ap-say em {
  font-family: var(--font-serif); font-style: italic; font-weight: 400;
  background: linear-gradient(96deg, #f7b53d, var(--brand) 62%);
  -webkit-background-clip: text; background-clip: text; color: transparent;
  -webkit-box-decoration-break: clone; box-decoration-break: clone;
}
.ap-intent-tx { display: grid; gap: 1.1em; padding-top: 6px; }
.ap-intent-tx p { margin: 0; color: var(--muted); }

/* ── the sequence: the numerals carry it, with a line threaded through ── */
.ap-flow { list-style: none; margin: 0; padding: 0; position: relative; display: grid; gap: 0; }
.ap-step {
  position: relative; display: grid; grid-template-columns: auto 1fr;
  gap: clamp(18px, 2.6vw, 34px); align-items: start;
  padding: clamp(22px, 2.8vw, 34px) 0;
}
.ap-num {
  position: relative; display: grid; place-items: center;
  width: clamp(40px, 4.4vw, 62px); aspect-ratio: 1; border-radius: 50%;
  border: 1px solid var(--line-strong); color: var(--brand);
  font-variant-numeric: tabular-nums; background: #101010;
  transition: border-color .3s var(--ease), box-shadow .3s var(--ease);
}

.ap-step:hover .ap-num { border-color: var(--brand); box-shadow: 0 0 0 6px rgba(232,147,12,.10); }
.ap-step-tx b { display: block; color: var(--ink); }
.ap-step-tx p { margin: 8px 0 0; color: var(--muted); max-width: 62ch; }
@media (min-width: 1080px) {
  .ap-step-tx { display: grid; grid-template-columns: minmax(0, 22rem) minmax(0, 1fr);
                gap: clamp(24px, 3vw, 52px); align-items: baseline; }
  .ap-step-tx p { margin: 0; }
}

/* ── bento ────────────────────────────────────────────────────────────── */
.ap-bento { display: grid; gap: clamp(12px, 1.4vw, 18px); }
@media (min-width: 760px)  { .ap-bento { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (min-width: 1080px) {
  .ap-bento { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .ap-cell-wide { grid-column: span 2; }
  /* the closing card runs the full width, so the last row closes too */
  .ap-cell-brand { grid-column: 1 / -1; }
}
@media (min-width: 760px) and (max-width: 1079.98px) {
  .ap-cell-wide, .ap-cell-brand { grid-column: 1 / -1; }
}
.ap-cell {
  padding: clamp(22px, 2.6vw, 32px);
  border: 1px solid var(--line); border-radius: 18px; background: #131313;
  transition: border-color .3s var(--ease), transform .3s var(--ease), background .3s;
}
.ap-cell:hover { border-color: var(--brand-line); transform: translateY(-3px); background: #171717; }
.ap-ic {
  display: grid; place-items: center; width: 42px; height: 42px; margin-bottom: 16px;
  border-radius: 12px; background: var(--brand-soft); color: var(--brand);
}
.ap-ic svg { width: 21px; height: 21px; }
.ap-cell b { display: block; color: var(--ink); }
.ap-cell p { margin: 8px 0 0; color: var(--muted); }
.ap-cell-brand {
  background: #1A1A1A;
  border-color: var(--brand-line);
}
@media (min-width: 1080px) {
  .ap-cell-brand { display: grid; grid-template-columns: minmax(0, 20rem) minmax(0, 1fr);
                   gap: clamp(24px, 3vw, 48px); align-items: center; }
  .ap-cell-brand p { margin: 0; }
}
.ap-cell-brand:hover { background: #1F1F1F; }

/* ── what you hold ────────────────────────────────────────────────────── */
.ap-hold {
  padding: clamp(28px, 4vw, 56px);
  border: 1px solid var(--line); border-radius: 24px;
  background: #101010;
}
.ap-hold-head { margin-bottom: clamp(22px, 2.6vw, 32px); }
.ap-hold-list { list-style: none; margin: 0; padding: 0; display: grid; gap: 14px 32px; }
@media (min-width: 820px) { .ap-hold-list { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
.ap-hold-list li { display: grid; grid-template-columns: auto 1fr; gap: 12px;
                   align-items: start; color: var(--muted); }
.ap-tick {
  margin-top: .35em; width: 18px; height: 18px; border-radius: 50%;
  background: var(--brand-soft); position: relative;
}
.ap-tick::after {
  content: ""; position: absolute; left: 6px; top: 4px; width: 4px; height: 8px;
  border: solid var(--brand); border-width: 0 1.6px 1.6px 0; transform: rotate(45deg);
}

/* ── the integrity note ───────────────────────────────────────────────── */
.ap-note {
  margin-top: clamp(40px, 5vw, 72px); padding: clamp(20px, 2.4vw, 30px) clamp(22px, 2.8vw, 34px);
  border: 1px solid var(--line); border-left: 3px solid var(--brand);
  border-radius: 0 16px 16px 0; background: #121212;
}
.ap-note p { margin: 0; color: var(--muted); max-width: 78ch; }
.ap-note b { color: var(--ink); }

/* ── reveal ───────────────────────────────────────────────────────────── */
[data-reveal] :is(.ap-say, .ap-intent-tx, .ap-head, .ap-cell, .ap-hold, .ap-note p) {
  opacity: 0; transform: translateY(18px);
  transition: opacity .6s var(--ease), transform .6s var(--ease),
              border-color .3s, background .3s;
  transition-delay: calc(var(--n, 0) * .07s);
}
[data-reveal].in :is(.ap-say, .ap-intent-tx, .ap-head, .ap-cell, .ap-hold, .ap-note p) {
  opacity: 1; transform: none;
}
@media (prefers-reduced-motion: reduce) {
  [data-reveal] :is(.ap-say, .ap-intent-tx, .ap-head, .ap-cell, .ap-hold, .ap-note p) {
    opacity: 1; transform: none; transition: none;
  }
  .ap-cell, .ap-num { transition: none; }
}

/* ── reading progress ─────────────────────────────────────────────────── */
.ap-progress {
  position: fixed; left: 0; right: 0; top: 0; height: 2px; z-index: 60;
  pointer-events: none; background: rgba(255, 255, 255, .06);
}
.ap-progress i {
  display: block; height: 100%; transform: scaleX(0); transform-origin: 0 50%;
  background: linear-gradient(90deg, #d97b06, var(--brand) 40%, #f7b53d);
}

/* ── the journey curve ────────────────────────────────────────────────
   Built to the supplied kit. Everything is sized in vw so the whole thing
   scales as one object; px comments are at a 1440px viewport. */
/* The starfield is the page's ground now, not just this section's, so the
   journey reads as part of the page rather than a panel dropped into it.
   Two layers drift at different speeds — enough to feel alive, slow enough
   that it never competes with the copy. */
:root[data-skin="nova"] body {
  background: #101010 url("assets/starfield-tile-1200.png") repeat 50% 50% / 1200px;
}
:root[data-skin="nova"] body::before {
  content: ""; position: fixed; inset: 0;
  z-index: 0; pointer-events: none; opacity: .55;
  background: url("assets/starfield-tile-1200.png") repeat 50% 50% / 880px;
}
/* Both exclusions are load-bearing. This rule means "page sections" — it
   lifts them over the decorative ground — but it matches every body child,
   and two of them are not sections. .site-header was the first casualty:
   given a stacking context at z-index 1 it trapped its own dropdown panels
   there. .ap-progress was the second and went unnoticed far longer, because
   losing position:fixed does not look broken, it just scrolls away: the
   reading bar sat at top:-1423px on every Nova and Atlas page and nobody
   saw a bar that was never there to miss. */
:root[data-skin="nova"] body > *:not(.site-header):not(.ap-progress) { position: relative; z-index: 1; }

.ap-jrn {
  --gold: #D58300;
  --track: #676767;
  --idle: #666666;
  --road-ground: #101010;      /* the page's own ground, for the labels */
  position: relative;
  margin: clamp(48px, 7vw, 104px) calc(50% - 50vw) 0;
  padding: 10vw 0 0;                       /* 144 */
}
.ap-jrn-head { text-align: center; padding-inline: 5vw; }
.ap-jrn-h { margin: 0; color: #fff; }
.ap-jrn-sub { margin: .6vw auto 0; max-width: 46ch; color: rgba(255, 255, 255, .74); }

/* The canvas is 95vw and the drawing overflows it: the SVG keeps its own
   1144:981 ratio and renders taller than this box, which is deliberate. */
.ap-canvas {
  position: relative; isolation: isolate;
  width: 95vw; margin: 13vw auto 0;
  height: 74.5vw;
  overflow: visible;
}
.ap-curve { position: absolute; inset: 0; width: 100%; height: auto; overflow: visible; }
.ap-curve-m { display: none; }
.ap-track { stroke: var(--track); stroke-width: 3; stroke-linecap: round; }
.ap-prog {
  stroke: var(--gold); stroke-width: 3; stroke-linecap: round;
}

/* ── a milestone ──────────────────────────────────────────────────────
   The dot is the node point; the label block hangs above it and the stem
   falls below. */
.ap-marks { list-style: none; margin: 0; padding: 0; }
/* No half-dot back-off here, and that is deliberate. The wide drawing's
   six positions were measured by hand against the rendered dot, so the
   offset between this origin and the dot's centre is already inside the
   numbers — subtracting it again moved every dot off the line by the
   same amount it was meant to fix, 3 to 8px in the other direction.
   The narrow drawing is the opposite case and corrects for it below. */
.ap-mark { position: absolute; left: var(--l); top: var(--t); --dot: .9vw; }
.ap-mark-dot {
  position: absolute; left: 0; top: 0;
  width: var(--dot); height: var(--dot); aspect-ratio: 1; flex-shrink: 0;
  border-radius: 50%; background: var(--gold);
  transform: scale(0); transition: transform .45s cubic-bezier(.22, 1, .36, 1);
}
.ap-mark-stem {
  position: absolute; left: calc(var(--dot) / 2 - 1px); top: calc(var(--dot) / 2);
  z-index: -1; width: 2px; height: 303px;
  background: linear-gradient(to bottom, #676767, rgba(103, 103, 103, 0));
}
/* the riser has to reach the dot's centre, not the marker origin, or it
   stops half a dot above the point it rises from.

   z-index lifts every label over every stem. The markers are siblings
   with no stacking context of their own, so they paint in document
   order and a later marker's stem was drawing across an earlier
   marker's words — four of them at 1400. The stems sit at -1 and the
   canvas isolates, so one positive value here puts all six labels above
   all six stems whatever order they are in. */
.ap-mark-lab {
  position: absolute; left: 0; bottom: calc(var(--dot) / -2);
  display: block; z-index: 2;
}
.ap-mark-ic {
  display: block; width: 1.667vw; height: 1.667vw;                  /* 24 */
  margin: 0 0 1vw -.3vw; color: var(--idle);
  transition: color .5s cubic-bezier(.22, 1, .36, 1);
}
.ap-mark-ic svg { width: 100%; height: 100%; display: block; }
.ap-mark-riser {
  display: block; margin-left: calc(var(--dot) / 2 - 1px); width: 2px;
  /* --dy is how far this label had to rise to get off the line; the
     script sets it on the narrow drawing. It lives in the riser rather
     than as an offset on the label so a lifted label stays on its own
     stalk instead of floating away from the dot it belongs to, and it
     is in the resting height as well as the revealed one so the
     clearance does not arrive only once the marker has been scrolled
     to. Transparent until then, so nothing is drawn to nothing. */
  height: var(--dy, 0px);
  background: transparent; border-radius: 1vw 1vw 0 0;
  transition: height .6s cubic-bezier(.22, 1, .36, 1);
}
.ap-mark.is-on .ap-mark-riser { background: var(--gold); }
.ap-mark-tx {
  position: absolute; left: 2.5vw; top: 0; width: 20vw;             /* 36 / 288 */
  opacity: 0; transform: translateY(.6vw);
  transition: opacity .6s cubic-bezier(.22, 1, .36, 1),
              transform .6s cubic-bezier(.22, 1, .36, 1);
}
.ap-mark-tx b { display: block; color: var(--gold); }
.ap-mark-tx em { display: block; font-style: normal; color: rgba(255, 255, 255, .74); }
.ap-mark-tx .ap-d-s { display: none; }   /* wide drawing keeps the full sentence */

/* Develop's counterpart: the label mirrors so it never runs off the right
   edge of the canvas. */
.ap-mark.is-mirror .ap-mark-lab {
  left: 0; right: auto; width: 20vw;
  transform: translateX(-100%) translateX(var(--dot));
  display: flex; flex-direction: column; align-items: flex-end;
}
.ap-mark.is-mirror .ap-mark-ic { margin-right: 0; margin-left: 0; }
.ap-mark.is-mirror .ap-mark-riser { margin-left: 0; margin-right: calc(var(--dot) / 2 - 1px); }
.ap-mark.is-mirror .ap-mark-tx { left: auto; right: 2.5vw; text-align: right; }

/* lit */
.ap-mark.is-on .ap-mark-dot { transform: scale(1); }
.ap-mark.is-on .ap-mark-ic { color: var(--gold); }
.ap-mark.is-on .ap-mark-riser { height: calc(6vw + var(--dy, 0px)); }   /* 87 */
.ap-mark.is-on .ap-mark-tx { opacity: 1; transform: none; }

/* The two open ends dissolve into the ground.

   These used to be two opaque #101010 rectangles laid over the curve, which
   worked while the section owned a flat background. Once the starfield moved
   to the page they became dark blocks that blanked out the stars and cut the
   line in half — the "upper and bottom not joining". Masking the drawing
   itself fades the stroke to nothing instead, and leaves the sky untouched. */
/* Longhand, because the `mask` shorthand resets mask-composite and the
   two-layer version silently parsed to none. */
.ap-curve-d {
  -webkit-mask-image:
    radial-gradient(ellipse 9% 11% at 1% 0%, transparent 30%, #000 100%),
    linear-gradient(to bottom, #000 86%, transparent 99%);
  mask-image:
    radial-gradient(ellipse 9% 11% at 1% 0%, transparent 30%, #000 100%),
    linear-gradient(to bottom, #000 86%, transparent 99%);
  -webkit-mask-composite: source-in;
  mask-composite: intersect;
}
.ap-curve-m {
  -webkit-mask-image:
    radial-gradient(ellipse 7% 5% at 1% 0%, transparent 30%, #000 100%),
    linear-gradient(to bottom, #000 90%, transparent 100%);
  mask-image:
    radial-gradient(ellipse 7% 5% at 1% 0%, transparent 30%, #000 100%),
    linear-gradient(to bottom, #000 90%, transparent 100%);
  -webkit-mask-composite: source-in;
  mask-composite: intersect;
}
.ap-fade-a, .ap-fade-b { display: none; }

/* ── narrow: the separate vertical drawing ────────────────────────────
   A different SVG, not the desktop path reflowed. The markers move onto
   its anchors and the label widths open up, since there is no second
   column competing for the room. */
@media (max-width: 860px) {
  .ap-jrn { padding-top: 16vw; }
  .ap-curve-d { display: none; }
  .ap-curve-m { display: block; }
  .ap-canvas { width: 90vw; height: 182vw; margin-top: 34vw; }
  /* mPos() derives these straight from M_POINTS, which are points ON the
     path — no dot was involved in measuring them, so the half-dot has to
     come off here or every dot sits down-right of the line it marks.
     Measured 3 to 6px adrift before, 0 to 1 after. */
  .ap-mark {
    --dot: 2.6vw;
    left: calc(var(--ml) - var(--dot) / 2);
    top: calc(var(--mt) - var(--dot) / 2);
  }
  /* shorter than the desktop 303px: at this scale the full drop crowded
     the marker below it */
  .ap-mark-stem { height: 15vw; }
  .ap-mark-ic { width: 5vw; height: 5vw; margin-bottom: 2.4vw; margin-left: -.8vw; }

  /* --dy-m lifts one label on the narrow drawing only. Two stages out of
     forty-two have a two-line subtitle sitting where the curve crosses
     the gap between their lines — the halo hugs each line and cannot
     fill between them, so the line reads as a strike through the text.
     Set per stage in the content, so nothing else moves. */
  .ap-mark-riser { height: calc(var(--dy, 0px) + var(--dy-m, 0px)); }
  .ap-mark.is-on .ap-mark-riser { height: calc(10vw + var(--dy, 0px) + var(--dy-m, 0px)); }
  /* The wide drawing's labels are a name and a line under it — 26 to 29
     characters, one or two lines, which is what makes it read as
     cleanly as it does. The same sentences at 39vw on a phone wrap to
     three and four lines and the box grows to 88px, which is what puts
     them across the curve.

     So the narrow label is given more width and held to two lines: a
     wider measure needs fewer of them, and two is what the wide drawing
     uses. The sentence stays whole in the markup — this trims what is
     drawn, not what is read out. */
  .ap-mark-tx { left: 6.6vw; width: 44vw; }
  /* the narrow drawing shows the short form. display:none rather than a
     clip, so exactly one of the two is in the accessibility tree at any
     width — a reader being read to gets one sentence, not both. */
  .ap-mark-tx .ap-d { display: none; }
  .ap-mark-tx .ap-d-s { display: block; }
  .ap-mark.is-mirror .ap-mark-lab { left: 0; right: auto; width: auto;
    transform: none; display: block; align-items: initial; }
  .ap-mark.is-mirror .ap-mark-ic { margin-left: -.8vw; margin-right: 0; }
  .ap-mark.is-mirror .ap-mark-riser { margin-left: calc(var(--dot) / 2 - 1px); margin-right: 0; }
  .ap-mark.is-mirror .ap-mark-tx { left: 6.6vw; right: auto; text-align: left; }

  /* the mirrored block has to be as wide as the text inside it, or the
     text hangs past its own edge — which is how one label ended up a
     pixel off the left of the screen */
  .ap-mark.is-mirror-m .ap-mark-lab {
    left: 0; right: auto; width: 44vw;
    transform: translateX(-100%) translateX(var(--dot));
    display: flex; flex-direction: column; align-items: flex-end; }
  .ap-mark.is-mirror-m .ap-mark-ic { margin-right: -.8vw; margin-left: 0; }
  .ap-mark.is-mirror-m .ap-mark-riser { margin-left: 0; margin-right: calc(var(--dot) / 2 - 1px); }
  .ap-mark.is-mirror-m .ap-mark-tx { left: auto; right: 6.6vw; text-align: right; }
  .ap-mark.is-mirror .ap-mark-tx { right: 6.6vw; }
  .ap-mark.is-mirror .ap-mark-riser { margin-right: 1.4vw; }
}

@media (prefers-reduced-motion: reduce) {
  .ap-mark-dot, .ap-mark-ic, .ap-mark-riser, .ap-mark-tx { transition: none; }
}

/* ── bento ────────────────────────────────────────────────────────────── */
.ap-bento { display: grid; gap: clamp(12px, 1.4vw, 18px); }
@media (min-width: 760px)  { .ap-bento { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (min-width: 1080px) {
  .ap-bento { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .ap-cell-wide { grid-column: span 2; }
  /* the closing card runs the full width, so the last row closes too */
  .ap-cell-brand { grid-column: 1 / -1; }
}
@media (min-width: 760px) and (max-width: 1079.98px) {
  .ap-cell-wide, .ap-cell-brand { grid-column: 1 / -1; }
}
.ap-cell {
  padding: clamp(22px, 2.6vw, 32px);
  border: 1px solid var(--line); border-radius: 18px; background: #131313;
  transition: border-color .3s var(--ease), transform .3s var(--ease), background .3s;
}
.ap-cell:hover { border-color: var(--brand-line); transform: translateY(-3px); background: #171717; }
.ap-ic {
  display: grid; place-items: center; width: 42px; height: 42px; margin-bottom: 16px;
  border-radius: 12px; background: var(--brand-soft); color: var(--brand);
}
.ap-ic svg { width: 21px; height: 21px; }
.ap-cell b { display: block; color: var(--ink); }
.ap-cell p { margin: 8px 0 0; color: var(--muted); }
.ap-cell-brand {
  background: #1A1A1A;
  border-color: var(--brand-line);
}
@media (min-width: 1080px) {
  .ap-cell-brand { display: grid; grid-template-columns: minmax(0, 20rem) minmax(0, 1fr);
                   gap: clamp(24px, 3vw, 48px); align-items: center; }
  .ap-cell-brand p { margin: 0; }
}
.ap-cell-brand:hover { background: #1F1F1F; }

/* ── what you hold ────────────────────────────────────────────────────── */
.ap-hold {
  padding: clamp(28px, 4vw, 56px);
  border: 1px solid var(--line); border-radius: 24px;
  background: #101010;
}
.ap-hold-head { margin-bottom: clamp(22px, 2.6vw, 32px); }
.ap-hold-list { list-style: none; margin: 0; padding: 0; display: grid; gap: 14px 32px; }
@media (min-width: 820px) { .ap-hold-list { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
.ap-hold-list li { display: grid; grid-template-columns: auto 1fr; gap: 12px;
                   align-items: start; color: var(--muted); }
.ap-tick {
  margin-top: .35em; width: 18px; height: 18px; border-radius: 50%;
  background: var(--brand-soft); position: relative;
}
.ap-tick::after {
  content: ""; position: absolute; left: 6px; top: 4px; width: 4px; height: 8px;
  border: solid var(--brand); border-width: 0 1.6px 1.6px 0; transform: rotate(45deg);
}

/* ── the integrity note ───────────────────────────────────────────────── */
.ap-note {
  margin-top: clamp(40px, 5vw, 72px); padding: clamp(20px, 2.4vw, 30px) clamp(22px, 2.8vw, 34px);
  border: 1px solid var(--line); border-left: 3px solid var(--brand);
  border-radius: 0 16px 16px 0; background: #121212;
}
.ap-note p { margin: 0; color: var(--muted); max-width: 78ch; }
.ap-note b { color: var(--ink); }

/* ── reveal ───────────────────────────────────────────────────────────── */
[data-reveal] :is(.ap-say, .ap-intent-tx, .ap-head, .ap-cell, .ap-hold, .ap-note p) {
  opacity: 0; transform: translateY(18px);
  transition: opacity .6s var(--ease), transform .6s var(--ease),
              border-color .3s, background .3s;
  transition-delay: calc(var(--n, 0) * .07s);
}
[data-reveal].in :is(.ap-say, .ap-intent-tx, .ap-head, .ap-cell, .ap-hold, .ap-note p) {
  opacity: 1; transform: none;
}
@media (prefers-reduced-motion: reduce) {
  [data-reveal] :is(.ap-say, .ap-intent-tx, .ap-head, .ap-cell, .ap-hold, .ap-note p) {
    opacity: 1; transform: none; transition: none;
  }
  .ap-cell, .ap-num { transition: none; }
}

/* ── reading progress ─────────────────────────────────────────────────── */
.ap-progress {
  position: fixed; left: 0; right: 0; top: 0; height: 2px; z-index: 60;
  pointer-events: none; background: rgba(255, 255, 255, .06);
}
.ap-progress i {
  display: block; height: 100%; transform: scaleX(0); transform-origin: 0 50%;
  background: linear-gradient(90deg, #d97b06, var(--brand) 40%, #f7b53d);
}

/* ── the roadmap ──────────────────────────────────────────────────────── */
/* ── the roadmap ──────────────────────────────────────────────────────
   One drawing at every width, which is the point: the reference keeps the
   same serpentine on a phone and on a desktop, so the layout is driven by
   the curve's own aspect ratio rather than by breakpoints.

   The path is pinned at its native viewBox and stretched with
   preserveAspectRatio="none"; the stages are absolutely placed on its
   anchor points as percentages, so they ride the curve at any size.
   vector-effect keeps the stroke 3px however far the box is stretched. */
.ap-road {
  position: relative;
  /* Full width, like every other block on the page. It used to carry the
     drawing's own 369:746 ratio plus a 46rem cap, which pinned it to 736px
     — 38% of a 1920px screen while its neighbours ran at 74%. The curve is
     stretched with preserveAspectRatio="none", so widening it simply makes
     the lobes broader and shallower, which is what it should do.

     Height cannot come from the ratio any more (1424px wide at 369:746
     would be 2879px tall), and it cannot be a constant either: the narrower
     the screen, the taller the stage blocks wrap and the MORE room the
     closest pair of anchors needs. So the height falls as the viewport
     grows — the inverse of a normal clamp, which is the whole point. */
  width: 100%;
  /* Measured at 1684px wide: text crossings fall 10 -> 7 -> 2 -> 1 as the
     height goes 760 -> 880 -> 1000 -> 1120, and stop improving after that.
     Crossings are a function of lobe steepness, i.e. height/width, so the
     height has to grow WITH the width — the opposite of what I first wrote.
     Deriving it from the element's own width needs no breakpoint; the floor
     covers narrow screens, where the blocks wrap taller. */
  aspect-ratio: 3 / 2;
  min-height: 1130px;
  margin-block: clamp(56px, 7vw, 104px);
}
.ap-road-svg { position: absolute; inset: 0; width: 100%; height: 100%; overflow: visible; }
.ap-road-track { stroke: rgba(255, 255, 255, .13); stroke-width: 3; }
.ap-road-fill {
  stroke: url(#ap-road-grad); stroke-width: 3; stroke-linecap: round;
  stroke-dasharray: 0 99999;
  filter: drop-shadow(0 0 6px rgba(232, 147, 12, .35));
}

.ap-flow { position: absolute; inset: 0; list-style: none; margin: 0; padding: 0; }
.ap-step {
  --nw: clamp(40px, 4.4vw, 62px);          /* the marker's diameter */
  position: absolute; top: var(--ay); left: var(--ax);
  width: max-content; max-width: min(47%, 16.5rem);
}
/* The anchors sit hard against the edges of the drawing, so the copy has to
   run inward from them or it leaves the box. A left-lobe stage therefore
   starts at its anchor and extends right; a right-lobe stage ends at its
   anchor and extends left. The margin re-centres the marker on the curve,
   since the element is aligned by its edge rather than its middle. */
.ap-step.is-left  { transform: translate(0, -50%);    margin-left: calc(var(--nw) / -2); padding-left: calc(var(--nw) + 14px); }
.ap-step.is-right { transform: translate(-100%, -50%); margin-left: calc(var(--nw) / 2);  padding-right: calc(var(--nw) + 14px); text-align: right; }
.ap-step .ap-num  { position: absolute; top: 50%; translate: 0 -50%; }
.ap-step.is-left  .ap-num { left: 0; }
.ap-step.is-right .ap-num { right: 0; }
.ap-step-tx { display: block; }
.ap-step-tx p { max-width: 24ch; }

/* The stages are positioned with transform, so the shared reveal — which
   resets transform to none — cannot be allowed near them. They fade only. */
[data-reveal] .ap-step { opacity: 0; transition: opacity .6s var(--ease); transition-delay: calc(var(--n, 0) * .07s); }
[data-reveal].in .ap-step { opacity: 1; }
@media (prefers-reduced-motion: reduce) { [data-reveal] .ap-step { opacity: 1; transition: none; } }
.ap-step.is-right .ap-step-tx p { margin-left: auto; }
.ap-step-tx {
  position: relative; isolation: isolate;
}
.ap-step-tx::before {
  content: ""; position: absolute; z-index: -1;
  inset: -0.7em -1.1em;
  background: radial-gradient(ellipse 62% 58% at 50% 50%,
              var(--bg) 58%, rgba(13, 13, 13, .82) 78%, transparent 100%);
}

.ap-num {
  position: relative; display: grid; place-items: center;
  width: clamp(40px, 4.4vw, 62px); aspect-ratio: 1; border-radius: 50%;
  border: 1px solid var(--line-strong); color: var(--faint);
  font-variant-numeric: tabular-nums; background: #101010;
  transition: border-color .45s var(--ease), color .45s var(--ease),
              box-shadow .45s var(--ease), background .45s var(--ease);
}
.ap-num b { font: inherit; color: inherit; }
.ap-step.is-done .ap-num { color: var(--brand); border-color: var(--brand-line); }
.ap-step.is-now .ap-num {
  border-color: var(--brand); background: #171208;
  box-shadow: 0 0 0 6px rgba(232, 147, 12, .12);
}
.ap-step .ap-step-tx b { transition: color .45s var(--ease); }
.ap-step:not(.is-done) .ap-step-tx b { color: var(--muted); }
.ap-step.is-done .ap-step-tx b { color: var(--ink); }

/* ── stream selector ──────────────────────────────────────────────────── */
.ap-str {
  border: 1px solid var(--line); border-radius: 22px; background: #111111; overflow: hidden;
}
.ap-str-tabs {
  position: relative; display: flex; gap: 6px; padding: 10px 10px 0;
  overflow-x: auto; scrollbar-width: none; border-bottom: 1px solid var(--line);
}
.ap-str-tabs::-webkit-scrollbar { display: none; }
/* The unselected tabs are the thing being read here — they are the menu.
   They sit at full --ink on a faint plate rather than dimmed to --muted,
   because a row of five low-contrast labels reads as decoration and the
   choice gets missed entirely. Selection is then carried by the plate and
   the gold rule, not by being the only legible one. */
.ap-str-tab {
  flex: none; padding: 15px 22px 17px; border: 0; cursor: pointer;
  background: rgba(255, 255, 255, .035);
  color: var(--ink); white-space: nowrap; border-radius: 12px 12px 0 0;
  transition: color .25s var(--ease), background .25s;
}
.ap-str-tab:hover { background: rgba(255, 255, 255, .08); }
.ap-str-tab[aria-selected="true"] { color: #fff; background: rgba(255, 255, 255, .08); }
.ap-str-tab:focus-visible { outline: 2px solid var(--brand); outline-offset: -3px; }
.ap-str-ink {
  position: absolute; bottom: -1px; left: 0; height: 3px; width: 0;
  background: linear-gradient(90deg, var(--brand), #f7b53d);
  transition: transform .4s var(--ease), width .4s var(--ease);
}
.ap-str-panel { padding: clamp(22px, 3vw, 38px); }
.ap-str-rows { margin: 0; display: grid; gap: clamp(14px, 1.8vw, 22px); }
.ap-str-row { display: grid; gap: 4px 32px; }
@media (min-width: 860px) { .ap-str-row { grid-template-columns: minmax(0, 14rem) minmax(0, 1fr); align-items: baseline; } }
.ap-str-row dt { color: var(--brand); }
.ap-str-row dd { margin: 0; color: var(--muted); }
/* each row fades in as its panel is shown, so switching tabs has a beat */
.ap-str-panel:not([hidden]) .ap-str-row { animation: ap-row .45s var(--ease) both; animation-delay: calc(var(--n) * .06s); }
@keyframes ap-row { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: none; } }

/* ── spotlight ────────────────────────────────────────────────────────── */
.ap-cell { position: relative; isolation: isolate; }
.ap-cell::after {
  content: ""; position: absolute; inset: 0; z-index: -1; border-radius: inherit;
  opacity: 0; transition: opacity .3s var(--ease);
  background: radial-gradient(320px circle at var(--mx, 50%) var(--my, 50%),
              rgba(232, 147, 12, .10), transparent 62%);
}
.ap-cell:hover::after { opacity: 1; }

/* ── ticker ───────────────────────────────────────────────────────────── */
.ap-tick-sec { margin-top: clamp(56px, 8vw, 116px); }
.ap-tick-k { margin: 0 0 18px; }
.ap-tick-wrap {
  position: relative; overflow: hidden;
  border-block: 1px solid var(--line); padding: 18px 0;
  -webkit-mask: linear-gradient(90deg, transparent, #000 8%, #000 92%, transparent);
          mask: linear-gradient(90deg, transparent, #000 8%, #000 92%, transparent);
}
.ap-tick-row { display: flex; width: max-content; animation: ap-tick 46s linear infinite; }
.ap-tick-list { list-style: none; margin: 0; padding: 0; display: flex; }
.ap-tick-list li {
  padding: 9px 18px; margin-right: 10px; white-space: nowrap;
  border: 1px solid var(--line); border-radius: 999px;
  background: rgba(255, 255, 255, .03); color: var(--muted);
}
@keyframes ap-tick { to { transform: translateX(-50%); } }

/* ── the contrast pair ────────────────────────────────────────────────── */
.ap-vs { display: grid; gap: clamp(18px, 2.4vw, 30px); align-items: start; }
@media (min-width: 900px) { .ap-vs { grid-template-columns: 1fr auto 1fr; } }
.ap-vs-col { padding: clamp(24px, 3vw, 36px); border: 1px solid var(--line); border-radius: 20px; }
.ap-vs-bad { background: #101010; }
.ap-vs-good {
  background: #141414;
  border-color: var(--brand-line);
}
.ap-vs-k { margin: 0 0 16px; }
.ap-vs-bad .ap-vs-k { color: var(--faint); }
.ap-vs-good .ap-vs-k { color: var(--brand); }
.ap-vs-col ul { list-style: none; margin: 0; padding: 0; display: grid; gap: 12px; }
.ap-vs-col li { position: relative; padding-left: 26px; color: var(--muted); }
.ap-vs-col li::before {
  position: absolute; left: 0; top: -.05em; width: 16px; text-align: center;
}
.ap-vs-bad li::before { content: "\00d7"; color: var(--faint); }
.ap-vs-good li::before { content: "\2713"; color: var(--brand); }
.ap-vs-div { align-self: stretch; width: 1px; background:
  linear-gradient(transparent, var(--line-strong) 22%, var(--line-strong) 78%, transparent); }
@media (max-width: 899px) { .ap-vs-div { display: none; } }

@media (prefers-reduced-motion: reduce) {
  .ap-tick-row { animation: none; }
  .ap-str-panel:not([hidden]) .ap-str-row { animation: none; }
  .ap-progress i, .ap-road-track i { transition: none; }
  .ap-num, .ap-step .ap-step-tx b, .ap-str-ink, .ap-cell::after { transition: none; }
}

/* ── shooting stars ───────────────────────────────────────────────────
   The sky itself is fixed — these are the only thing that moves, which is
   what makes the movement read as something crossing the field rather than
   the field sliding. Five tracks at different angles, lengths, speeds and
   delays, so they never fall into a pattern. */
:root[data-skin="nova"] .ap-streaks {
  position: absolute; inset: 0; overflow: hidden; pointer-events: none; z-index: 0;
}
:root[data-skin="nova"] .ap-streaks i {
  position: absolute; display: block; height: 1px; opacity: 0;
  background: linear-gradient(90deg, transparent, var(--sc, rgba(255,255,255,.85)), transparent);
  animation: ap-shoot var(--dur) ease-in infinite var(--delay);
}
@keyframes ap-shoot {
  0%, 86%  { opacity: 0; translate: 0 0; }
  89%      { opacity: 1; }
  100%     { opacity: 0; translate: var(--tx) var(--ty); }
}
:root[data-skin="nova"] .ap-streaks i:nth-child(1) {
  top: 8%;  left: -14%; width: 13vw; rotate: 14deg;
  --tx: 128vw; --ty: 30vw;  --dur: 11s; --delay: 0s;
}
:root[data-skin="nova"] .ap-streaks i:nth-child(2) {
  top: 34%; left: -12%; width: 9vw;  rotate: -8deg;
  --tx: 126vw; --ty: -18vw; --dur: 14s; --delay: 3.5s;
  --sc: rgba(255, 236, 210, .8);
}
:root[data-skin="nova"] .ap-streaks i:nth-child(3) {
  top: 58%; left: -10%; width: 15vw; rotate: 20deg;
  --tx: 124vw; --ty: 40vw;  --dur: 9s;  --delay: 6s;
}
:root[data-skin="nova"] .ap-streaks i:nth-child(4) {
  top: 74%; left: -16%; width: 7vw;  rotate: -14deg;
  --tx: 132vw; --ty: -26vw; --dur: 16s; --delay: 1.5s;
  --sc: rgba(232, 197, 148, .75);
}
:root[data-skin="nova"] .ap-streaks i:nth-child(5) {
  top: 22%; left: -11%; width: 11vw; rotate: 6deg;
  --tx: 127vw; --ty: 12vw;  --dur: 13s; --delay: 8.5s;
}

@media (prefers-reduced-motion: reduce) {
  :root[data-skin="nova"] .ap-streaks { display: none; }
}

/* ══ NOVA UI SYSTEM ═══════════════════════════════════════════════════
   Built to the supplied measured spec. Everything is scoped to the nova
   skin, so the other 112 pages keep the site's own px role layer.

   Two departures from the spec, both deliberate:

   · PP Neue Montreal is commercial (Pangram Pangram) and cannot be
     licensed here, so Instrument Sans stands in — one of the free
     substitutes the build kit itself names. The spec's real point is
     honoured: regular and medium are declared as separate families, not
     as weights of one, because the nav has to render medium.
   · Clash Display IS free, so it is self-hosted at 400/500/600 rather
     than pulled from a CDN, matching how every other face here loads. */

@font-face {
  font-family: 'Clash Display';
  src: url('assets/fonts/clash-display-400.woff2') format('woff2');
  font-weight: 400; font-style: normal; font-display: swap;
}
@font-face {
  font-family: 'Clash Display';
  src: url('assets/fonts/clash-display-500.woff2') format('woff2');
  font-weight: 500; font-style: normal; font-display: swap;
}
@font-face {
  font-family: 'Clash Display';
  src: url('assets/fonts/clash-display-600.woff2') format('woff2');
  font-weight: 600; font-style: normal; font-display: swap;
}

:root[data-skin="nova"] {
  --nv-display: 'Clash Display', 'Host Grotesk', ui-sans-serif, system-ui, sans-serif;
  --nv-body: 'Instrument Sans', ui-sans-serif, system-ui, sans-serif;
  --nv-gold: #D58300;
  --nv-gold-2: #E48C00;
  --nv-w80: rgba(255, 255, 255, .8);
  --nv-w60: rgba(255, 255, 255, .8);
  --nv-w50: rgba(255, 255, 255, .74);
  /* the single border treatment for the whole skin */
  --nv-edge: 1px solid rgba(255, 255, 255, .04);
}

/* ── type scale, all vw ───────────────────────────────────────────────
   line-height 1.5 and letter-spacing normal throughout, per the spec —
   this skin runs no tracking adjustments anywhere. */
:root[data-skin="nova"] :is(
  .ap-jrn-h, .ap-jrn-sub, .ap-mark-tx b, .ap-mark-tx em,
  .ap-h1, .ap-sub, .ap-eyebrow, .ap-sec-k, .ap-sec-h, .ap-sec-d,
  .ap-say, .ap-intent-tx p, .ap-cell b, .ap-cell p, .ap-stats b, .ap-stats span,
  .ap-hold-list li, .ap-note p, .ap-str-tab, .ap-str-row dt, .ap-str-row dd,
  .ap-vs-k, .ap-vs-col li, .ap-tick-list li, .ap-crumbs a, .ap-crumbs span
) { letter-spacing: normal !important; }

/* headings and anything numeric — Clash */
:root[data-skin="nova"] .ap-h1.ap-h1.ap-h1 {
  font-family: var(--nv-display) !important; font-weight: 500 !important;
  font-size: 3.2vw !important; line-height: 1.15 !important; color: #fff !important;
}
:root[data-skin="nova"] .ap-h1 em { color: var(--nv-gold-2) !important; }
:root[data-skin="nova"] .ap-jrn-h.ap-jrn-h.ap-jrn-h,
:root[data-skin="nova"] .ap-sec-h.ap-sec-h.ap-sec-h,
:root[data-skin="nova"] .ap-say.ap-say.ap-say,
:root[data-skin="nova"] .abstand-say.abstand-say.abstand-say {
  font-family: var(--nv-display) !important; font-weight: 500 !important;
  font-size: 1.667vw !important; line-height: 1.5 !important; color: #fff !important;
}
:root[data-skin="nova"] .ap-stats.ap-stats b {
  font-family: var(--nv-display) !important; font-weight: 600 !important;
  font-size: 3.47vw !important; line-height: 1.2 !important; color: #ED9C00 !important;
}
:root[data-skin="nova"] .ap-cell.ap-cell b,
:root[data-skin="nova"] .ap-mark-tx.ap-mark-tx b {
  font-family: var(--nv-display) !important; font-weight: 500 !important;
  font-size: 1.389vw !important; line-height: 1.5 !important;
}
:root[data-skin="nova"] .ap-mark-tx.ap-mark-tx b { color: var(--nv-gold) !important; }
:root[data-skin="nova"] .ap-cell.ap-cell b { color: #fff !important; }

/* body copy — the Montreal stand-in */
:root[data-skin="nova"] :is(.ap-jrn-sub, .ap-sub, .ap-sec-d).ap-jrn-sub,
:root[data-skin="nova"] .ap-sub.ap-sub, :root[data-skin="nova"] .ap-sec-d.ap-sec-d {
  font-family: var(--nv-body) !important; font-weight: 400 !important;
  font-size: 1.389vw !important; line-height: 1.5 !important; color: var(--nv-w80) !important;
}
:root[data-skin="nova"] :is(.ap-cell p, .ap-intent-tx p, .ap-str-row dd,
                            .ap-vs-col li, .ap-hold-list li, .ap-note p) {
  font-family: var(--nv-body) !important; font-weight: 400 !important;
  font-size: 1.25vw !important; line-height: 1.5 !important; color: var(--nv-w60) !important;
}
:root[data-skin="nova"] .ap-mark-tx.ap-mark-tx em,
:root[data-skin="nova"] :is(.ap-eyebrow, .ap-sec-k, .ap-vs-k, .ap-tick-list li, .ap-stats span, .ap-badge-txt) {
  font-family: var(--nv-body) !important; font-weight: 400 !important;
  font-size: 1.11vw !important; line-height: 1.5 !important; color: var(--nv-w80) !important;
  text-transform: none !important;
}
:root[data-skin="nova"] :is(.ap-sec-k, .ap-vs-k) { color: var(--nv-gold) !important; }

/* The stream tabs are a control, so the role layer sets them at 15px — but
   on this page they sit beside 1.25vw body copy and read as a caption
   rather than a choice. Registered here at the skin's own control size. */
:root[data-skin="nova"] .ap-str-tab.ap-str-tab {
  font-family: var(--nv-body) !important; font-weight: 500 !important;
  font-size: 1.11vw !important; line-height: 1.5 !important;
}
@media (max-width: 600px) {
  :root[data-skin="nova"] .ap-str-tab.ap-str-tab { font-size: 3.6vw !important; }
}

/* Buttons in the page body take the medium face at the skin's size. The
   header is deliberately absent from this skin's type rules entirely: it
   keeps the site's own face and size, so the bar reads identically here
   and on the other 94 pages. Only its colours are this skin's business. */
:root[data-skin="nova"] .ap-cta .btn {
  font-family: var(--nv-body) !important; font-weight: 500 !important;
  font-size: 1.11vw !important; line-height: 1.5 !important;
}

/* ── one border treatment, and the blur that actually separates cards ── */
:root[data-skin="nova"] :is(.ap-cell, .ap-stats li, .ap-str, .ap-hold,
                            .ap-vs-col, .ap-note, .ap-tick-list li, .ap-eyebrow) {
  border: var(--nv-edge) !important;
  -webkit-backdrop-filter: blur(12px); backdrop-filter: blur(12px);
}
:root[data-skin="nova"] .ap-note { border-left: 3px solid var(--nv-gold) !important; }

/* ── radius scale ─────────────────────────────────────────────────────── */
:root[data-skin="nova"] .ap-tick-list li { border-radius: .833vw; }
:root[data-skin="nova"] .ap-cta .btn { border-radius: .972vw; }
:root[data-skin="nova"] :is(.ap-cell, .ap-stats li, .ap-vs-col, .ap-str, .ap-hold) {
  border-radius: 1.389vw;
}

/* ── cards to the measured sizes ──────────────────────────────────────── */
@media (min-width: 601px) {
  :root[data-skin="nova"] .ap-bento { gap: 1.389vw; }
  :root[data-skin="nova"] .ap-cell { padding: 1.667vw; }
  :root[data-skin="nova"] .ap-stats { gap: 1.389vw; }
  :root[data-skin="nova"] .ap-stats li { padding: 1.389vw; }
  :root[data-skin="nova"] .ap-vs { gap: 1.389vw; }
  /* heading block to the content beneath it */
  :root[data-skin="nova"] .ap-head { margin-bottom: 3.2vw; }
  /* The first two markers hang their labels above the top of the canvas —
   a 6vw riser and a couple of lines of text — so at 3.2vw of clearance
   they sat level with the heading: "Frame" 103px above the sub-line's
   baseline and "Feasibility" straight through it. The gap has to clear
   the tallest label, not the canvas edge, and both scale with the
   viewport, so it is stated in the same units they are. */
:root[data-skin="nova"] .ap-jrn-head + .ap-canvas { margin-top: 10.5vw; }
  /* section rhythm */
  :root[data-skin="nova"] .ap-sec { margin-top: 10vw; }
  :root[data-skin="nova"] .ap-tick-sec { margin-top: 11vw; }
}

/* ── the CTA ──────────────────────────────────────────────────────────── */
:root[data-skin="nova"] .ap-cta .btn-primary {
  min-width: 16.42vw; min-height: 3.25vw;
  padding: .79vw 2.5vw; gap: .833vw;
  background: #fff !important; color: #212121 !important;
}
:root[data-skin="nova"] .ap-cta .btn-primary:hover { background: #f0ece7 !important; }

/* ── full-bleed rhythm; no px container on this skin ─────────────────── */
:root[data-skin="nova"] .ap-hero { padding-top: 9vw; }

/* The hero is its own ground: flat #101010, no starfield, no glow, no
   ruled grid. The artwork carries all the light in this band, and anything
   behind it was reading as noise around the illustration's own bloom. The
   field resumes below the hero, where it has room to be seen. */
:root[data-skin="nova"] .ap-hero { background: #101010; }
:root[data-skin="nova"] .ap-hero-fx { display: none; }

/* ── hero: the two-column split ───────────────────────────────────────────
   Every measurement here is off the reference frame at 1440px and stated in
   vw, which is how that composition is authored — it is a single fixed
   layout that scales, not a set of breakpoints. The one rule worth stating
   plainly: the illustration is 54vw inside a 42.66vw column, and the
   overflow on both sides is the composition. It is not a bug to fix. */
:root[data-skin="nova"] .ap-hero {
  padding: 0 0 clamp(40px, 5vw, 68px); text-align: left;
}
:root[data-skin="nova"] .ap-hero-split {
  display: flex; align-items: center; justify-content: space-between;
  padding: 0 7vw; height: 46.67vw;
}
:root[data-skin="nova"] .ap-hero-l { width: 50%; margin-bottom: 8vw; }
:root[data-skin="nova"] .ap-hero-r {
  width: 50%; display: flex; align-items: center; justify-content: center;
}
/* The reference runs this at 54vw inside a 42.66vw column so the art bleeds
   past both edges. That was right for their artwork, which is mostly empty
   at the margins; ours carries its subject much closer to the frame, so at
   54vw it simply read as too big. It sits inside its column now, centred,
   with the hero's dark ground all the way around it. */
:root[data-skin="nova"] .ap-illus {
  position: relative; width: 38vw; max-width: 100%; aspect-ratio: 1349 / 1166;
  height: auto; flex: none; margin: 0 auto;
}
:root[data-skin="nova"] .ap-illus img {
  width: 100%; height: 100%; object-fit: contain; display: block;
}

/* the badge — ring is the padding on a #404040 parent, not a border */
:root[data-skin="nova"] .ap-badge {
  position: relative; display: block; width: 29vw; height: 4.1vw;
  border: 0 !important; border-radius: 1.25vw;
  background: #404040; padding: .05vw;
}
:root[data-skin="nova"] .ap-badge-in {
  position: relative; z-index: 20; display: flex; align-items: center; gap: .55vw;
  width: 100%; height: 100%; border-radius: 1.2vw; background: #212121;
  padding-left: 1.6vw;
}
:root[data-skin="nova"] .ap-badge-ico { width: 1.8vw; height: 1.8vw; flex: none; }
:root[data-skin="nova"] .ap-badge-txt {
  font-size: 1.2vw !important; color: rgba(255, 255, 255, .8) !important;
  white-space: nowrap; letter-spacing: normal !important; text-transform: none !important;
}
/* no overflow:visible — the SVG viewport clipping keeps the trail on the edge */
:root[data-skin="nova"] .ap-beam { position: absolute; inset: 0; z-index: 10; }

:root[data-skin="nova"] .ap-hero .ap-h1 { margin: 1.6vw 0 0; text-align: left; }
:root[data-skin="nova"] .ap-hero .ap-h1.ap-h1.ap-h1 { line-height: 1.09 !important; }
/* the second line is a gold block in the same face, not the serif italic
   gradient the rest of the site uses for an <em> inside a heading */
:root[data-skin="nova"] .ap-hero .ap-h1 em {
  display: block; font-family: inherit; font-style: normal; font-weight: inherit;
  background: none; -webkit-background-clip: border-box; background-clip: border-box;
  color: #E48C00 !important;
}
/* pure white, unlike every other body string in this skin */
:root[data-skin="nova"] .ap-hero .ap-sub.ap-sub {
  margin: .83vw 0 0; max-width: none; color: #fff !important;
}
:root[data-skin="nova"] .ap-hero .ap-cta { justify-content: flex-start; margin: 0; }
:root[data-skin="nova"] .ap-btn-hero {
  display: inline-flex; align-items: center; justify-content: center; gap: .79vw;
  border-radius: .833vw !important;          /* deliberately not the nav's .972vw */
  padding: 1.11vw 6.8vw; margin-top: 2.78vw;
}
:root[data-skin="nova"] .ap-btn-ico { width: 1.25vw; height: 1.25vw; flex: none; }

/* The three figures, to the reference: separate cards rather than one
   ruled strip, each on a gradient that lifts at the top and settles into
   the page at the bottom, with the number and its line centred.

   The shared component builds the strip by laying the cells on a 1px grid
   gap over a border-coloured background — that is what drew the hairlines
   between them — so the ground and the border are cleared here before the
   cards can stand apart. It also takes the hero's 7vw margin: it was
   inheriting the page grid's 62rem, which left a 992px band under a
   1240px composition with cells narrow enough to break "stages, agreed
   before any of them start" across four lines. */
:root[data-skin="nova"] .ap-hero .wrap { max-width: none; padding: 0 7vw; }
:root[data-skin="nova"] .ap-hero .ap-stats {
  margin: 0; max-width: none;
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 1.7vw;
  background: none; border: 0; border-radius: 0; overflow: visible;
}
:root[data-skin="nova"] .ap-hero .ap-stats li {
  background: linear-gradient(180deg, #272727 0%, #1a1a1a 42%, #101010 100%);
  border: 1px solid rgba(255, 255, 255, .05) !important;
  border-radius: 1.7vw;
  padding: 2.6vw 2.2vw;
  text-align: center;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 1.1vw;
}
:root[data-skin="nova"] .ap-hero .ap-stats b { margin: 0; line-height: 1 !important; }
:root[data-skin="nova"] .ap-hero .ap-stats span {
  margin: 0; max-width: 20ch;
  font-size: 1.25vw !important; color: var(--nv-w80) !important;
}

@media (max-width: 600px) {
  :root[data-skin="nova"] .ap-hero .ap-stats {
    grid-template-columns: 1fr; gap: 4.2vw;
  }
  :root[data-skin="nova"] .ap-hero .ap-stats li {
    border-radius: 4.6vw; padding: 8vw 6vw; gap: 3.2vw;
  }
  :root[data-skin="nova"] .ap-hero .ap-stats span { font-size: 3.9vw !important; }
}

/* The trail sits in the hero's own band on the hero's own margin, with no
   plate, rule or chip of its own — the point is that it reads as the top
   line of the hero rather than as a component parked above it. */
:root[data-skin="nova"] .ap-hero .ap-crumbs {
  margin: 0; padding: 1.6vw 7vw 0;
}
:root[data-skin="nova"] .ap-hero .ap-crumbs ol { justify-content: flex-start; gap: .55vw; }
:root[data-skin="nova"] .ap-hero .ap-crumbs li + li::before { margin-right: .55vw; }
:root[data-skin="nova"] .ap-hero .ap-crumbs :is(a, span) {
  font-family: var(--nv-body) !important; font-weight: 400 !important;
  font-size: .97vw !important; letter-spacing: normal !important;
}
:root[data-skin="nova"] .ap-hero .ap-crumbs a { color: rgba(255, 255, 255, .74) !important; }
:root[data-skin="nova"] .ap-hero .ap-crumbs a:hover { color: var(--nv-gold-2) !important; }
:root[data-skin="nova"] .ap-hero .ap-crumbs span { color: rgba(255, 255, 255, .8) !important; }
/* the hero's height is measured from the split, so the trail is absorbed
   into the 8vw the left column already reserves rather than added on top */
:root[data-skin="nova"] .ap-hero-split { margin-top: -3.2vw; }

/* entrance — CSS only, so nothing here waits on a script to flip a class */
@keyframes ap-hero-in { from { opacity: 0; transform: translateY(1.4vw); } to { opacity: 1; transform: none; } }
@keyframes ap-hero-img { from { opacity: 0; transform: scale(.97); } to { opacity: 1; transform: none; } }
:root[data-skin="nova"] .heroText {
  animation: ap-hero-in .9s cubic-bezier(.22, 1, .36, 1) both;
}
:root[data-skin="nova"] .ap-hero-l > .heroText:nth-child(1) { animation-delay: .05s; }
:root[data-skin="nova"] .ap-hero-l > .heroText:nth-child(2) { animation-delay: .18s; }
:root[data-skin="nova"] .ap-hero-l > .heroText:nth-child(3) { animation-delay: .31s; }
:root[data-skin="nova"] .heroImage {
  animation: ap-hero-img 1.1s cubic-bezier(.22, 1, .36, 1) both; animation-delay: .05s;
}
@media (prefers-reduced-motion: reduce) {
  :root[data-skin="nova"] :is(.heroText, .heroImage) { animation: none; }
  :root[data-skin="nova"] .ap-beam circle { display: none; }
}

@media (max-width: 600px) {
  :root[data-skin="nova"] .ap-hero-split {
    flex-direction: column-reverse; height: auto; padding: 0 6.154vw;
  }
  :root[data-skin="nova"] .ap-hero-l,
  :root[data-skin="nova"] .ap-hero-r { width: 100%; }
  :root[data-skin="nova"] .ap-hero-l {
    margin-bottom: 12vw; display: flex; flex-direction: column; align-items: center;
  }
  :root[data-skin="nova"] .ap-badge { display: none; }
  /* the kit's own mobile size — the page-wide 9.5vw splits "built to be
     defended" across two lines, which reads as an accident */
  :root[data-skin="nova"] .ap-hero .ap-h1.ap-h1.ap-h1 { font-size: 8.2vw !important; }
  :root[data-skin="nova"] .ap-hero .ap-h1 { text-align: center; }
  :root[data-skin="nova"] .ap-hero .ap-sub.ap-sub { text-align: center; }
  :root[data-skin="nova"] .ap-btn-hero { width: 100%; padding: 3.5vw 0; }
  :root[data-skin="nova"] .ap-btn-ico { width: 4.1vw; height: 4.1vw; }
  :root[data-skin="nova"] .ap-illus { width: 100%; height: 88vw; top: 0; }
  :root[data-skin="nova"] .ap-hero .ap-stats li { text-align: center; }
  :root[data-skin="nova"] .ap-hero .ap-crumbs { padding: 3vw 6.154vw 0; }
  :root[data-skin="nova"] .ap-hero .ap-crumbs ol { justify-content: center; }
  :root[data-skin="nova"] .ap-hero .ap-crumbs :is(a, span) { font-size: 3.2vw !important; }
  :root[data-skin="nova"] .ap-hero-split { margin-top: 0; }
}
@media (max-width: 600px) {
  /* the spec re-declares the ramp about 3.7x larger below this breakpoint */
  :root[data-skin="nova"] .ap-h1.ap-h1.ap-h1 { font-size: 9.5vw !important; }
  :root[data-skin="nova"] .ap-jrn-h.ap-jrn-h.ap-jrn-h,
  :root[data-skin="nova"] .ap-sec-h.ap-sec-h.ap-sec-h,
  :root[data-skin="nova"] .ap-say.ap-say.ap-say { font-size: 6.2vw !important; }
  :root[data-skin="nova"] .ap-sub.ap-sub, :root[data-skin="nova"] .ap-sec-d.ap-sec-d,
  :root[data-skin="nova"] .ap-jrn-sub.ap-jrn-sub.ap-jrn-sub { font-size: 4.4vw !important; }
  :root[data-skin="nova"] .ap-cell.ap-cell b,
  :root[data-skin="nova"] .ap-mark-tx.ap-mark-tx b { font-size: 4.6vw !important; }
  :root[data-skin="nova"] :is(.ap-cell p, .ap-intent-tx p, .ap-str-row dd,
    .ap-vs-col li, .ap-hold-list li, .ap-note p) { font-size: 4.1vw !important; }
  :root[data-skin="nova"] .ap-mark-tx.ap-mark-tx em,
  :root[data-skin="nova"] :is(.ap-eyebrow, .ap-sec-k, .ap-vs-k, .ap-stats span) {
    font-size: 3.6vw !important;
  }
  :root[data-skin="nova"] .ap-stats.ap-stats b { font-size: 11vw !important; }
  :root[data-skin="nova"] :is(.ap-cell, .ap-stats li, .ap-vs-col, .ap-str, .ap-hold) {
    border-radius: 6.154vw;
  }
  :root[data-skin="nova"] .wrap { padding-inline: 5.128vw; }
  :root[data-skin="nova"] .ap-cta { flex-direction: column; gap: 3.1vw; width: 100%; }
  :root[data-skin="nova"] .ap-cta .btn {
    font-size: 4.1vw !important;
    min-width: 0; width: 100%; min-height: 12.8vw;
    padding: 3.1vw 6.2vw; gap: 3.1vw; border-radius: 3.6vw;
  }
  :root[data-skin="nova"] .ap-cta .btn svg { width: 4.6vw; height: 4.6vw; }
  :root[data-skin="nova"] .ap-sec { margin-top: 20vw; }
}

/* ══ NOVA MOBILE HEADER ═══════════════════════════════════════════════
   Measured off nova.ambitio.club at 390px: a 56px (14.36vw) sticky bar on
   rgba(17,17,17,.6), the wordmark 98x20 at 16px in from the left, and a
   plain 24x24 (6.154vw) hamburger 16px in from the right. No Sign in, no
   Get Started — below 600px their nav collapses to just those two things.

   Scoped to this skin AND this breakpoint, so the site's own mobile header
   is untouched on the other 112 pages. */
@media (max-width: 600px) {
  :root[data-skin="nova"] .site-header {
    height: 14.36vw; min-height: 14.36vw;
    background: rgba(17, 17, 17, .6) !important;
    -webkit-backdrop-filter: blur(10px); backdrop-filter: blur(10px);
    border: 0 !important; box-shadow: none !important;
    border-radius: 0 !important;
  }
  :root[data-skin="nova"] .site-header .hdr-inner,
  :root[data-skin="nova"] .site-header > .wrap {
    height: 100%; padding-inline: 4.1vw;
    display: flex; align-items: center; justify-content: space-between;
  }
  /* the wordmark at their proportions */
  :root[data-skin="nova"] .site-header .brand-mark {
    width: 25.1vw; height: 5.13vw; background: #fff;
  }
  /* below 600px their bar carries nothing but the logo and the burger */
  :root[data-skin="nova"] .site-header :is(.nav-signin, .nav-start, .nav-links, .menu-trigger) {
    display: none !important;
  }

  /* ── the hamburger ──────────────────────────────────────────────────
     Ours ships a pill backdrop, a "Menu"/"Close" label and a plus glyph.
     The reference is three bare rules, so the label and backdrop go and the
     lines are drawn on the button itself — the third shorter, as theirs is. */
  :root[data-skin="nova"] .site-header .burger {
    position: relative; width: 6.154vw; height: 6.154vw;
    padding: 0; background: none !important; border: 0 !important;
    box-shadow: none !important; border-radius: 0 !important;
  }
  :root[data-skin="nova"] .site-header .burger :is(.bg-tx, .bg-ic) { display: none !important; }
  :root[data-skin="nova"] .site-header .burger .ab {
    position: absolute; inset: 0; display: block;
    background: none !important; border: 0 !important; box-shadow: none !important;
    transform: none !important; border-radius: 0 !important;
  }
  :root[data-skin="nova"] .site-header .burger::before,
  :root[data-skin="nova"] .site-header .burger::after,
  :root[data-skin="nova"] .site-header .burger .ab i {
    content: ""; position: absolute; left: 0; height: .52vw;
    background: #fff !important; border-radius: .26vw;
  }
  :root[data-skin="nova"] .site-header .burger::before { top: 1.1vw; width: 100%; }
  :root[data-skin="nova"] .site-header .burger .ab i  { top: 2.9vw; width: 100%; }
  :root[data-skin="nova"] .site-header .burger::after { top: 4.7vw; width: 74%; }
  /* the reference keeps the full wordmark on mobile; ours swaps to the
     icon-only mark at narrow widths, so that swap is reverted here */
  :root[data-skin="nova"] .site-header .brand-mark {
    -webkit-mask-image: url('assets/wordmark.webp'); mask-image: url('assets/wordmark.webp');
    -webkit-mask-size: contain; mask-size: contain;
    -webkit-mask-repeat: no-repeat; mask-repeat: no-repeat;
    -webkit-mask-position: left center; mask-position: left center;
  }
}

/* ══ NOVA "WHO WE ARE" ════════════════════════════════════════════════
   The shared About block renders in the site's system stack, so on this
   page it arrived in a different typeface from everything above and below
   it — the one section that read as though it came from another site.
   Faces only: sizes stay on the shared scale. */
:root[data-skin="nova"] #about :is(.ab-h, .ab-big b, .ab-big span, .abstand-say) {
  font-family: var(--nv-display) !important; font-weight: 500 !important;
}
:root[data-skin="nova"] #about :is(.ab-kicker, .ab-note, .ab-small p, .ab-small b,
                                   .ab-lede, .ab-stand p, p) {
  font-family: var(--nv-body) !important;
}
:root[data-skin="nova"] #about .ab-h { color: #fff !important; letter-spacing: normal !important; }
:root[data-skin="nova"] #about .ab-kicker { color: var(--nv-w80) !important; }
:root[data-skin="nova"] #about .ab-star { color: var(--nv-gold-2) !important; }
:root[data-skin="nova"] #about :is(.ab-note, .ab-small p) { color: var(--nv-w60) !important; }

/* ══ NOVA HUB GRID ════════════════════════════════════════════════════
   The section hub carries the shared .abx card grid, which is entirely
   token-driven and so follows the dark skin for colour without help. What
   it does not inherit is this skin's geometry — the cards kept a 14px
   radius and a hairline border while everything around them is at 1.389vw
   with the skin's edge. Matched here so the hub and its six subpages look
   like one section. */
:root[data-skin="nova"] .abx-card {
  border: var(--nv-edge) !important;
  border-radius: 1.389vw;
  background: rgba(255, 255, 255, .02);
  -webkit-backdrop-filter: blur(12px); backdrop-filter: blur(12px);
}
:root[data-skin="nova"] .abx-card:hover { border-color: rgba(232, 147, 12, .35) !important; }
:root[data-skin="nova"] .abx-card h3 { color: #fff; }
:root[data-skin="nova"] .abx-go { color: var(--nv-gold-2); }
:root[data-skin="nova"] .abx-lead { color: var(--nv-w80); }
:root[data-skin="nova"] .abx-areas { gap: 1.389vw; }
@media (max-width: 600px) {
  :root[data-skin="nova"] .abx-card { border-radius: 3.6vw; }
  :root[data-skin="nova"] .abx-areas { gap: 3.6vw; }
}

/* ══ NOVA FAQ ═════════════════════════════════════════════════════════
   Built to ambitiofaq.html. Every value below is that kit's, mapped onto
   the shared component's markup — same accordion behaviour, same reading
   order in the accessibility tree, only the dressing changes.

     kit                     ours
     .faq  margin 0 12vw     #cfq > .wrap
     .faq-list               ul.faq
     .item                   li.fq
     .row                    .fq h3 > button
     .plus                   .fq-ic
     .panel                  .fq-p

   One substitution: the kit's ring holds a literal "+" glyph in Space
   Grotesk 800; the shared component draws the mark with ::before/::after
   and drops the vertical bar on open. Keeping the drawn mark and rotating
   the pair 45deg gives the kit's cross without shipping a font for one
   character — so the bar is put back on open and the ring rotates. */
:root[data-skin="nova"] :is(#faq, #cfq) {
  /* set on the SECTION, so :root cannot reach it — the same trap atlas and
     lab each had to fix at this level */
  --fq-card: #171717;
  --fq-edge: var(--line);
}
:root[data-skin="nova"] #cfq { background: none; border: 0; }
:root[data-skin="nova"] #cfq > .wrap { max-width: none; padding: 0 12vw; }

:root[data-skin="nova"] #cfq .fq-head { text-align: center; }
:root[data-skin="nova"] #cfq .fq-pill { display: none; }
:root[data-skin="nova"] #cfq h2 {
  font-family: var(--nv-display) !important; font-weight: 500 !important;
  font-size: 1.667vw !important; line-height: 1.5 !important; color: #fff !important;
  margin: 0;
}
/* the shared heading sets this word in red serif italic — the kit runs the
   line in one voice, so it simply rejoins the sentence */
:root[data-skin="nova"] #cfq h2 em {
  font-family: inherit !important; font-style: normal !important;
  font-weight: inherit !important; color: #fff !important;
  background: none !important; -webkit-text-fill-color: #fff;
}
:root[data-skin="nova"] #cfq .lede {
  font-family: var(--nv-body) !important; font-weight: 400 !important;
  font-size: 1.389vw !important; line-height: 1.5 !important;
  color: rgba(255, 255, 255, .74) !important; margin: .3vw 0 0;
}

:root[data-skin="nova"] #cfq .faq {
  display: flex; flex-direction: column; gap: 1.667vw;
  margin: 3.2vw 0 0; padding: 0; max-width: none; list-style: none;
}
:root[data-skin="nova"] #cfq .fq {
  border: 1.5px solid rgba(255, 255, 255, .1);
  border-radius: 1.389vw;
  background: linear-gradient(90deg, rgba(24, 24, 24, .6) 0%, rgba(24, 24, 24, .6) 100%);
  -webkit-backdrop-filter: blur(16px); backdrop-filter: blur(16px);
  box-shadow: -2px -2px 16px 0 rgba(0, 0, 0, .15);
  overflow: hidden;
}
:root[data-skin="nova"] #cfq .fq-glow { display: none; }
:root[data-skin="nova"] #cfq .fq h3 { margin: 0; }
:root[data-skin="nova"] #cfq .fq h3 > button {
  width: 100%; display: flex; align-items: center; justify-content: space-between;
  gap: 1.11vw; padding: 1.667vw; background: none; border: 0;
  text-align: left; cursor: pointer;
}
/* the kit puts the toggle on the right; the markup writes it first, so it
   is reordered visually rather than moved in the DOM */
:root[data-skin="nova"] #cfq .fq-ic { order: 2; }
:root[data-skin="nova"] #cfq .fq-tx { order: 1; flex: 1 1 auto; min-width: 0; }
:root[data-skin="nova"] #cfq .fq-row { display: block; }
:root[data-skin="nova"] #cfq .fq-meta { display: none; }
:root[data-skin="nova"] #cfq .fq-q {
  font-family: var(--nv-display) !important; font-weight: 400 !important;
  font-size: 1.25vw !important; line-height: 1.5 !important; color: #fff !important;
}

/* the ring. min-width and max-width are both pinned: .fq-ic is a flex item,
   whose min-width defaults to auto and floors it at min-content — without
   this it draws half again too large. */
:root[data-skin="nova"] #cfq .fq-ic {
  display: flex; align-items: center; justify-content: center; flex: none;
  width: 1.667vw; height: 1.667vw;
  min-width: 1.667vw; max-width: 1.667vw; min-height: 1.667vw;
  border-radius: 50%; border: 2px solid var(--nv-gold); background: none;
  transform: rotate(0deg); transform-origin: center;
  transition: transform .15s var(--ease), border-color .15s var(--ease);
}
:root[data-skin="nova"] #cfq .fq-ic::before,
:root[data-skin="nova"] #cfq .fq-ic::after { background: var(--nv-gold); }
:root[data-skin="nova"] #cfq .fq-ic::before { height: 2px; }
:root[data-skin="nova"] #cfq .fq-ic::after { width: 2px; }
:root[data-skin="nova"] #cfq .fq[data-open="true"] .fq-ic { transform: rotate(45deg); }
:root[data-skin="nova"] #cfq .fq[data-open="true"] .fq-ic::after { opacity: 1; }

/* the answer. Padding never goes on .fq-p > div: that is the grid item the
   collapse sizes to 0fr, and a grid item's automatic minimum includes its
   own padding, which would hold every closed row open by that much. */
:root[data-skin="nova"] #cfq .fq-p { opacity: 0; transition: grid-template-rows .2s cubic-bezier(.4,0,.2,1), opacity .2s cubic-bezier(.4,0,.2,1); }
:root[data-skin="nova"] #cfq .fq[data-open="true"] .fq-p { opacity: 1; }
:root[data-skin="nova"] #cfq .fq-p > div { padding: 0; }
:root[data-skin="nova"] #cfq .fq-p :is(p, .fq-list) {
  font-family: var(--nv-body) !important; font-weight: 400 !important;
  font-size: 1.25vw !important; line-height: 1.5 !important;
  color: rgba(255, 255, 255, .74) !important;
  margin: 0; padding-inline: 1.667vw;
}
:root[data-skin="nova"] #cfq .fq-p p + p,
/* the kit styles only p + p, because none of its placeholder answers put a
   paragraph after a list; ours do, and without this the closing line runs
   into the last bullet */
:root[data-skin="nova"] #cfq .fq-p .fq-list + p { margin-top: 1.11vw; }
:root[data-skin="nova"] #cfq .fq-p .fq-list {
  margin-top: 1.25vw; list-style-type: square;
  padding-left: calc(1.667vw + 1.4vw);
}
:root[data-skin="nova"] #cfq .fq-p .fq-list li { padding-left: .56vw; }
:root[data-skin="nova"] #cfq .fq-p :is(p, .fq-list):last-child { padding-bottom: 1.667vw; }

:root[data-skin="nova"] #cfq .fq-all { text-align: center; margin-top: 2.2vw; }
:root[data-skin="nova"] #cfq .fq-all a {
  font-family: var(--nv-body) !important; font-size: 1.11vw !important;
  color: var(--nv-gold-2) !important; text-decoration: none;
}
:root[data-skin="nova"] #cfq .fq-all a:hover { text-decoration: underline; }

@media (max-width: 600px) {
  :root[data-skin="nova"] #cfq > .wrap { padding: 0 6.154vw; }
  :root[data-skin="nova"] #cfq h2 { font-size: 6.154vw !important; }
  :root[data-skin="nova"] #cfq .lede { font-size: 4.103vw !important; margin-top: 2.59vw; }
  :root[data-skin="nova"] #cfq .faq { gap: 5.128vw; margin-top: 6.154vw; }
  :root[data-skin="nova"] #cfq .fq { border-radius: 5.128vw; }
  :root[data-skin="nova"] #cfq .fq h3 > button { padding: 5.128vw; gap: 4.103vw; }
  :root[data-skin="nova"] #cfq .fq-q { font-size: 4.103vw !important; }
  :root[data-skin="nova"] #cfq .fq-ic {
    width: 5.641vw; height: 5.641vw;
    min-width: 5.641vw; max-width: 5.641vw; min-height: 5.641vw;
  }
  :root[data-skin="nova"] #cfq .fq-p :is(p, .fq-list) {
    font-size: 4.103vw !important; padding-inline: 6.154vw;
  }
  :root[data-skin="nova"] #cfq .fq-p p + p,
  :root[data-skin="nova"] #cfq .fq-p .fq-list + p { margin-top: 4.103vw; }
  :root[data-skin="nova"] #cfq .fq-p .fq-list {
    margin-top: 4.103vw; padding-left: calc(6.154vw + 4.6vw);
  }
  :root[data-skin="nova"] #cfq .fq-p :is(p, .fq-list):last-child { padding-bottom: 6.154vw; }
  :root[data-skin="nova"] #cfq .fq-all a { font-size: 3.6vw !important; }
}

/* ══ NOVA REACH ═══════════════════════════════════════════════════════
   The band above the questions: copy left, globe right, on the page's own
   starfield. It runs on the hero's 7vw margin rather than the FAQ's 12vw —
   the reference gives this band a wider bleed than the list below it, and
   at 12vw the globe had nowhere to be big. */
:root[data-skin="nova"] .ap-reach { margin: clamp(56px, 8vw, 116px) 0 clamp(40px, 5vw, 76px); }
:root[data-skin="nova"] .ap-reach-in {
  display: flex; align-items: center; justify-content: space-between;
  gap: 4vw; padding: 0 7vw;
}
:root[data-skin="nova"] .ap-reach-tx { flex: 1 1 auto; min-width: 0; }
:root[data-skin="nova"] .ap-reach-h {
  font-family: var(--nv-display) !important; font-weight: 500 !important;
  font-size: 2.78vw !important; line-height: 1.25 !important;
  color: #fff !important; margin: 0; text-wrap: balance;
}
:root[data-skin="nova"] .ap-reach-h em {
  font-family: inherit !important; font-style: normal !important;
  font-weight: inherit !important; color: var(--nv-gold-2) !important;
  background: none !important; -webkit-text-fill-color: var(--nv-gold-2);
}
:root[data-skin="nova"] .ap-reach-d {
  font-family: var(--nv-body) !important; font-weight: 400 !important;
  font-size: 1.25vw !important; line-height: 1.6 !important;
  color: rgba(255, 255, 255, .74) !important; margin: 1.6vw 0 0;
}
:root[data-skin="nova"] .ap-globe { flex: none; width: 42vw; }
:root[data-skin="nova"] .ap-globe img {
  width: 100%; height: auto; display: block;
}
@media (max-width: 900px) {
  :root[data-skin="nova"] .ap-reach-in { flex-direction: column-reverse; text-align: center; gap: 6vw; }
  :root[data-skin="nova"] .ap-reach-d { margin-inline: auto; }
  :root[data-skin="nova"] .ap-globe { width: 72vw; }
}
@media (max-width: 600px) {
  :root[data-skin="nova"] .ap-reach-in { padding: 0 6.154vw; }
  :root[data-skin="nova"] .ap-reach-h { font-size: 6.9vw !important; }
  :root[data-skin="nova"] .ap-reach-d { font-size: 4.103vw !important; margin-top: 4vw; }
  :root[data-skin="nova"] .ap-globe { width: 86vw; }
}

/* ══ NOVA DESKTOP HEADER ══════════════════════════════════════════════
   This block used to re-measure the whole bar to the reference: a 6.11vw
   height, 1.25vw links, a 6.81 x 1.40vw wordmark, 3.25vw buttons. Against
   the rest of the site that read as a different header on one page — 88px
   tall next to 77px everywhere else, 18px links next to 15px, and a
   wordmark at half the height it has on every other page.

   So the sizing is gone. The bar keeps the site's own metrics and this
   block now does one job: put it on a dark ground with light ink, which is
   the only thing this skin actually needs from it. Anything that changes
   the header's proportions belongs in the shared header rules, where it
   applies to all 95 pages at once. */
@media (min-width: 601px) {
  :root[data-skin="nova"] .site-header {
    background: #101010 !important;
    -webkit-backdrop-filter: none !important; backdrop-filter: none !important;
    border-bottom-color: rgba(255, 255, 255, .08) !important;
    box-shadow: none !important;
  }
  /* white on the dark bar, at the shared size */
  :root[data-skin="nova"] .site-header .nav-start {
    background: #fff !important; color: #212121 !important; border: 0 !important;
  }
  :root[data-skin="nova"] .site-header .nav-start:hover { background: #f0ece7 !important; }
}


/* ══ PRELOADER ════════════════════════════════════════════════════════
   Landing page only. Built to the founder's PercentLoader spec; the
   numbers below are that spec's, and the logic lives in preloader.js.

   Host Grotesk is already declared at the top of this file, so the reels
   inherit the brand face without a second @font-face.

   Registered here rather than in the type role layer on purpose: this is
   a full-screen readout that exists for under six seconds and shares no
   scale with the page beneath it. Giving it a role would put a size in
   that layer that nothing else can ever reuse. */
.pl {
  position: fixed; inset: 0; z-index: 9999;
  display: flex; align-items: center; justify-content: center;
  background: #F7F7F7;
  padding: env(safe-area-inset-top) env(safe-area-inset-right)
           env(safe-area-inset-bottom) env(safe-area-inset-left);
  will-change: transform;
  transition: transform 1.15s cubic-bezier(.76, 0, .24, 1);
}
/* The curtain lift. The original brief called for a cross-fade; the ask
   now is for it to travel upward, so the panel translates its own height
   and takes the ground with it. Slow on purpose — this is the reveal, and
   the hero underneath is worth uncovering rather than blinking to. */
.pl.is-gone { transform: translateY(-100%); pointer-events: none; }
/* the lock is on <html> so the restore cannot be lost if something else
   writes body.style.overflow while the overlay is up */
.pl-lock, .pl-lock body { overflow: hidden; }

.pl-stack {
  display: flex; flex-direction: column; align-items: center;
  /* rides up a little faster than the panel — parallax, so the lift reads
     as depth rather than one flat sheet sliding */
  transition: transform 1.15s cubic-bezier(.76, 0, .24, 1),
              opacity .5s ease .35s;
  /* backwards, NOT both: a filled-forwards animation keeps overriding the
     element's own transform after it ends, which silently pinned the stack
     at translateY(0) and killed the parallax on the way out. `backwards`
     supplies the from-state before it starts and then gets out of the way. */
  animation: pl-in .7s cubic-bezier(.22, 1, .36, 1) backwards;
}
.pl.is-gone .pl-stack { transform: translateY(-28%); opacity: 0; }
/* the landing beat: a small settle on the number once it reaches 100 */
.pl-stack.is-settled .pl-count { animation: pl-settle .42s cubic-bezier(.22, 1, .36, 1); }

@keyframes pl-in {
  from { opacity: 0; transform: translateY(14px); }
  to   { opacity: 1; transform: none; }
}
@keyframes pl-settle {
  0%   { transform: scale(1); }
  38%  { transform: scale(1.035); }
  100% { transform: scale(1); }
}

/* ── the counter ──────────────────────────────────────────────────────
   The digits and the % are one centred group, so the optical centre sits
   between them rather than the % hanging off a centred number. */
.pl-count {
  /* NOT align-items: baseline. A block with overflow other than visible has
     no baseline of its own — CSS substitutes its bottom margin edge — so a
     baseline-aligned % was lining up with the bottom of the clipped column
     rather than with the digits inside it. The gap scaled with --pl-step
     and was nearly invisible at 1em, obvious at 1.2em. The % is given a
     digit-shaped cell below and both are centred instead. */
  display: flex; align-items: center; justify-content: center;
  /* Baskerville is a macOS system face. Windows has no reliable equivalent
     — 'Baskerville Old Face' ships only with some Office installs — so the
     chain falls to Georgia, which is on effectively every Windows and Mac
     and sits in the same transitional-serif register. Libre Baskerville is
     the OFL cut and could be self-hosted the way Host Grotesk is, if this
     needs to look identical everywhere. */
  font-family: 'Baskerville', 'Libre Baskerville', 'Baskerville Old Face',
               Georgia, 'Times New Roman', serif;
  /* A serif carries contrast in its own stroke modulation; the uniform
     stroke that thickened the grotesque fills in the thins and flattens
     that, so it stays light and the weight does the work. */
  font-weight: 700;
  -webkit-text-stroke: .008em currentColor;
  paint-order: stroke fill;
  /* +5 on both ends of the clamp, and the vw nudged to match so the middle
     of the range grows by the same amount rather than only the extremes */
  font-size: clamp(61px, 7.9vw, 93px);
  /* The reel step: the clipped window, each strip cell and the JS transform
     all read it, so the roll travel and the window can never disagree.
     Tuned per face from its real line box — measured at an 88px size,
     Host Grotesk is 117px, Baskerville 101px, Apple Chancery 139px. At 1em
     (88px) a serif's descenders clip; 1.2em (105.6px) clears Baskerville
     with a little air and keeps the counter box tight. */
  --pl-step: 1.2em;
  line-height: 1;
  letter-spacing: -.02em;
  /* darker than the spec's #1F1F1F: on this ground the extra weight wants
     the extra contrast to read as solid rather than smudged. 18.9:1 */
  color: #0E0E0E;
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum" 1;
}
.pl-reels { display: flex; }
/* same box as a digit cell, so the two cannot drift apart */
.pl-pct {
  display: block; height: var(--pl-step); line-height: var(--pl-step);
}

/* Each column clips one digit box. Width is pinned to 1ch — with tabular
   figures that is the digit advance — which is what lets a new column
   animate its width from 0 and the row re-centre continuously instead of
   snapping when the value crosses 9→10 and 99→100. */
.pl-col {
  display: block; overflow: hidden;
  /* --pl-adv is the real tabular advance, measured in preloader.js from the
     face that actually resolved. The ch fallback only applies for the frame
     before that runs: `ch` ignores font-variant-numeric, so on a face whose
     tabular figures are wider than its natural ones it is too narrow. */
  width: var(--pl-adv, calc(1ch - .02em)); height: var(--pl-step);
  transition: width .3s ease-out, opacity .2s ease-out, transform .2s ease-out;
}
.pl-col.is-new { width: 0; opacity: 0; transform: translateY(40%); }

.pl-strip {
  display: block;
  transition: transform .32s cubic-bezier(.22, 1, .36, 1);
}
.pl-strip > span {
  display: block; height: var(--pl-step); line-height: var(--pl-step);
  text-align: center;             /* script figures sit off-centre in the advance */
}

/* ── the line ─────────────────────────────────────────────────────────
   Deliberately slower and differently eased than the reels: the digits
   are the crisp readout, the line is the trailing confirmation. */
.pl-track {
  width: 240px; max-width: 60vw; height: 4px;
  margin-top: 24px;               /* below the counter's baseline box */
  border-radius: 9999px;
  background: transparent;        /* no visible unfilled track */
  overflow: hidden;
}
.pl-fill {
  position: relative;
  display: block; width: 0; height: 100%;
  background: #B1060F; border-radius: 9999px;
  transform-origin: left;
  transition: width .7s cubic-bezier(0, 0, .2, 1);
}
/* a brighter head at the leading edge — the bar reads as being drawn
   rather than as a rectangle changing width */
.pl-fill::after {
  content: ""; position: absolute; right: 0; top: 50%;
  width: 22px; height: 200%; transform: translateY(-50%);
  border-radius: 9999px;
  background: linear-gradient(90deg, rgba(177, 6, 15, 0), #D8232E);
  opacity: .85;
}
.pl.is-gone .pl-fill::after { opacity: 0; transition: opacity .2s; }

@media (prefers-reduced-motion: reduce) {
  /* no curtain and no parallax — it fades, as the original brief had it */
  .pl { transition: opacity .15s linear; }
  .pl.is-gone { transform: none; opacity: 0; }
  .pl .pl-stack, .pl.is-gone .pl-stack {
    transform: none; animation: none; transition: opacity .15s linear;
  }
  .pl-stack.is-settled .pl-count { animation: none; }
  .pl-strip { transition: none; }
  .pl-col { transition: width .3s ease-out; }
  .pl-col.is-new { opacity: 1; transform: none; }
  .pl-fill { transition: width .2s linear; }
  .pl-fill::after { display: none; }
}

/* ── the slow-connection note ─────────────────────────────────────────
   Only appears once the wait is long enough to be worth explaining, and
   it names the reason rather than saying "please wait". Out of flow, so
   showing it never moves the number. */
.pl-stack { position: relative; }
.pl-note {
  position: absolute; left: 50%; bottom: 100%;
  display: inline-flex; align-items: center; gap: 10px;
  margin: 0 0 32px; padding: 11px 20px 11px 16px;
  border-radius: 9999px;
  background: #FFFFFF;
  box-shadow: 0 2px 10px rgba(0, 0, 0, .06), 0 0 0 1px rgba(0, 0, 0, .04);
  font-family: 'Host Grotesk', 'Host-Grotesk', ui-sans-serif, system-ui, sans-serif;
  font-size: 17px; font-weight: 500; line-height: 1; white-space: nowrap;
  color: #1F1F1F;
  transform: translate(-50%, 6px); opacity: 0;
  transition: opacity .3s ease, transform .3s cubic-bezier(.22, 1, .36, 1);
}
.pl-note[hidden] { display: none; }
.pl-note.is-on { opacity: 1; transform: translate(-50%, 0); }
.pl-note i {
  width: 9px; height: 9px; border-radius: 50%;
  background: #B1060F; flex: none;
}
@media (max-width: 600px) {
  .pl-note { font-size: 15px; padding: 9px 16px 9px 13px; margin-bottom: 24px; }
}
@media (prefers-reduced-motion: reduce) {
  .pl-note { transition: opacity .2s ease; transform: translate(-50%, 0); }
}

/* ══ ATLAS SKIN — the Services section ═════════════════════════════════
   A sibling to Nova, deliberately not a recolour of it. Nova is the
   Academic Projects page: warm near-black, amber gold, Clash Display, a
   starfield, and a serpentine roadmap. Atlas has to read as the same house
   and a different room, so every one of those axes moves:

     ground     cool graphite, blue-shifted, against Nova's warm neutral
     accent     the brand red, brightened for a dark ground, against gold
     texture    a blueprint rule grid, against a starfield
     roadmap    a vertical spine with alternating branches, against an S-curve
     cards      corner brackets and a border that traces on hover, against
                a plain bento
     type       Host Grotesk at scale, against Clash Display

   TYPE NOTE. The shipped Host Grotesk woff2 is a single static instance —
   every weight from 300 to 800 measures identically — so this skin cannot
   build hierarchy out of weight the way Nova does. It uses scale, the
   accent and letter-spacing instead. That is a constraint honestly worked
   around, not a preference; swap in real weighted files and the whole
   thing gets better for free. */
:root[data-skin="atlas"] {
  --at-bg:      #0A0A0A;   /* neutral black — no tint in any ground */
  --at-panel:   #141414;
  --at-line:    rgba(255, 255, 255, .07);
  --at-line-2:  rgba(255, 255, 255, .14);
  /* Two reds, and the rule is contrast, not taste.

     --at-red is for marks and for text on the dark ground: at 5.50:1 it
     passes AA there. It does NOT pass behind white text — white on #FF3B4E
     is 3.51:1 — so every solid fill carrying a white label uses --at-fill
     instead, which is 5.41:1. Using the bright one for buttons is the
     mistake this pair exists to prevent. */
  --at-red:     #FF3B4E;   /* marks, rules, text on dark      5.50:1 on ground */
  --at-red-2:   #FF6B7A;   /* hover for the above */
  --at-fill:    #D01B2E;   /* solid fills under white text    5.41:1 with #fff */
  --at-fill-2:  #E52338;   /* hover for the above             4.54:1 with #fff */
  --at-ink:     #F4F4F4;
  --at-w70:     rgba(244, 244, 244, .84);
  --at-w50:     rgba(244, 244, 244, .74);
  --sh: 0, 0, 0;
  --at-display: 'Host Grotesk', 'Instrument Sans', ui-sans-serif, system-ui, sans-serif;
  --at-body:    'Instrument Sans', ui-sans-serif, system-ui, sans-serif;

  /* ── the full shared token set ───────────────────────────────────
     The first cut of this block flipped nine tokens and left the rest at
     their light-theme values. That is not a partial job with a partial
     cost: --pr-ink stayed #212121 and painted the FAQ heading near-black on
     graphite at 1.2:1, --faint stayed #5d554e and made the cross-link rail
     unreadable, and --fq-card was never set at all. Nova's block carries a
     comment warning about exactly this. Mirrored properly here.

     Contrast figures are against --at-bg #0C0E13. */
  --bg: var(--at-bg);
  --surface: var(--at-panel);
  --surface-2: #141414;
  --ink: var(--at-ink);              /* 17.7:1 */
  --muted: var(--at-w70);            /* 11.4:1 — body copy */
  --faint: rgba(244, 244, 244, .54); /*  5.3:1 — captions, rail descriptions */
  --line: var(--at-line);
  --line-soft: rgba(255, 255, 255, .05);
  --line-strong: var(--at-line-2);

  /* --brand is read as TEXT in more places than it is read as a fill
     (.kicker, .mm-all, link hovers), so it takes the bright red at 5.5:1.
     Every solid fill under a white label sets --at-fill explicitly. */
  --brand: var(--at-red);
  --brand-2: var(--at-red-2);
  --brand-soft: rgba(255, 255, 255, .06);
  --brand-line: rgba(255, 59, 78, .42);
  /* Dark, not white. --brand is the BRIGHT signal on this skin, and a
     dozen shared components pair `background: var(--brand)` with
     `color: var(--on-brand)` — the topic chips, the mega-menu CTA, the
     comparison table. White on #FF3B4E red is under 2:1 and shipped that
     way; near-black on it is 5.6:1. Nova already carries this and
     says so in its own block. Buttons that must stay white-on-fill set
     the fill token explicitly and are unaffected. */
  --on-brand: #0A0A0A;

  --grid: rgba(255, 255, 255, .05);
  --grid-hi: rgba(255, 255, 255, .14);
  --dot: rgba(255, 255, 255, .14);
  --hover: rgba(255, 255, 255, .05);
  --accent-a: var(--at-red); --accent-b: var(--at-red-2);

  /* the second family — the FAQ, the process rail and the planner read it */
  --pr-ground: var(--at-bg);
  --pr-card:   var(--at-panel);
  --pr-chip:   #1A1A1A;
  --pr-ink:    var(--at-ink);
  --pr-ink-2:  rgba(244, 244, 244, .88);
  --pr-ink-3:  rgba(244, 244, 244, .74);
  --pr-body:   var(--at-w70);
  --pr-rail:   rgba(255, 255, 255, .11);
  --pr-dash:   rgba(255, 255, 255, .20);
  --pr-wire:   var(--at-red);
  --pr-bevel:  inset 0 1px 0 rgba(255, 255, 255, .05);
  --pr-shadow: 0 4px 14px rgba(0, 0, 0, .5);
  --pr-shadow-s: 0 1px 2px rgba(0, 0, 0, .4);

  --fq-card: var(--at-panel);
  --fq-edge: var(--at-line);

  --hdr-glass: rgba(10, 10, 10, .92);
  --hdr-glass-thin: rgba(10, 10, 10, .62);
  --logo: var(--at-ink);
  color-scheme: dark;
}

/* the ground: a drafting rule, not a starfield. Two grids at different
   pitches so it reads as paper rather than as a table */
:root[data-skin="atlas"] body {
  background:
    linear-gradient(rgba(255,255,255,.022) 1px, transparent 1px) 0 0 / 100% 96px,
    linear-gradient(90deg, rgba(255,255,255,.022) 1px, transparent 1px) 0 0 / 96px 100%,
    linear-gradient(rgba(255,255,255,.05) 1px, transparent 1px) 0 0 / 100% 480px,
    linear-gradient(90deg, rgba(255,255,255,.05) 1px, transparent 1px) 0 0 / 480px 100%,
    var(--at-bg);
  color: var(--at-ink);
}
/* :not(.site-header) is load-bearing, and this is the second time the site
   has learned it. Without the exclusion the header is given a stacking
   context at z-index 1, which traps its dropdown panels (z-index 40) at
   that level — and .at-hero, a later sibling also at z-index 1, then paints
   straight over the open menu. The panel renders at full size the whole
   time; it is simply behind the page. Nova carries the same exclusion for
   the same reason. */
:root[data-skin="atlas"] body > *:not(.site-header):not(.ap-progress) { position: relative; z-index: 1; }
:root[data-skin="atlas"] .site-header { background: var(--at-bg) !important; }

/* ── hero ─────────────────────────────────────────────────────────────
   Same split geometry as Nova's — it is the founder's kit and it works —
   but dressed differently: a bracketed tag instead of a comet pill, and
   the headline's second line carries a rule rather than a colour change. */
:root[data-skin="atlas"] .at-hero {
  position: relative; isolation: isolate; overflow: hidden;
  padding: 0 0 clamp(40px, 5vw, 72px); background: transparent;
}
:root[data-skin="atlas"] .at-crumbs { padding: 1.6vw 7vw 0; margin: 0; }
:root[data-skin="atlas"] .at-crumbs ol {
  list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; gap: .55vw;
}
:root[data-skin="atlas"] .at-crumbs li + li::before { content: "/"; margin-right: .55vw; color: var(--at-w50); }
:root[data-skin="atlas"] .at-crumbs :is(a, span) {
  font-family: var(--at-body) !important; font-size: .97vw !important;
  letter-spacing: .02em !important; text-decoration: none;
}
:root[data-skin="atlas"] .at-crumbs a { color: var(--at-w50) !important; }
:root[data-skin="atlas"] .at-crumbs a:hover { color: var(--at-red-2) !important; }
:root[data-skin="atlas"] .at-crumbs span { color: var(--at-w70) !important; }

:root[data-skin="atlas"] .at-hero-split {
  display: flex; align-items: center; justify-content: space-between;
  padding: 0 7vw; min-height: 42vw; gap: 3vw;
}
:root[data-skin="atlas"] .at-hero-l { width: 50%; }
:root[data-skin="atlas"] .at-hero-r { width: 50%; display: flex; justify-content: center; }
:root[data-skin="atlas"] .at-illus { width: 40vw; max-width: 100%; }
:root[data-skin="atlas"] .at-illus img { width: 100%; height: auto; display: block; }

/* the tag: drafting brackets, not a pill with a comet in it */
:root[data-skin="atlas"] .at-tag {
  display: inline-flex; align-items: center; gap: .7vw;
  padding: .7vw 1.2vw; position: relative;
  font-family: var(--at-body) !important; font-size: 1.04vw !important;
  letter-spacing: .06em !important; text-transform: uppercase;
  color: var(--at-w70) !important;
}
:root[data-skin="atlas"] .at-tag::before,
:root[data-skin="atlas"] .at-tag::after {
  content: ""; position: absolute; width: .8vw; height: .8vw;
  border: 1px solid var(--at-red); pointer-events: none;
}
:root[data-skin="atlas"] .at-tag::before { left: 0; top: 0; border-right: 0; border-bottom: 0; }
:root[data-skin="atlas"] .at-tag::after  { right: 0; bottom: 0; border-left: 0; border-top: 0; }
:root[data-skin="atlas"] .at-tag i {
  width: .5vw; height: .5vw; background: var(--at-red); flex: none;
  animation: at-pulse 2.4s ease-in-out infinite;
}
@keyframes at-pulse { 0%,100% { opacity: 1 } 50% { opacity: .25 } }

:root[data-skin="atlas"] .at-h1 {
  font-family: var(--at-display) !important;
  font-size: 3.6vw !important; line-height: 1.05 !important;
  letter-spacing: -.03em !important; color: var(--at-ink) !important;
  margin: 1.8vw 0 0; text-wrap: balance;
}
/* the second line is marked by a rule, not by a colour swap — Nova already
   owns "the gold half" as a device */
:root[data-skin="atlas"] .at-h1 em {
  display: block; font-style: normal; font-family: inherit; color: inherit;
  position: relative; padding-left: 2.6vw;
}
:root[data-skin="atlas"] .at-h1 em::before {
  content: ""; position: absolute; left: 0; top: .52em;
  width: 1.9vw; height: 2px; background: var(--at-red);
}
:root[data-skin="atlas"] .at-sub {
  font-family: var(--at-body) !important; font-size: 1.32vw !important;
  line-height: 1.55 !important; color: var(--at-w70) !important;
  margin: 1.4vw 0 0; max-width: 40ch;
}
:root[data-skin="atlas"] .at-cta { margin: 2.4vw 0 0; display: flex; gap: 1vw; flex-wrap: wrap; }
:root[data-skin="atlas"] .at-btn {
  display: inline-flex; align-items: center; gap: .7vw;
  padding: 1vw 2.2vw; border-radius: 2px;
  background: var(--at-fill); color: #fff !important; border: 0;
  font-family: var(--at-body) !important; font-size: 1.11vw !important;
  text-decoration: none; transition: background .2s var(--ease), transform .2s var(--ease);
}
:root[data-skin="atlas"] .at-btn:hover { background: var(--at-fill-2); transform: translateY(-1px); }
:root[data-skin="atlas"] .at-btn-ghost {
  background: none; color: var(--at-ink) !important;
  box-shadow: inset 0 0 0 1px var(--at-line-2);
}
:root[data-skin="atlas"] .at-btn-ghost:hover { background: rgba(255,255,255,.04); box-shadow: inset 0 0 0 1px var(--at-red); }

@media (max-width: 900px) {
  :root[data-skin="atlas"] .at-hero-split { flex-direction: column-reverse; padding: 0 6.154vw; min-height: 0; }
  :root[data-skin="atlas"] .at-hero-l, :root[data-skin="atlas"] .at-hero-r { width: 100%; }
  :root[data-skin="atlas"] .at-illus { width: 82vw; }
  :root[data-skin="atlas"] .at-h1 { font-size: 8.6vw !important; }
  :root[data-skin="atlas"] .at-h1 em { padding-left: 6vw; }
  :root[data-skin="atlas"] .at-h1 em::before { width: 4.4vw; }
  :root[data-skin="atlas"] .at-sub { font-size: 4.1vw !important; max-width: none; }
  :root[data-skin="atlas"] .at-tag { font-size: 3.2vw !important; padding: 2.4vw 4vw; gap: 2.4vw; }
  :root[data-skin="atlas"] .at-tag::before, :root[data-skin="atlas"] .at-tag::after { width: 2.6vw; height: 2.6vw; }
  :root[data-skin="atlas"] .at-tag i { width: 1.8vw; height: 1.8vw; }
  :root[data-skin="atlas"] .at-btn { font-size: 3.8vw !important; padding: 3.4vw 6vw; }
  :root[data-skin="atlas"] .at-crumbs { padding: 3vw 6.154vw 0; }
  :root[data-skin="atlas"] .at-crumbs :is(a, span) { font-size: 3.2vw !important; }
}

/* ── the spine ────────────────────────────────────────────────────────
   Atlas's roadmap. Nova draws a serpentine SVG path and pins markers to it
   in vw; this is a straight vertical rule with nodes branching left and
   right off it. Structurally different on purpose, and it needs no SVG,
   no sampled points and no per-marker geometry — the whole thing is a
   flex column, so it reflows to one column on mobile without a second
   drawing the way Nova needs. */
:root[data-skin="atlas"] .at-spine {
  position: relative; margin: clamp(56px, 8vw, 120px) auto 0;
  max-width: 76vw; padding: 0 0 2vw;
}
:root[data-skin="atlas"] .at-spine-head { text-align: center; margin-bottom: clamp(36px, 5vw, 72px); }
:root[data-skin="atlas"] .at-spine::before {
  content: ""; position: absolute; left: 50%; top: 0; bottom: 0;
  width: 1px; background: var(--at-line-2); transform: translateX(-50%);
}
/* the lit portion, driven by --p (0-1) from academic-projects.js */
:root[data-skin="atlas"] .at-spine::after {
  content: ""; position: absolute; left: 50%; top: 0;
  width: 1px; height: calc(var(--p, 0) * 100%);
  background: linear-gradient(180deg, var(--at-red), var(--at-red-2));
  box-shadow: 0 0 12px rgba(255, 59, 78, .55);
  transform: translateX(-50%); transition: height .25s linear;
}
:root[data-skin="atlas"] .at-steps { list-style: none; margin: 0; padding: 0; display: grid; gap: clamp(28px, 3.6vw, 56px); }
:root[data-skin="atlas"] .at-step {
  position: relative; display: grid; align-items: center;
  grid-template-columns: 1fr 2.4vw 1fr; gap: 0;
}
:root[data-skin="atlas"] .at-step-card {
  grid-column: 1; text-align: right; padding-right: 2.4vw;
  opacity: .34; transition: opacity .45s var(--ease), transform .45s var(--ease);
  transform: translateY(10px);
}
:root[data-skin="atlas"] .at-step:nth-child(even) .at-step-card {
  grid-column: 3; text-align: left; padding: 0 0 0 2.4vw;
}
:root[data-skin="atlas"] .at-step.is-on .at-step-card { opacity: 1; transform: none; }
:root[data-skin="atlas"] .at-step-n {
  font-family: var(--at-body) !important; font-size: .97vw !important;
  letter-spacing: .16em !important; color: var(--at-red) !important;
  display: block; margin-bottom: .4vw;
}
:root[data-skin="atlas"] .at-step-t {
  font-family: var(--at-display) !important; font-size: 1.6vw !important;
  letter-spacing: -.02em !important; color: var(--at-ink) !important; margin: 0;
}
:root[data-skin="atlas"] .at-step-d {
  font-family: var(--at-body) !important; font-size: 1.11vw !important;
  line-height: 1.55 !important; color: var(--at-w50) !important; margin: .5vw 0 0;
}
/* the node sits in the middle column, on the spine */
:root[data-skin="atlas"] .at-step-node {
  grid-column: 2; justify-self: center;
  width: 1.05vw; height: 1.05vw; border-radius: 50%;
  background: var(--at-bg); box-shadow: 0 0 0 1px var(--at-line-2);
  transition: box-shadow .35s var(--ease), background .35s var(--ease);
}
:root[data-skin="atlas"] .at-step.is-on .at-step-node {
  background: var(--at-red);
  box-shadow: 0 0 0 1px var(--at-red), 0 0 0 .5vw rgba(255, 59, 78, .16), 0 0 16px rgba(255,59,78,.6);
}

@media (max-width: 900px) {
  :root[data-skin="atlas"] .at-spine { max-width: none; padding: 0 6.154vw 6vw; }
  :root[data-skin="atlas"] .at-spine::before,
  :root[data-skin="atlas"] .at-spine::after { left: 6.154vw; transform: none; }
  :root[data-skin="atlas"] .at-step { grid-template-columns: 6vw 1fr; }
  :root[data-skin="atlas"] .at-step-node { grid-column: 1; justify-self: start; width: 3vw; height: 3vw; }
  :root[data-skin="atlas"] .at-step-card,
  :root[data-skin="atlas"] .at-step:nth-child(even) .at-step-card {
    grid-column: 2; text-align: left; padding: 0 0 0 2vw;
  }
  :root[data-skin="atlas"] .at-step-n { font-size: 3.2vw !important; }
  :root[data-skin="atlas"] .at-step-t { font-size: 5.4vw !important; }
  :root[data-skin="atlas"] .at-step-d { font-size: 3.9vw !important; }
}
@media (prefers-reduced-motion: reduce) {
  :root[data-skin="atlas"] .at-step-card { transition: none; transform: none; opacity: 1; }
  :root[data-skin="atlas"] .at-spine::after { transition: none; }
  :root[data-skin="atlas"] .at-tag i { animation: none; }
}

/* ── the cards ────────────────────────────────────────────────────────
   Nova's bento is a plain surface that reveals on scroll. Atlas's card is a
   drafting frame: four corner brackets always visible, and a border that
   traces around the card on hover. The trace is two pseudo-elements, each
   scaling on one axis with a delay, so it reads as a line drawn round the
   box rather than a border switching on. */
:root[data-skin="atlas"] .at-grid {
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 1.6vw; margin-top: clamp(28px, 3.6vw, 52px);
}
:root[data-skin="atlas"] .at-card {
  position: relative; padding: 2.2vw; background: var(--at-panel);
  border: 1px solid var(--at-line);
  transition: background .3s var(--ease), transform .3s var(--ease);
}
:root[data-skin="atlas"] .at-card-wide { grid-column: span 2; }
:root[data-skin="atlas"] .at-card:hover { background: #1A1A1A; transform: translateY(-2px); }

/* corner brackets — the drafting motif, always on */
:root[data-skin="atlas"] .at-card > .at-br {
  position: absolute; width: 12px; height: 12px; pointer-events: none;
  border: 1px solid var(--at-line-2); transition: border-color .3s var(--ease);
}
:root[data-skin="atlas"] .at-br-tl { left: -1px; top: -1px; border-right: 0; border-bottom: 0; }
:root[data-skin="atlas"] .at-br-tr { right: -1px; top: -1px; border-left: 0; border-bottom: 0; }
:root[data-skin="atlas"] .at-br-bl { left: -1px; bottom: -1px; border-right: 0; border-top: 0; }
:root[data-skin="atlas"] .at-br-br { right: -1px; bottom: -1px; border-left: 0; border-top: 0; }
:root[data-skin="atlas"] .at-card:hover > .at-br { border-color: var(--at-red); }

/* the trace: top+right drawn first, then bottom+left, so the line travels */
:root[data-skin="atlas"] .at-card::before,
:root[data-skin="atlas"] .at-card::after {
  content: ""; position: absolute; pointer-events: none;
  background: var(--at-red); opacity: 0;
  transition: transform .34s var(--ease), opacity .2s;
}
:root[data-skin="atlas"] .at-card::before {
  left: -1px; top: -1px; height: 1px; width: calc(100% + 2px);
  transform: scaleX(0); transform-origin: left;
}
:root[data-skin="atlas"] .at-card::after {
  left: -1px; bottom: -1px; height: 1px; width: calc(100% + 2px);
  transform: scaleX(0); transform-origin: right;
}
:root[data-skin="atlas"] .at-card:hover::before,
:root[data-skin="atlas"] .at-card:hover::after { transform: scaleX(1); opacity: 1; }
:root[data-skin="atlas"] .at-card:hover::after { transition-delay: .1s; }

:root[data-skin="atlas"] .at-card-ic {
  display: block; width: 2vw; height: 2vw; color: var(--at-red); margin-bottom: 1.2vw;
}
:root[data-skin="atlas"] .at-card-ic svg { width: 100%; height: 100%; }
:root[data-skin="atlas"] .at-card b {
  display: block; font-family: var(--at-display) !important;
  font-size: 1.32vw !important; letter-spacing: -.015em !important;
  color: var(--at-ink) !important; margin-bottom: .6vw;
}
:root[data-skin="atlas"] .at-card p {
  font-family: var(--at-body) !important; font-size: 1.11vw !important;
  line-height: 1.6 !important; color: var(--at-w50) !important; margin: 0;
}

/* ── section furniture ────────────────────────────────────────────── */
:root[data-skin="atlas"] .at-sec { margin-top: clamp(56px, 8vw, 120px); padding: 0 7vw; }
:root[data-skin="atlas"] .at-head { max-width: 52rem; }
:root[data-skin="atlas"] .at-k {
  font-family: var(--at-body) !important; font-size: .97vw !important;
  letter-spacing: .16em !important; text-transform: uppercase;
  color: var(--at-red) !important; margin: 0 0 .8vw;
}
:root[data-skin="atlas"] .at-h {
  font-family: var(--at-display) !important; font-size: 2.4vw !important;
  line-height: 1.15 !important; letter-spacing: -.025em !important;
  color: var(--at-ink) !important; margin: 0; text-wrap: balance;
}
:root[data-skin="atlas"] .at-d {
  font-family: var(--at-body) !important; font-size: 1.25vw !important;
  line-height: 1.6 !important; color: var(--at-w70) !important; margin: 1vw 0 0; max-width: 62ch;
}

/* ── the ledger: a two-column spec table, Atlas's answer to Nova's
   stream selector. A table rather than tabs because these pages are
   specifications, and a reader comparing rows should not have to click. */
:root[data-skin="atlas"] .at-ledger { margin-top: clamp(28px, 3.4vw, 48px); border-top: 1px solid var(--at-line-2); }
:root[data-skin="atlas"] .at-row {
  display: grid; grid-template-columns: minmax(0, 15rem) minmax(0, 1fr);
  gap: 1vw 2.4vw; padding: 1.6vw 0; border-bottom: 1px solid var(--at-line);
  transition: background .25s var(--ease);
}
:root[data-skin="atlas"] .at-row:hover { background: rgba(255,255,255,.02); }
:root[data-skin="atlas"] .at-row dt {
  font-family: var(--at-display) !important; font-size: 1.18vw !important;
  letter-spacing: -.01em !important; color: var(--at-ink) !important;
}
:root[data-skin="atlas"] .at-row dd {
  margin: 0; font-family: var(--at-body) !important; font-size: 1.11vw !important;
  line-height: 1.6 !important; color: var(--at-w50) !important;
}

:root[data-skin="atlas"] .at-note {
  margin: clamp(48px, 6vw, 88px) 7vw 0; padding: 1.8vw 2.2vw;
  border-left: 2px solid var(--at-red); background: rgba(255, 255, 255, .03);
}
:root[data-skin="atlas"] .at-note p {
  margin: 0; font-family: var(--at-body) !important; font-size: 1.11vw !important;
  line-height: 1.65 !important; color: var(--at-w70) !important;
}
:root[data-skin="atlas"] .at-note b { color: var(--at-ink) !important; }

@media (max-width: 900px) {
  :root[data-skin="atlas"] .at-grid { grid-template-columns: 1fr; gap: 4vw; }
  :root[data-skin="atlas"] .at-card-wide { grid-column: span 1; }
  :root[data-skin="atlas"] .at-card { padding: 6vw; }
  :root[data-skin="atlas"] .at-card-ic { width: 7vw; height: 7vw; margin-bottom: 4vw; }
  :root[data-skin="atlas"] .at-card b { font-size: 5vw !important; }
  :root[data-skin="atlas"] .at-card p, :root[data-skin="atlas"] .at-row dd { font-size: 3.9vw !important; }
  :root[data-skin="atlas"] .at-sec { padding: 0 6.154vw; }
  :root[data-skin="atlas"] .at-k { font-size: 3.2vw !important; }
  :root[data-skin="atlas"] .at-h { font-size: 7vw !important; }
  :root[data-skin="atlas"] .at-d { font-size: 4.1vw !important; }
  :root[data-skin="atlas"] .at-row { grid-template-columns: 1fr; padding: 5vw 0; }
  :root[data-skin="atlas"] .at-row dt { font-size: 4.4vw !important; }
  :root[data-skin="atlas"] .at-note { margin-inline: 6.154vw; padding: 5vw; }
  :root[data-skin="atlas"] .at-note p { font-size: 3.9vw !important; }
}
@media (prefers-reduced-motion: reduce) {
  :root[data-skin="atlas"] .at-card,
  :root[data-skin="atlas"] .at-card::before,
  :root[data-skin="atlas"] .at-card::after,
  :root[data-skin="atlas"] .at-card > .at-br { transition: none; }
  :root[data-skin="atlas"] .at-card:hover { transform: none; }
}

/* ── the header on Atlas ──────────────────────────────────────────────
   The header writes literal light-theme inks rather than reading tokens,
   so on a dark ground the wordmark came through brand red and the nav
   links were near-invisible. Nova carries the same patch for the same
   reason; this is Atlas's copy of it.

   The :not(.on-hero):not(.past-hero) prefix is there purely for weight —
   the rules being overridden are :root:not([data-theme="dark"])
   .site-header:not(.on-hero) X at (0,5,0), and a plainer selector loses on
   class count no matter that it comes later in the file. */
:root[data-skin="atlas"] .site-header {
  border-bottom-color: var(--at-line) !important;
  -webkit-backdrop-filter: none; backdrop-filter: none;
}
:root[data-skin="atlas"] .site-header:not(.on-hero):not(.past-hero) :is(
  .menu-trigger, .nav-links > a, .mp-plain > a, .nav-signin, .burger
) { color: var(--at-ink); }
:root[data-skin="atlas"] .site-header:not(.on-hero):not(.past-hero)
  :is(.menu-trigger, .nav-links > a):hover { color: var(--at-red-2); }
:root[data-skin="atlas"] .site-header .brand-mark,
:root[data-skin="atlas"] .site-header:not(.on-hero):not(.past-hero) .brand-mark {
  background: var(--at-ink) !important; --logo-ink: var(--at-ink);
}
:root[data-skin="atlas"] .site-header:not(.on-hero):not(.past-hero) .nav-signin {
  border-color: var(--at-line-2);
}
:root[data-skin="atlas"] .site-header:not(.on-hero):not(.past-hero) .nav-signin:hover {
  border-color: var(--at-red); color: var(--at-red-2);
}
:root[data-skin="atlas"] .site-header .nav-start {
  background: var(--at-fill) !important; color: #fff !important; border: 0 !important;
}
:root[data-skin="atlas"] .site-header .nav-start:hover { background: var(--at-fill-2) !important; }

/* .btn-primary paints `background: var(--ink)`, which is a near-white ink on
   this skin, under a `var(--on-brand)` white label: 1.09:1, an invisible
   button. The drawer's Get Started is the only .btn-primary these pages
   carry, so it was the only casualty, but it is the drawer's one call to
   action. --at-fill is the token the two-red note above is written for —
   every solid fill under a white label takes it. Nova carries the same
   correction for the same reason. */
:root[data-skin="atlas"] .btn-primary { background: var(--at-fill); color: #fff; }
:root[data-skin="atlas"] .btn-primary:hover { background: var(--at-fill-2); color: #fff; }
:root[data-skin="atlas"] .site-header:not(.on-hero):not(.past-hero) .burger .ab {
  background: rgba(255, 255, 255, .06);
}
:root[data-skin="atlas"] .site-header:not(.on-hero):not(.past-hero) :is(.burger .bg-tx, .burger .bg-ic) {
  background: none; color: var(--at-ink);
}
:root[data-skin="atlas"] .site-header:not(.on-hero):not(.past-hero) .burger svg { color: var(--at-ink); }

/* the dropdown and account panels are their own surface */
:root[data-skin="atlas"] :is(.menu-panel, .menu-panel.mega) {
  background: #1A1A1A; border-color: var(--at-line-2); color: var(--at-ink);
}
:root[data-skin="atlas"] :is(.mm-row b, .mm-col h3) { color: var(--at-ink); }
:root[data-skin="atlas"] .mm-row em { color: var(--at-w50); }
:root[data-skin="atlas"] .mm-row:hover { background: rgba(255, 255, 255, .04); }
:root[data-skin="atlas"] .mm-ic { background: rgba(255, 255, 255, .05); }
:root[data-skin="atlas"] .menu-panel a.mm-all { color: var(--at-red-2); }
:root[data-skin="atlas"] .menu-panel a.mm-all span { color: var(--at-red-2); }

/* the footer sits on the same ground */
:root[data-skin="atlas"] .foot { background: #050505; }
:root[data-skin="atlas"] .foot-glow { opacity: .2; }

/* Atlas keeps the wordmark on mobile too.

   The site swaps to the icon-only mark below 600px. Nova reverts that (its
   reference keeps the full wordmark), so without the same revert here the
   logo changed shape as a visitor moved between two sibling dark sections —
   wordmark in Project Training, icon in Services. Whichever is right, it
   has to be the same on both. */
@media (max-width: 600px) {
  :root[data-skin="atlas"] .site-header .brand-mark {
    width: 25.1vw; height: 5.13vw; background: var(--at-ink);
    -webkit-mask-image: url('assets/wordmark.webp'); mask-image: url('assets/wordmark.webp');
    -webkit-mask-size: contain; mask-size: contain;
    -webkit-mask-repeat: no-repeat; mask-repeat: no-repeat;
    -webkit-mask-position: left center; mask-position: left center;
  }
}

/* ── the FAQ on Atlas ─────────────────────────────────────────────────
   :is(#faq, #cfq) sets --fq-card and --fq-edge on the SECTION, not on
   :root. A custom property set closer to the element always wins, so
   flipping them in the skin's :root block did nothing at all and the cards
   stayed white with near-white text on them. They are overridden at the
   same level here, which is the only level that works.

   Dressed in Atlas's idiom rather than Nova's: square corners and a
   bracketed toggle instead of rounded cards and a gold ring. */
:root[data-skin="atlas"] :is(#faq, #cfq) {
  --fq-card: var(--at-panel);
  --fq-edge: var(--at-line);
  color: var(--at-ink);
}
:root[data-skin="atlas"] #cfq .fq-head { text-align: left; align-items: flex-start; }
:root[data-skin="atlas"] #cfq .fq-pill {
  background: none; border: 1px solid var(--at-line-2); border-radius: 0;
  color: var(--at-red) !important; letter-spacing: .16em !important; text-transform: uppercase;
}
:root[data-skin="atlas"] #cfq h2 {
  font-family: var(--at-display) !important; letter-spacing: -.025em !important;
  color: var(--at-ink) !important;
}
:root[data-skin="atlas"] #cfq h2 em {
  font-family: inherit !important; font-style: normal !important;
  color: var(--at-red) !important; background: none !important;
  -webkit-text-fill-color: var(--at-red);
}
:root[data-skin="atlas"] #cfq .lede { color: var(--at-w70) !important; }
:root[data-skin="atlas"] #cfq .fq {
  border-radius: 0; box-shadow: none;
  border-color: var(--at-line);
}
:root[data-skin="atlas"] #cfq .fq:hover { transform: none; border-color: var(--at-line-2); }
:root[data-skin="atlas"] #cfq .fq[data-open="true"] { border-color: rgba(255, 59, 78, .4); }
:root[data-skin="atlas"] #cfq .fq-q { color: var(--at-ink) !important; }
:root[data-skin="atlas"] #cfq .fq-p p { color: var(--at-w50) !important; }
:root[data-skin="atlas"] #cfq .fq-meta { color: var(--at-red) !important; }
/* the toggle: a square, not a circle */
:root[data-skin="atlas"] #cfq .fq-ic {
  border-radius: 0; border-color: var(--at-line-2);
}
:root[data-skin="atlas"] #cfq .fq:hover .fq-ic { border-color: var(--at-red); }
:root[data-skin="atlas"] #cfq .fq-ic::before,
:root[data-skin="atlas"] #cfq .fq-ic::after { background: var(--at-red); }
:root[data-skin="atlas"] #cfq .fq-all a { color: var(--at-red) !important; }
:root[data-skin="atlas"] #cfq .fq-glow { display: none; }

/* ── the closing CTA and the cross-link rail ────────────────────────── */
:root[data-skin="atlas"] .abx-cta {
  background: var(--at-panel); border: 1px solid var(--at-line); border-radius: 0;
}
:root[data-skin="atlas"] .abx-cta .lede { color: var(--at-w70) !important; }
:root[data-skin="atlas"] .svc-more { border-top-color: var(--at-line-2); }
:root[data-skin="atlas"] .svc-more .kicker { color: var(--at-red) !important; }
/* atlas squared these off and repainted them for the old flat list. The
   card is now the design on every skin, so only the hover accent stays
   — leaving the rest would give the services pages square corners and
   nothing else, which is how a skin override outlives its design. */
:root[data-skin="atlas"] .svc-more a:hover { border-color: var(--at-red); }
:root[data-skin="atlas"] .abx-note { border-left-color: var(--at-red); background: rgba(255, 59, 78, .05); }

/* the hub grid inherits colour from the token set, but not geometry — the
   shared card is a 14px-radius pill while every other surface in this skin
   is square with brackets */
:root[data-skin="atlas"] .abx-card {
  border-radius: 0; background: var(--at-panel); border-color: var(--at-line);
}
:root[data-skin="atlas"] .abx-card:hover { border-color: var(--at-red); }
:root[data-skin="atlas"] .abx-go { color: var(--at-red); }
:root[data-skin="atlas"] .abx-chips li { border-radius: 0; }

/* ══ LAB SKIN — the Project Training section ═══════════════════════════

   The third page skin, and the third reading of the same content object.
   Nova is a roadmap, Atlas is a specification, Lab is an instrument: a
   training page claims a reader will be able to do something afterwards,
   and the honest way to show a claim like that is the way a lab shows a
   measurement — a channel, a trace, a reading, and a label naming what was
   measured.

   Everything that moves is an existing behaviour contract, so this skin
   adds no JavaScript at all. The reveals are CSS scroll-driven animations,
   which cost nothing and simply do not run where they are unsupported.

   Alignment is the other half of the brief, and it is one decision: every
   section hangs off a single left axis with its index on the rail. Headings,
   kickers, body and cards all start on that axis, so the eye returns to the
   same x on every scroll step instead of re-finding the column each time. */

:root[data-skin="lab"] {
  --lb-bg:      #0A0A0A;   /* neutral black — no tint in any ground */
  --lb-panel:   #141414;
  --lb-panel-2: #1A1A1A;
  --lb-line:    rgba(255, 255, 255, .11);
  --lb-line-2:  rgba(255, 255, 255, .22);

  /* Two signals, and the rule is contrast, not taste — the same pair Atlas
     needed and for the same reason.

     --lb-sig is for traces, marks and text on the dark ground, where it
     reads at 12.2:1. It does NOT pass behind white text, so every solid
     fill carrying a white label takes --lb-fill instead, at 5.1:1. Using
     the bright one on a button is the mistake this pair exists to prevent.

     --lb-amber is the second channel: annotations, the B side of the
     differential, and the one warning. It is never a fill. */
  --lb-sig:     #3FE3C0;   /* traces, marks, text on dark    12.2:1 on ground */
  --lb-sig-2:   #6BF0D6;   /* hover for the above */
  --lb-fill:    #0C7C68;   /* solid fills under white text    5.1:1 with #fff */
  --lb-fill-2:  #0F9A81;   /* hover for the above */
  --lb-amber:   #FFB454;   /* channel B, annotations         11.1:1 on ground */

  --lb-ink:     #F4F4F4;
  --lb-w70:     rgba(244, 244, 244, .84);
  --lb-w52:     rgba(244, 244, 244, .74);

  /* The readout face. A system stack, so it costs no request and no layout
     shift — and tabular figures, because every number on this skin sits in
     a column that has to line up. */
  --sh: 0, 0, 0;
  --lb-mono: ui-monospace, SFMono-Regular, 'SF Mono', Menlo, Consolas, monospace;

  /* ── the full shared token set ───────────────────────────────────
     Mirrored completely, not partially. The first cut of the Atlas block
     flipped nine tokens and left the rest at their light-theme values,
     which painted the FAQ heading at 1.2:1 and left the cross-link rail
     unreadable. Nova's block carries a warning about it and Atlas repeated
     it anyway, so it is written out in full here.

     Contrast figures are against --lb-bg #060B14. */
  --bg: var(--lb-bg);
  --surface: var(--lb-panel);
  --surface-2: var(--lb-panel-2);
  --ink: var(--lb-ink);              /* 17.3:1 */
  --muted: var(--lb-w70);            /* 11.1:1 — body copy */
  --faint: var(--lb-w52);            /*  9.0:1 — captions, rail descriptions */
  --line: var(--lb-line);
  --line-soft: rgba(255, 255, 255, .06);
  --line-strong: var(--lb-line-2);

  /* --brand is read as text far more often than as a fill (.kicker,
     .mm-all, link hovers), so it takes the bright signal. Every solid fill
     under a white label sets --lb-fill explicitly. */
  --brand: var(--lb-sig);
  --brand-2: var(--lb-sig-2);
  --brand-soft: rgba(255, 255, 255, .06);
  --brand-line: rgba(63, 227, 192, .40);
  /* Dark, not white. --brand is the BRIGHT signal on this skin, and a
     dozen shared components pair `background: var(--brand)` with
     `color: var(--on-brand)` — the topic chips, the mega-menu CTA, the
     comparison table. White on #3FE3C0 teal is under 2:1 and shipped that
     way; near-black on it is 12.2:1. Nova already carries this and
     says so in its own block. Buttons that must stay white-on-fill set
     the fill token explicitly and are unaffected. */
  --on-brand: #0A0A0A;

  --grid: rgba(255, 255, 255, .05);
  --grid-hi: rgba(255, 255, 255, .13);
  --dot: rgba(255, 255, 255, .15);
  --hover: rgba(255, 255, 255, .06);
  --accent-a: var(--lb-sig); --accent-b: var(--lb-amber);

  /* the second family — the FAQ, the process rail and the planner read it */
  --pr-ground: var(--lb-bg);
  --pr-card:   var(--lb-panel);
  --pr-chip:   var(--lb-panel-2);
  --pr-ink:    var(--lb-ink);
  --pr-ink-2:  rgba(244, 244, 244, .88);
  --pr-ink-3:  rgba(244, 244, 244, .74);
  --pr-body:   var(--lb-w70);
  --pr-rail:   rgba(255, 255, 255, .13);
  --pr-dash:   rgba(255, 255, 255, .22);
  --pr-wire:   var(--lb-sig);
  --pr-bevel:  inset 0 1px 0 rgba(255, 255, 255, .06);
  --pr-shadow: 0 4px 14px rgba(0, 0, 0, .55);
  --pr-shadow-s: 0 1px 2px rgba(0, 0, 0, .45);

  --fq-card: var(--lb-panel);
  --fq-edge: var(--lb-line);

  --hdr-glass: rgba(10, 10, 10, .92);
  --hdr-glass-thin: rgba(10, 10, 10, .62);
  --logo: var(--lb-ink);
  color-scheme: dark;
}

/* ── the Lab components, on white ─────────────────────────────────────
   /support/pricing-and-payment/ runs the same Lab markup as the rest of
   that section but on a white ground, because someone comparing what
   things cost should not have to do it in the dark half of the site.

   It is a palette swap, not a second stylesheet. 84 Lab rules apply to
   that page and 80 of them read these tokens, so redefining the tokens
   moves the lot; only three hard-code a value that assumed black, and
   they are corrected below.

   The signal colour changes with the ground. Teal at 12.2:1 on #0A0A0A
   is 2.1:1 on white — it was chosen FOR the black. The brand red takes
   its place, and the amber, a highlight on black and invisible on white,
   drops to a tone that can be read. */
:root[data-skin="lab"][data-lab="light"] {
  --lb-bg:      #FFFFFF;
  --lb-panel:   #FFFFFF;
  --lb-panel-2: #F6F6F4;
  --lb-line:    rgba(26, 19, 16, .11);
  --lb-line-2:  rgba(26, 19, 16, .20);

  --lb-sig:     #AE0E20;
  --lb-sig-2:   #8A0007;
  --lb-fill:    #AE0E20;
  --lb-fill-2:  #8A0007;
  --lb-amber:   #8A5B0C;

  --lb-ink:     #1a1310;
  --lb-w70:     #1C1C1C;
  --lb-w52:     #262626;
  --sh: 26, 19, 16;

  --line-soft:   rgba(26, 19, 16, .07);
  --brand-soft:  rgba(174, 14, 32, .06);
  --brand-line:  rgba(174, 14, 32, .30);
  --on-brand:    #FFFFFF;
  --grid:        rgba(26, 19, 16, .05);
  --grid-hi:     rgba(26, 19, 16, .10);
  --dot:         rgba(26, 19, 16, .16);
  --hover:       rgba(26, 19, 16, .05);

  --pr-ink-2:    rgba(26, 19, 16, .88);
  --pr-ink-3:    rgba(26, 19, 16, .74);
  --pr-rail:     rgba(26, 19, 16, .14);
  --pr-dash:     rgba(26, 19, 16, .24);
  --pr-bevel:    inset 0 1px 0 rgba(255, 255, 255, .9);
  --pr-shadow:   0 4px 14px rgba(26, 19, 16, .10);
  --pr-shadow-s: 0 1px 2px rgba(26, 19, 16, .06);

  --hdr-glass:      rgba(255, 255, 255, .92);
  --hdr-glass-thin: rgba(255, 255, 255, .62);

  color-scheme: light;
}

/* the three that assumed a black ground */
:root[data-skin="lab"][data-lab="light"] .lb-wash {
  background: radial-gradient(closest-side, rgba(174, 14, 32, .05), transparent 72%);
}
:root[data-skin="lab"][data-lab="light"] .lb-tag { background: rgba(26, 19, 16, .04); }
:root[data-skin="lab"][data-lab="light"] .lb-sweep {
  background: linear-gradient(90deg, transparent, rgba(26, 19, 16, .05), transparent);
}

/* ── the render, unmounted ──────────────────────────────────────────
   On the black pages the illustration sits in an instrument bezel:
   corner ticks, a CH 01 header, a sweep across the face. That mount
   reads as equipment against black. On white it reads as a widget —
   a small grey box with a caption, which is the one thing a premium
   page cannot have — and this page's render already carries its own
   light, its own ground and its own shadow. So the frame comes off
   and the picture is left to sit on the page.

   The edges are masked, not trusted. The render's ground is #F8F8F9
   against the page's #FFFFFF: seven levels of grey, invisible as a
   tone and unmissable as a straight edge four hundred pixels long.
   The mask dissolves all four sides so the picture ends in the page
   instead of ending at a line. Two gradients intersected rather than
   one radial, because a radial would eat the corners of the podium.

   Where mask-composite is missing the two masks union instead, which
   lands back on the plain rectangle — the pre-existing look, not a
   broken one. */
:root[data-skin="lab"][data-lab="light"] .lb-scope {
  border: 0; border-radius: 0; background: none; box-shadow: none;
}
:root[data-skin="lab"][data-lab="light"] .lb-scope-bar,
:root[data-skin="lab"][data-lab="light"] .lb-tick,
:root[data-skin="lab"][data-lab="light"] .lb-sweep { display: none; }
:root[data-skin="lab"][data-lab="light"] .lb-scope-face { overflow: visible; }
:root[data-skin="lab"][data-lab="light"] .lb-scope-face img {
  -webkit-mask-image:
    linear-gradient(90deg,  transparent, #000 7%, #000 93%, transparent),
    linear-gradient(180deg, transparent, #000 6%, #000 90%, transparent);
          mask-image:
    linear-gradient(90deg,  transparent, #000 7%, #000 93%, transparent),
    linear-gradient(180deg, transparent, #000 6%, #000 90%, transparent);
  -webkit-mask-composite: source-in;
          mask-composite: intersect;
}
/* the one piece of chrome that survives: a brand bloom under the
   render, so it sits in a pool of light rather than on a flat sheet.
   No z-index — it is earlier in the tree than the face, so it paints
   under it, and staying out of the stacking order keeps it from
   sliding behind the section ground. */
:root[data-skin="lab"][data-lab="light"] .lb-scope::before {
  content: ""; position: absolute; inset: -6% -8% -12%;
  background: radial-gradient(58% 52% at 50% 56%, rgba(174, 14, 32, .07), transparent 72%);
  pointer-events: none;
}

/* ══ THE HERO THAT OPENS ══════════════════════════════════════════════
   The render starts as a small card with the headline across it, and
   grows to fill the screen as the reader scrolls while the two halves
   of the headline slide out past opposite edges. One page uses it.

   THE PIN IS CSS. A tall range with a sticky child, the same mechanism
   as the crowd section. The spec pins by calling scrollTo(0, 0) on
   every scroll event and accumulates progress out of wheel deltas it
   preventDefaults — which reads well on a trackpad and locks the page
   for everyone arriving by keyboard, scrollbar, or screen reader. Here
   the browser scrolls normally and the script only reports how far
   through the range we are; every number below is a calc() off that.

   THE BASE STATE IS THE STATIC ONE. No JavaScript, or reduced motion,
   and none of the .is-on rules apply: the range collapses, the pin
   stops pinning, and what is left is an ordinary stacked hero with the
   headline in place. The alternative is a reader who cannot run the
   script scrolling two blank viewports, or a headline parked 150vw off
   the side of a page that will never scrub it back. */
.pe { position: relative; }
.pe-pin { display: grid; grid-template-rows: auto 1fr; }
.pe-stage { position: relative; display: grid; place-items: center; width: 100%; }
.pe-media {
  position: relative; margin: 0; z-index: 1;
  width: 100%; border-radius: 20px; overflow: hidden;
}
/* contain, not cover. The spec crops to fill, which works on a
   photograph and would cut the floating tiles off this render — and
   the letterbox it avoids is white on white here, so there is nothing
   to avoid. */
.pe-media img { display: block; width: 100%; height: 100%; object-fit: contain; }
.pe-scrim { display: none; }
.pe-words { position: relative; z-index: 2; text-align: center; }
/* display:block, or the first line never moves: a transform does not
   apply to a non-replaced inline element, and this span is the only
   piece of the headline that is not already blockified — the <em>
   beside it is, which is exactly why the two halves went out of step. */
.pe-h1 > span { display: block; }
.pe-hint {
  margin: 14px 0 0; font-size: 12.5px; font-weight: 500;
  letter-spacing: .08em; text-transform: uppercase; color: var(--lb-w52);
}
.pe-after {
  width: 100%; max-width: 1440px; margin-inline: auto;
  padding-inline: clamp(20px, 4vw, 48px); padding-block: clamp(24px, 3vw, 44px);
  text-align: center;
}
/* the lab rule sets `margin: … 0 0`, which zeroes the inline margins,
   and it sits later in the file at the same specificity — so this one
   carries the skin selector to outrank it rather than to scope it. */
:root[data-skin="lab"] .pe-after .lb-sub { margin-inline: auto; }

/* ── scrubbing, once the script is in ─────────────────────────────── */
.pe.is-on .pe-range { height: 200dvh; }
.pe.is-on .pe-pin {
  position: sticky; top: 0; height: 100dvh;
  padding-top: var(--hdr);
  /* the headline halves leave through the sides; without this they
     leave through the sides and take the document width with them */
  overflow: hidden;
}
.pe.is-on .pe-ground { position: absolute; inset: 0; opacity: calc(1 - var(--pe-p, 0)); }
.pe.is-on .pe-crumbs { position: relative; z-index: 3; }

/* the stack: media and words share one grid cell, so the headline sits
   across the card rather than under it */
.pe.is-on .pe-stage > * { grid-area: 1 / 1; }

.pe.is-on .pe-media {
  width: calc(300px + var(--pe-p, 0) * 1250px);
  height: calc(400px + var(--pe-p, 0) * 400px);
  max-width: 95vw; max-height: 85vh;
  box-shadow: 0 0 50px rgba(26, 19, 16, .13);
}
/* a white veil while the headline is still over the card, gone by the
   time it is not. The spec uses black at .7→.4 because its text is pale
   over a photograph; this page is dark ink over a white render, so the
   veil is white and it clears completely. */
.pe.is-on .pe-scrim {
  display: block; position: absolute; inset: 0; pointer-events: none;
  background: #FFFFFF; opacity: calc(.62 - var(--pe-p, 0) * .62);
}
.pe.is-on .pe-out-l { transform: translateX(calc(var(--pe-p, 0) * -150vw)); }
.pe.is-on .pe-out-r { transform: translateX(calc(var(--pe-p, 0) *  150vw)); }
.pe.is-on .pe-hint  { opacity: clamp(0, calc(1 - var(--pe-p, 0) * 3), 1); }
/* the spec reveals at three-quarters and hides again below it */
.pe.is-on .pe-after { opacity: clamp(0, calc((var(--pe-p, 0) - .75) * 4), 1); }

@media (max-width: 767px) {
  /* The spec grows the box to 300x600 here. On a phone the width pins
     against 95vw almost immediately while the height keeps climbing, so
     the card finishes as a tall white box with the render stranded in
     the middle of it. The height is aimed at the picture instead:
     95vw x 2/3 is exactly where a 3:2 render lands once the width has
     capped, so the card closes on the image rather than around it. */
  .pe.is-on .pe-media {
    width: calc(300px + var(--pe-p, 0) * 650px);
    height: calc(400px - var(--pe-p, 0) * (400px - 63.4vw));
  }
  .pe.is-on .pe-out-l { transform: translateX(calc(var(--pe-p, 0) * -180vw)); }
  .pe.is-on .pe-out-r { transform: translateX(calc(var(--pe-p, 0) *  180vw)); }
}

/* The exclusions are load-bearing, and the site has now learned this four
   times. The rule means "page sections": it lifts them over the decorative
   ground. Anything else that happens to be a body child is caught by it and
   silently loses its own positioning — the header gets a stacking context
   at z-index 1 that traps its dropdown panels, and the reading bar loses
   position:fixed and scrolls away off the top of the document, which is
   exactly what it did here until it was measured at top:-1423px.

   Stated as what it excludes rather than discovered again next time. */
:root[data-skin="lab"] body > *:not(.site-header):not(.lb-progress) {
  position: relative; z-index: 1;
}
:root[data-skin="lab"] .site-header { background: var(--lb-bg) !important; }

/* The graticule. Two gradients at two scales — a fine division and a coarse
   one — so it reads as ruled glass rather than graph paper. Fixed to the
   viewport, so scrolling moves the page across the grid instead of dragging
   the grid with it, which is what makes it feel like a screen. */
:root[data-skin="lab"] body {
  background-color: var(--lb-bg);
  background-image:
    linear-gradient(var(--grid) 1px, transparent 1px),
    linear-gradient(90deg, var(--grid) 1px, transparent 1px),
    linear-gradient(var(--line-soft) 1px, transparent 1px),
    linear-gradient(90deg, var(--line-soft) 1px, transparent 1px);
  background-size: 120px 120px, 120px 120px, 24px 24px, 24px 24px;
  background-attachment: fixed;
  color: var(--lb-ink);
}

/* ── reading progress ─────────────────────────────────────────────── */
:root[data-skin="lab"] .lb-progress {
  position: fixed; inset: 0 0 auto; height: 2px; z-index: 70;
  background: var(--lb-line); pointer-events: none;
}
:root[data-skin="lab"] .lb-progress i {
  display: block; height: 100%; width: 100%;
  transform: scaleX(var(--p, 0)); transform-origin: 0 50%;
  background: linear-gradient(90deg, var(--lb-fill), var(--lb-sig));
}

/* ── hero ─────────────────────────────────────────────────────────── */
:root[data-skin="lab"] .lb-hero {
  position: relative; overflow: hidden;
  padding: clamp(26px, 3.4vw, 52px) 0 clamp(30px, 3.6vw, 56px);
}
:root[data-skin="lab"] .lb-fx { position: absolute; inset: 0; pointer-events: none; }
/* a soft signal wash off the left shoulder, so the headline sits in light */
:root[data-skin="lab"] .lb-wash {
  position: absolute; left: -12%; top: -30%; width: 70%; height: 130%;
  background: radial-gradient(closest-side, rgba(255, 255, 255, .05), transparent 72%);
}
:root[data-skin="lab"] .lb-graticule {
  position: absolute; inset: 0;
  background-image:
    linear-gradient(var(--grid-hi) 1px, transparent 1px),
    linear-gradient(90deg, var(--grid-hi) 1px, transparent 1px);
  background-size: 120px 120px;
  -webkit-mask-image: radial-gradient(120% 90% at 50% 0%, #000 30%, transparent 78%);
          mask-image: radial-gradient(120% 90% at 50% 0%, #000 30%, transparent 78%);
  opacity: .5;
}

:root[data-skin="lab"] .lb-crumbs {
  position: relative; max-width: var(--shell); margin: 0 auto;
  padding: 0 var(--gutter) clamp(20px, 2.4vw, 34px);
}
:root[data-skin="lab"] .lb-crumbs ol {
  display: flex; flex-wrap: wrap; align-items: center; gap: 8px;
  margin: 0; padding: 0; list-style: none;
}
:root[data-skin="lab"] .lb-crumbs li + li::before {
  content: '/'; margin-right: 8px; color: var(--lb-w52);
}
:root[data-skin="lab"] .lb-crumbs a { color: var(--lb-w70); text-decoration: none; }
:root[data-skin="lab"] .lb-crumbs a:hover { color: var(--lb-sig); }
:root[data-skin="lab"] .lb-crumbs [aria-current] { color: var(--lb-ink); }

:root[data-skin="lab"] .lb-hero-split {
  position: relative; max-width: var(--shell); margin: 0 auto;
  padding: 0 var(--gutter);
  display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, .82fr);
  gap: clamp(28px, 4vw, 64px); align-items: center;
}

/* the status tag, with a live indicator rather than a decorative dot */
:root[data-skin="lab"] .lb-tag {
  display: inline-flex; align-items: center; gap: 10px; margin: 0;
  padding: 7px 14px 7px 11px;
  border: 1px solid var(--lb-line-2); border-radius: 2px;
  background: rgba(255, 255, 255, .04);
  color: var(--lb-sig);
}
:root[data-skin="lab"] .lb-led {
  width: 7px; height: 7px; flex: none; border-radius: 50%;
  background: var(--lb-sig); box-shadow: 0 0 0 3px rgba(63, 227, 192, .16);
}
@media (prefers-reduced-motion: no-preference) {
  :root[data-skin="lab"] .lb-led { animation: lb-pulse 2.6s ease-in-out infinite; }
}
@keyframes lb-pulse {
  50% { box-shadow: 0 0 0 6px rgba(63, 227, 192, .05); opacity: .78; }
}

:root[data-skin="lab"] .lb-h1 {
  margin: clamp(18px, 2.2vw, 30px) 0 0; text-wrap: balance; color: var(--lb-ink);
}
:root[data-skin="lab"] .lb-h1 em {
  display: block; font-style: normal; color: var(--lb-sig);
}
:root[data-skin="lab"] .lb-sub {
  margin: clamp(14px, 1.5vw, 22px) 0 0; max-width: 46ch; color: var(--lb-w70);
}
:root[data-skin="lab"] .lb-cta { margin: clamp(22px, 2.6vw, 36px) 0 0; }
:root[data-skin="lab"] .lb-btn {
  display: inline-block; padding: 14px 26px; border-radius: 2px;
  background: var(--lb-fill); color: #fff; text-decoration: none;
  border: 1px solid transparent;
  transition: background .2s var(--ease), transform .2s var(--ease);
}
:root[data-skin="lab"] .lb-btn:hover { background: var(--lb-fill-2); transform: translateY(-1px); }

/* the bezel. Corner ticks, a channel header and a sweep across the face —
   the illustration is untouched; the mount is what says instrument. */
:root[data-skin="lab"] .lb-scope {
  position: relative; margin: 0;
  border: 1px solid var(--lb-line-2); border-radius: 3px;
  background: var(--lb-panel);
  box-shadow: 0 30px 70px -40px rgba(0, 0, 0, .9), inset 0 1px 0 rgba(150, 190, 245, .06);
}
:root[data-skin="lab"] .lb-scope-bar {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  padding: 9px 14px; border-bottom: 1px solid var(--lb-line);
  color: var(--lb-w52);
}
:root[data-skin="lab"] .lb-scope-ch { display: inline-flex; align-items: center; gap: 8px; color: var(--lb-sig); }
:root[data-skin="lab"] .lb-scope-face { position: relative; overflow: hidden; }
:root[data-skin="lab"] .lb-scope-face img { display: block; width: 100%; height: auto; }
:root[data-skin="lab"] .lb-sweep {
  position: absolute; inset: 0 auto 0 0; width: 34%;
  background: linear-gradient(90deg, transparent, rgba(255, 255, 255, .07), transparent);
  pointer-events: none;
}
@media (prefers-reduced-motion: no-preference) {
  :root[data-skin="lab"] .lb-sweep { animation: lb-sweep 5.5s var(--ease) infinite; }
}
@keyframes lb-sweep { from { transform: translateX(-110%); } to { transform: translateX(400%); } }

/* corner ticks — machined, not drawn */
:root[data-skin="lab"] .lb-tick {
  position: absolute; width: 9px; height: 9px; pointer-events: none;
  border: 1px solid var(--lb-sig); opacity: .55;
}
:root[data-skin="lab"] .lb-tick-tl { top: -1px; left: -1px; border-width: 1px 0 0 1px; }
:root[data-skin="lab"] .lb-tick-tr { top: -1px; right: -1px; border-width: 1px 1px 0 0; }
:root[data-skin="lab"] .lb-tick-bl { bottom: -1px; left: -1px; border-width: 0 0 1px 1px; }
:root[data-skin="lab"] .lb-tick-br { bottom: -1px; right: -1px; border-width: 0 1px 1px 0; }

/* the figures as a readout strip */
:root[data-skin="lab"] .lb-readout {
  position: relative; max-width: var(--shell); margin: clamp(30px, 3.6vw, 56px) auto 0;
  padding: 0 var(--gutter); list-style: none;
  display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 1px;
}
:root[data-skin="lab"] .lb-readout li {
  position: relative; padding: 18px 20px;
  border: 1px solid var(--lb-line); background: rgba(255, 255, 255, .022);
  display: grid; gap: 4px;
}
:root[data-skin="lab"] .lb-readout-ch { color: var(--lb-sig); }
:root[data-skin="lab"] .lb-readout b { color: var(--lb-ink); font-variant-numeric: tabular-nums; }
:root[data-skin="lab"] .lb-readout-s { color: var(--lb-w52); }

/* ── section furniture: one axis, one index ───────────────────────── */
:root[data-skin="lab"] .lb-sec,
:root[data-skin="lab"] .lb-seq {
  position: relative; margin: clamp(56px, 7vw, 120px) 0 0;
  padding-left: clamp(0px, 4.6vw, 74px);
}
/* the rail and its index. attr() is the whole mechanism — the number lives
   in the markup because the sections genuinely are an ordered argument. */
:root[data-skin="lab"] [data-ix]::before {
  content: attr(data-ix);
  position: absolute; left: 0; top: 2px;
  font-family: var(--lb-mono); font-size: 12px; letter-spacing: .1em;
  color: var(--lb-sig); font-variant-numeric: tabular-nums;
}
:root[data-skin="lab"] [data-ix]::after {
  content: ''; position: absolute; left: 5px; top: 26px; bottom: 0; width: 1px;
  background: linear-gradient(var(--lb-line-2), transparent);
}
:root[data-skin="lab"] .lb-head { max-width: 62ch; }
:root[data-skin="lab"] .lb-k {
  margin: 0 0 12px; color: var(--lb-sig);
  font-family: var(--lb-mono); text-transform: uppercase;
}
:root[data-skin="lab"] .lb-h { margin: 0; color: var(--lb-ink); text-wrap: balance; }
:root[data-skin="lab"] .lb-d { margin: 14px 0 0; max-width: 66ch; color: var(--lb-w70); }

/* ── thesis ───────────────────────────────────────────────────────── */
:root[data-skin="lab"] .lb-thesis-grid {
  display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: clamp(24px, 3.4vw, 56px); align-items: start;
}
:root[data-skin="lab"] .lb-say { margin: 0; color: var(--lb-ink); text-wrap: balance; }
:root[data-skin="lab"] .lb-say em {
  display: block; font-style: normal; color: var(--lb-sig); margin-top: .35em;
}
:root[data-skin="lab"] .lb-thesis-tx { display: grid; gap: 1em; }
:root[data-skin="lab"] .lb-thesis-tx p { margin: 0; color: var(--lb-w70); max-width: 62ch; }

/* ── the sequence, as six acquisition channels ────────────────────── */
:root[data-skin="lab"] .lb-seq-head { margin-bottom: clamp(30px, 3.4vw, 52px); }
:root[data-skin="lab"] .lb-chans {
  position: relative; list-style: none; margin: 0; padding: 0;
  display: grid; gap: clamp(14px, 1.6vw, 22px);
}
/* the bus every channel taps. Its fill is --p, written by [data-spine]. */
:root[data-skin="lab"] .lb-bus {
  position: absolute; left: 11px; top: 12px; bottom: 12px; width: 2px;
  background: var(--lb-line-2);
}
:root[data-skin="lab"] .lb-bus i {
  display: block; height: 100%; width: 100%;
  transform: scaleY(var(--p, 0)); transform-origin: 50% 0;
  background: linear-gradient(var(--lb-sig), var(--lb-fill));
  box-shadow: 0 0 12px rgba(63, 227, 192, .55);
}
:root[data-skin="lab"] .lb-chan {
  position: relative; padding-left: 46px;
}
:root[data-skin="lab"] .lb-chan-node {
  position: absolute; left: 5px; top: 22px;
  width: 14px; height: 14px; border-radius: 50%;
  border: 2px solid var(--lb-line-2); background: var(--lb-bg);
  transition: border-color .3s var(--ease), box-shadow .3s var(--ease), background .3s var(--ease);
}
:root[data-skin="lab"] .lb-chan.is-on .lb-chan-node {
  border-color: var(--lb-sig); background: var(--lb-sig);
  box-shadow: 0 0 0 4px rgba(63, 227, 192, .16), 0 0 14px rgba(63, 227, 192, .7);
}
:root[data-skin="lab"] .lb-chan-card {
  position: relative; overflow: hidden;
  padding: 16px 20px 18px;
  border: 1px solid var(--lb-line); border-radius: 2px;
  background: var(--lb-panel);
  transition: border-color .3s var(--ease), transform .3s var(--ease);
}
:root[data-skin="lab"] .lb-chan.is-on .lb-chan-card { border-color: var(--lb-line-2); }
:root[data-skin="lab"] .lb-chan-card:hover { border-color: var(--brand-line); transform: translateX(3px); }
:root[data-skin="lab"] .lb-chan-bar {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  margin-bottom: 10px; color: var(--lb-w52);
}
:root[data-skin="lab"] .lb-chan-n { font-family: var(--lb-mono); letter-spacing: .1em; }
:root[data-skin="lab"] .lb-chan.is-on .lb-chan-n { color: var(--lb-sig); }
:root[data-skin="lab"] .lb-chan-ic { display: block; width: 20px; height: 20px; color: var(--lb-w52); }
:root[data-skin="lab"] .lb-chan-ic svg { width: 100%; height: 100%; display: block; }
:root[data-skin="lab"] .lb-chan.is-on .lb-chan-ic { color: var(--lb-sig); }
:root[data-skin="lab"] .lb-chan-t { margin: 0; color: var(--lb-ink); }
:root[data-skin="lab"] .lb-chan-d { margin: 6px 0 0; color: var(--lb-w70); }
/* the trace along the card's foot: empty until the bus reaches the channel */
:root[data-skin="lab"] .lb-chan-trace {
  position: absolute; inset: auto 0 0 0; height: 2px; background: var(--lb-line);
}
:root[data-skin="lab"] .lb-chan-trace i {
  display: block; height: 100%; width: 100%;
  transform: scaleX(0); transform-origin: 0 50%;
  background: linear-gradient(90deg, var(--lb-sig), transparent);
  transition: transform .7s var(--ease);
}
:root[data-skin="lab"] .lb-chan.is-on .lb-chan-trace i { transform: scaleX(1); }

/* ── instrument panels ────────────────────────────────────────────── */
:root[data-skin="lab"] .lb-panels {
  margin-top: clamp(26px, 3vw, 44px);
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: clamp(12px, 1.4vw, 18px);
}
:root[data-skin="lab"] .lb-panel {
  position: relative; padding: clamp(20px, 2vw, 28px);
  border: 1px solid var(--lb-line); border-radius: 2px;
  background: var(--lb-panel);
  transition: border-color .3s var(--ease), background .3s var(--ease), transform .3s var(--ease);
}
:root[data-skin="lab"] .lb-panel-wide { grid-column: span 2; }
:root[data-skin="lab"] .lb-panel:hover {
  border-color: var(--brand-line); background: var(--lb-panel-2); transform: translateY(-3px);
}
:root[data-skin="lab"] .lb-panel .lb-tick { opacity: 0; transition: opacity .3s var(--ease); }
:root[data-skin="lab"] .lb-panel:hover .lb-tick { opacity: .8; }
:root[data-skin="lab"] .lb-panel-bar {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  padding-bottom: 14px; margin-bottom: 14px; border-bottom: 1px solid var(--lb-line);
}
:root[data-skin="lab"] .lb-panel-ic { display: block; width: 22px; height: 22px; color: var(--lb-sig); }
:root[data-skin="lab"] .lb-panel-ic svg { width: 100%; height: 100%; display: block; }
:root[data-skin="lab"] .lb-panel-n {
  font-family: var(--lb-mono); letter-spacing: .1em; color: var(--lb-w52);
  font-variant-numeric: tabular-nums; margin-left: auto;
}
:root[data-skin="lab"] .lb-panel b { display: block; color: var(--lb-ink); }
:root[data-skin="lab"] .lb-panel p { margin: 8px 0 0; color: var(--lb-w70); }
/* the closing panel is the one on the signal, so the set ends on a mark */
:root[data-skin="lab"] .lb-panel-key {
  background: var(--lb-panel-2);
  border-color: var(--brand-line);
}
:root[data-skin="lab"] .lb-panel-key b { color: var(--lb-sig); }

/* ── what you end up holding ──────────────────────────────────────── */
:root[data-skin="lab"] .lb-hold-list {
  margin: clamp(24px, 2.8vw, 40px) 0 0; padding: 0; list-style: none;
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 1px; background: var(--lb-line);
  border: 1px solid var(--lb-line);
}
:root[data-skin="lab"] .lb-hold-list li {
  display: flex; align-items: baseline; gap: 14px;
  padding: 16px 20px; background: var(--lb-bg);
  transition: background .25s var(--ease);
}
:root[data-skin="lab"] .lb-hold-list li:hover { background: var(--lb-panel); }
:root[data-skin="lab"] .lb-hold-n {
  flex: none; font-family: var(--lb-mono); color: var(--lb-sig);
  font-variant-numeric: tabular-nums;
}
:root[data-skin="lab"] .lb-hold-tx { color: var(--lb-w70); }

/* ── channel selector ─────────────────────────────────────────────── */
:root[data-skin="lab"] .lb-str { margin-top: clamp(26px, 3vw, 44px); }
:root[data-skin="lab"] .lb-str-tabs {
  position: relative; display: flex; flex-wrap: wrap; gap: 2px;
  border-bottom: 1px solid var(--lb-line);
}
:root[data-skin="lab"] .lb-str-tab {
  position: relative; padding: 12px 18px; border: 0; background: none;
  color: var(--lb-w52); cursor: pointer; font: inherit;
  font-family: var(--lb-mono); text-transform: uppercase;
  transition: color .22s var(--ease), background .22s var(--ease);
}
:root[data-skin="lab"] .lb-str-tab:hover { color: var(--lb-ink); background: var(--hover); }
:root[data-skin="lab"] .lb-str-tab[aria-selected="true"] { color: var(--lb-sig); }
:root[data-skin="lab"] .lb-str-ink {
  position: absolute; left: 0; bottom: -1px; height: 2px; width: 0;
  background: var(--lb-sig); box-shadow: 0 0 10px rgba(63, 227, 192, .8);
  transition: transform .32s var(--ease), width .32s var(--ease);
}
:root[data-skin="lab"] .lb-str-rows { margin: 0; padding: clamp(18px, 2vw, 28px) 0 0; }
:root[data-skin="lab"] .lb-str-row {
  display: grid; grid-template-columns: minmax(0, 15rem) minmax(0, 1fr);
  gap: clamp(12px, 2vw, 32px);
  padding: 16px 0; border-bottom: 1px solid var(--lb-line);
}
:root[data-skin="lab"] .lb-str-row dt {
  display: flex; align-items: baseline; gap: 10px; margin: 0; color: var(--lb-ink);
}
:root[data-skin="lab"] .lb-str-rn {
  font-family: var(--lb-mono); color: var(--lb-sig); font-variant-numeric: tabular-nums;
}
:root[data-skin="lab"] .lb-str-row dd { margin: 0; color: var(--lb-w70); }

/* ── the field ────────────────────────────────────────────────────── */
:root[data-skin="lab"] .lb-field {
  position: relative; margin: clamp(56px, 7vw, 120px) 0 0;
  padding: clamp(26px, 3vw, 44px) 0;
  border-block: 1px solid var(--lb-line);
}
:root[data-skin="lab"] .lb-field-k { margin: 0 0 18px; padding-left: clamp(0px, 4.6vw, 74px); }
:root[data-skin="lab"] .lb-field-wrap { overflow: hidden; }
:root[data-skin="lab"] .lb-field-row { display: flex; width: max-content; }
@media (prefers-reduced-motion: no-preference) {
  :root[data-skin="lab"] .lb-field-row { animation: lb-marquee 44s linear infinite; }
}
@keyframes lb-marquee { to { transform: translateX(-50%); } }
:root[data-skin="lab"] .lb-field-list {
  display: flex; gap: 10px; margin: 0; padding: 0 5px 0 0; list-style: none;
}
:root[data-skin="lab"] .lb-field-list li {
  padding: 9px 16px; white-space: nowrap;
  border: 1px solid var(--lb-line); border-radius: 2px;
  background: var(--lb-panel); color: var(--lb-w70);
  font-family: var(--lb-mono);
}

/* ── the differential ─────────────────────────────────────────────── */
:root[data-skin="lab"] .lb-diff {
  margin-top: clamp(26px, 3vw, 44px);
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 1px; background: var(--lb-line); border: 1px solid var(--lb-line);
}
:root[data-skin="lab"] .lb-diff-col { padding: clamp(20px, 2.4vw, 32px); background: var(--lb-bg); }
:root[data-skin="lab"] .lb-diff-k {
  display: flex; align-items: center; gap: 10px; margin: 0 0 16px;
  font-family: var(--lb-mono); text-transform: uppercase;
}
:root[data-skin="lab"] .lb-diff-k span {
  display: grid; place-items: center; width: 22px; height: 22px; flex: none;
  border: 1px solid currentColor; border-radius: 2px;
}
:root[data-skin="lab"] .lb-diff-a .lb-diff-k { color: var(--lb-amber); }
:root[data-skin="lab"] .lb-diff-b .lb-diff-k { color: var(--lb-sig); }
:root[data-skin="lab"] .lb-diff-col ul { margin: 0; padding: 0; list-style: none; display: grid; gap: 12px; }
:root[data-skin="lab"] .lb-diff-col li {
  position: relative; padding-left: 20px; color: var(--lb-w70);
}
:root[data-skin="lab"] .lb-diff-col li::before {
  content: ''; position: absolute; left: 0; top: .62em;
  width: 8px; height: 1px; background: currentColor; opacity: .6;
}
:root[data-skin="lab"] .lb-diff-a li::before { background: var(--lb-amber); }
:root[data-skin="lab"] .lb-diff-b li::before { background: var(--lb-sig); }

/* ── the limit ────────────────────────────────────────────────────── */
:root[data-skin="lab"] .lb-note {
  position: relative; margin: clamp(40px, 5vw, 72px) 0 0;
  padding: clamp(20px, 2.4vw, 30px);
  border: 1px solid var(--brand-line); border-radius: 2px;
  background: rgba(255, 255, 255, .03);
}
:root[data-skin="lab"] .lb-note p { margin: 0; color: var(--lb-w70); }
:root[data-skin="lab"] .lb-note b { color: var(--lb-sig); }

/* ── the reach band ───────────────────────────────────────────────── */
:root[data-skin="lab"] .lb-reach {
  margin: clamp(56px, 7vw, 120px) 0 0; padding: clamp(30px, 4vw, 64px) 0;
  border-top: 1px solid var(--lb-line);
}
:root[data-skin="lab"] .lb-reach-in {
  display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, .7fr);
  gap: clamp(20px, 3vw, 48px); align-items: center;
}
:root[data-skin="lab"] .lb-reach-h { margin: 0; color: var(--lb-ink); text-wrap: balance; }
:root[data-skin="lab"] .lb-reach-h em { font-style: normal; color: var(--lb-sig); }
:root[data-skin="lab"] .lb-reach-d { margin: 14px 0 0; max-width: 60ch; color: var(--lb-w70); }
:root[data-skin="lab"] .lb-globe img { display: block; width: 100%; height: auto; }

/* ── arrival ──────────────────────────────────────────────────────────
   Scroll-driven, so it is the browser's own timeline rather than an
   observer: no listener, no layout read, and nothing to run on a page
   nobody scrolls. Where it is unsupported the section is simply already
   there, which is the correct fallback for content. */
@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    :root[data-skin="lab"] [data-rise] {
      animation: lb-rise linear both;
      animation-timeline: view();
      animation-range: entry 4% cover 24%;
    }
  }
}
@keyframes lb-rise { from { opacity: 0; transform: translateY(24px); } }

/* ── type scale ───────────────────────────────────────────────────────
   The role layer is marked important on the type properties, so a skin
   that wants its own scale has to say so at higher specificity. Nova does
   this with repeated class names; :root[data-skin="lab"] .x is already
   (0,3,0) against the role layer's (0,1,0), so the selector alone carries
   it and the repetition is unnecessary.

   clamp() rather than Nova's vw: the ends are fixed in rem, so the scale
   stops growing on a 32-inch display and stops shrinking before it becomes
   unreadable, which raw vw does neither of. */
:root[data-skin="lab"] .lb-h1 {
  font-family: var(--font-display) !important; font-weight: 500 !important;
  font-size: clamp(2.4rem, 4.2vw, 4rem) !important; line-height: 1.08 !important;
  letter-spacing: -.02em !important;
}
:root[data-skin="lab"] :is(.lb-h, .lb-say, .lb-reach-h) {
  font-family: var(--font-display) !important; font-weight: 500 !important;
  font-size: clamp(1.65rem, 2.5vw, 2.5rem) !important; line-height: 1.18 !important;
  letter-spacing: -.015em !important;
}
:root[data-skin="lab"] .lb-sub {
  font-size: clamp(1rem, 1.25vw, 1.2rem) !important; line-height: 1.55 !important;
  font-weight: 400 !important;
}
:root[data-skin="lab"] :is(.lb-k, .lb-tag, .lb-chan-n, .lb-panel-n, .lb-str-tab,
    .lb-diff-k, .lb-readout-ch, .lb-scope-bar, .lb-hold-n, .lb-str-rn, .lb-crumbs) {
  font-family: var(--lb-mono) !important; font-size: .75rem !important;
  font-weight: 500 !important; letter-spacing: .1em !important; line-height: 1.4 !important;
}
:root[data-skin="lab"] .lb-readout b {
  font-family: var(--font-display) !important; font-weight: 500 !important;
  font-size: clamp(1.7rem, 2.4vw, 2.4rem) !important; line-height: 1.1 !important;
}
:root[data-skin="lab"] :is(.lb-chan-t, .lb-panel b) {
  font-family: var(--font-display) !important; font-weight: 500 !important;
  font-size: 1.1rem !important; line-height: 1.3 !important; letter-spacing: normal !important;
}
:root[data-skin="lab"] :is(.lb-d, .lb-thesis-tx p, .lb-chan-d, .lb-panel p, .lb-hold-tx,
    .lb-str-row dt, .lb-str-row dd, .lb-diff-col li, .lb-note p, .lb-reach-d, .lb-readout-s,
    .lb-field-list li) {
  font-size: .95rem !important; line-height: 1.6 !important;
  font-weight: 400 !important; letter-spacing: normal !important;
}
:root[data-skin="lab"] .lb-str-row dt { font-weight: 500 !important; }

/* ── narrow ───────────────────────────────────────────────────────── */
@media (max-width: 1000px) {
  :root[data-skin="lab"] .lb-hero-split { grid-template-columns: minmax(0, 1fr); }
  :root[data-skin="lab"] .lb-hero-r { order: -1; }
  :root[data-skin="lab"] .lb-thesis-grid,
  :root[data-skin="lab"] .lb-reach-in { grid-template-columns: minmax(0, 1fr); }
  :root[data-skin="lab"] .lb-panels { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  :root[data-skin="lab"] .lb-panel-wide { grid-column: span 2; }
}
@media (max-width: 680px) {
  :root[data-skin="lab"] .lb-panels,
  :root[data-skin="lab"] .lb-hold-list,
  :root[data-skin="lab"] .lb-diff { grid-template-columns: minmax(0, 1fr); }
  :root[data-skin="lab"] .lb-panel-wide { grid-column: auto; }
  :root[data-skin="lab"] .lb-str-row { grid-template-columns: minmax(0, 1fr); gap: 6px; }
  /* the rail costs more than it earns at this width */
  :root[data-skin="lab"] .lb-sec,
  :root[data-skin="lab"] .lb-seq { padding-left: 0; }
  :root[data-skin="lab"] [data-ix]::before { position: static; display: block; margin-bottom: 10px; }
  :root[data-skin="lab"] [data-ix]::after { display: none; }
  :root[data-skin="lab"] .lb-field-k { padding-left: 0; }
}

/* ── the chrome on Lab ────────────────────────────────────────────────
   The header, the dropdowns, the footer and the FAQ are shared components
   painted for a light ground. On a dark skin every one of them has to be
   re-stated or it ships dark-on-dark; measured before this block, the nav
   triggers read 1.93:1 and Sign in 1.07:1 — present, and invisible. Nova
   and Atlas each carry the same block for the same reason. */
:root[data-skin="lab"] .site-header:not(.on-hero):not(.past-hero) :is(
  .menu-trigger, .nav-links > a, .mp-plain > a, .nav-signin, .burger
) { color: var(--lb-ink); }
:root[data-skin="lab"] .site-header:not(.on-hero):not(.past-hero)
  :is(.menu-trigger, .nav-links > a):hover { color: var(--lb-sig); }
:root[data-skin="lab"] .site-header .brand-mark,
:root[data-skin="lab"] .site-header:not(.on-hero):not(.past-hero) .brand-mark {
  background: var(--lb-ink) !important; --logo-ink: var(--lb-ink);
}
:root[data-skin="lab"] .site-header:not(.on-hero):not(.past-hero) .nav-signin {
  border-color: var(--lb-line-2);
}
:root[data-skin="lab"] .site-header:not(.on-hero):not(.past-hero) .nav-signin:hover {
  border-color: var(--lb-sig); color: var(--lb-sig);
}
:root[data-skin="lab"] .site-header .nav-start {
  background: var(--lb-fill) !important; color: #fff !important; border: 0 !important;
}
:root[data-skin="lab"] .site-header .nav-start:hover { background: var(--lb-fill-2) !important; }
:root[data-skin="lab"] .site-header:not(.on-hero):not(.past-hero) :is(.burger .bg-tx, .burger .bg-ic) {
  background: none; color: var(--lb-ink);
}
:root[data-skin="lab"] .site-header:not(.on-hero):not(.past-hero) .burger svg { color: var(--lb-ink); }

/* .btn-primary paints background: var(--ink) — a near-white ink here — under
   a white label. That is the 1.09:1 button Atlas shipped before it was
   caught; the mobile drawer's Get Started is the one on these pages. */
:root[data-skin="lab"] .btn-primary { background: var(--lb-fill); color: #fff; }
:root[data-skin="lab"] .btn-primary:hover { background: var(--lb-fill-2); color: #fff; }

/* the dropdown panels are their own surface */
:root[data-skin="lab"] :is(.menu-panel, .menu-panel.mega) {
  background: var(--lb-panel-2); border-color: var(--lb-line-2); color: var(--lb-ink);
}
:root[data-skin="lab"] :is(.mm-row b, .mm-col h3) { color: var(--lb-ink); }
:root[data-skin="lab"] .mm-row em { color: var(--lb-w52); }
:root[data-skin="lab"] .mm-row:hover { background: var(--hover); }
:root[data-skin="lab"] .mm-ic { background: rgba(255, 255, 255, .06); }
:root[data-skin="lab"] .menu-panel a.mm-all,
:root[data-skin="lab"] .menu-panel a.mm-all span { color: var(--lb-sig); }

:root[data-skin="lab"]:not([data-lab="light"]) .foot { background: #050505; }
:root[data-skin="lab"]:not([data-lab="light"]) .foot-glow { opacity: .18; }

/* The wordmark stays on mobile, as it does on the other two dark skins.
   The site swaps to the icon-only mark below 600px; a visitor moving
   between Services, Project Training and Academic Projects would otherwise
   watch the logo change shape between three sibling dark sections. */
@media (max-width: 600px) {
  :root[data-skin="lab"] .site-header .brand-mark {
    width: 25.1vw; height: 5.13vw; background: var(--lb-ink);
    -webkit-mask-image: url('assets/wordmark.webp'); mask-image: url('assets/wordmark.webp');
    -webkit-mask-size: contain; mask-size: contain;
    -webkit-mask-repeat: no-repeat; mask-repeat: no-repeat;
    -webkit-mask-position: left center; mask-position: left center;
  }
}

/* ── the FAQ on Lab ───────────────────────────────────────────────────
   :is(#faq, #cfq) sets --fq-card and --fq-edge on the SECTION. A custom
   property set closer to the element wins, so flipping them in the :root
   block above does nothing — they have to be re-set at this level, which
   is the only one that works. Atlas learned this the expensive way. */
:root[data-skin="lab"] :is(#faq, #cfq) {
  --fq-card: var(--lb-panel);
  --fq-edge: var(--lb-line);
  color: var(--lb-ink);
}
:root[data-skin="lab"] #cfq .fq-head { text-align: left; align-items: flex-start; }
:root[data-skin="lab"] #cfq .fq-pill {
  background: none; border: 1px solid var(--lb-line-2); border-radius: 2px;
  color: var(--lb-sig) !important; font-family: var(--lb-mono) !important;
  letter-spacing: .12em !important; text-transform: uppercase;
}
:root[data-skin="lab"] #cfq h2 {
  font-family: var(--font-display) !important; letter-spacing: -.02em !important;
  color: var(--lb-ink) !important;
}
:root[data-skin="lab"] #cfq h2 em {
  font-family: inherit !important; font-style: normal !important;
  color: var(--lb-sig) !important; background: none !important;
  -webkit-text-fill-color: var(--lb-sig);
}
:root[data-skin="lab"] #cfq .lede { color: var(--lb-w70) !important; }
:root[data-skin="lab"] #cfq .fq {
  border-radius: 2px; box-shadow: none; border-color: var(--lb-line);
}
:root[data-skin="lab"] #cfq .fq:hover { transform: none; border-color: var(--lb-line-2); }
:root[data-skin="lab"] #cfq .fq[data-open="true"] { border-color: var(--brand-line); }
:root[data-skin="lab"] #cfq .fq-q { color: var(--lb-ink) !important; }
:root[data-skin="lab"] #cfq .fq-p p { color: var(--lb-w70) !important; }
:root[data-skin="lab"] #cfq .fq-meta { color: var(--lb-sig) !important; }
:root[data-skin="lab"] #cfq .fq-ic { border-radius: 2px; border-color: var(--lb-line-2); }
:root[data-skin="lab"] #cfq .fq:hover .fq-ic { border-color: var(--lb-sig); }
:root[data-skin="lab"] #cfq .fq-ic::before,
:root[data-skin="lab"] #cfq .fq-ic::after { background: var(--lb-sig); }
:root[data-skin="lab"] #cfq .fq-all a { color: var(--lb-sig) !important; }
:root[data-skin="lab"] #cfq .fq-glow { display: none; }

/* the cross-link rail at the foot of every subpage */
:root[data-skin="lab"] .abx-more { border-top-color: var(--lb-line); }
:root[data-skin="lab"] :is(.abx-more h2, .abx-more h3) { color: var(--lb-ink); }
:root[data-skin="lab"] .abx-more p { color: var(--lb-w70); }

/* The channel row, laid out wide.

   Stacked, each row was a two-word title and a one-line reading floating in
   a full-width card — mostly empty box. Read across instead, the three parts
   land on three fixed columns, so six rows scan down as a readout table
   rather than six paragraphs: channel, stage, reading. It also answers the
   alignment brief, since every title starts at the same x and every reading
   starts at the same x, on every page in the section. */
@media (min-width: 861px) {
  :root[data-skin="lab"] .lb-chan-card {
    display: grid;
    grid-template-columns: 7.5rem minmax(0, 13rem) minmax(0, 1fr);
    align-items: baseline; gap: 0 clamp(18px, 2.6vw, 44px);
    padding: 18px 22px 20px;
  }
  :root[data-skin="lab"] .lb-chan-bar {
    justify-content: flex-start; gap: 12px; margin-bottom: 0;
  }
  :root[data-skin="lab"] .lb-chan-t,
  :root[data-skin="lab"] .lb-chan-d { margin: 0; }
}

/* ── the closing ask ──────────────────────────────────────────────────
   This was a standalone <section class="abx-cta"> sitting directly above
   the footer: its own dark block, immediately followed by another dark
   block, so every page ended by saying one thing twice over.

   It is the same composition — .close-cta and .cta-pill are untouched, so
   the centred headline with its italic accent and the moulded slab button
   are exactly what they were. Only the container moved. Which means there
   is almost nothing to write here: a ground it inherits from the footer,
   the footer's own rule beneath it, and the padding the band used to carry
   on its own section. */
.foot-cta {
  position: relative; z-index: 1;
  padding-block: clamp(48px, 6vw, 84px);
  border-bottom: 1px solid var(--line);
}
.foot-cta .close-cta { margin-inline: auto; }

/* The burger and the on-hero header controls take their tint from the light
   theme's warm brown, rgba(28,18,14,...). On a black ground that is a
   coloured background, faint but present, and the brief is that no ground
   carries a colour. Neutral alphas on all three dark skins. */
:root:is([data-skin="nova"], [data-skin="atlas"], [data-skin="lab"]) .site-header .burger {
  background: rgba(255, 255, 255, .04);
  border-color: rgba(255, 255, 255, .16);
}
:root:is([data-skin="nova"], [data-skin="atlas"], [data-skin="lab"]) .site-header .burger:hover {
  background: rgba(255, 255, 255, .09);
  border-color: rgba(255, 255, 255, .3);
}

/* ══ ONE TYPE SYSTEM ACROSS THE DARK SECTIONS ═════════════════════════
   Every section subpage is now set in the Academic Projects page's type:
   Clash Display for anything structural, Instrument Sans for anything read,
   and Nova's eight roles at Nova's sizes and line-heights.

   Three skins had grown three scales — Nova in vw, Atlas and Lab in
   clamp() — with three different tracking policies. That is three answers
   to a question the reader asks once. Nova's is the answer, so the other
   two are mapped onto it role for role rather than approximated.

   Nova's roles, and what each is:
     h1       display 500   3.2vw / 1.15    9.5vw on phones
     h2       display 500   1.667vw / 1.5   6.2vw
     figure   display 600   3.47vw / 1.2    11vw
     title    display 500   1.389vw / 1.5   4.6vw
     lede     body    400   1.389vw / 1.5   4.4vw
     body     body    400   1.25vw / 1.5    4.1vw
     label    body    400   1.11vw / 1.5    3.6vw
     control  body    500   1.11vw / 1.5    4.1vw

   Two things go with this, and both are deliberate. Lab's labels were set
   in a monospace and tracked uppercase, which was the readout idiom; Nova
   tracks nothing anywhere, so they come back to Instrument Sans at normal
   tracking. Tabular figures stay wherever numbers line up in a column,
   because that is alignment doing a job rather than a style. */
:root:is([data-skin="atlas"], [data-skin="lab"]) {
  --sec-display: 'Clash Display', 'Host Grotesk', ui-sans-serif, system-ui, sans-serif;
  --sec-body: 'Instrument Sans', ui-sans-serif, system-ui, sans-serif;
}

/* no tracking adjustments anywhere, as on the reference page */
:root:is([data-skin="atlas"], [data-skin="lab"]) :is(
  .at-h1, .at-h, .at-k, .at-sub, .at-d, .at-tag, .at-step-t, .at-step-d,
  .at-step-n, .at-card b, .at-card p, .at-row dt, .at-row dd, .at-note p,
  .at-btn, .at-crumbs, .at-crumbs a, .at-crumbs span,
  .lb-h1, .lb-h, .lb-say, .lb-reach-h, .lb-sub, .lb-d, .lb-reach-d, .lb-k,
  .lb-tag, .lb-diff-k, .lb-scope-bar, .lb-crumbs, .lb-crumbs a, .lb-crumbs span,
  .lb-readout b, .lb-readout-s, .lb-readout-ch, .lb-chan-t, .lb-chan-d,
  .lb-chan-n, .lb-panel b, .lb-panel p, .lb-panel-n, .lb-hold-n, .lb-hold-tx,
  .lb-str-tab, .lb-str-row dt, .lb-str-row dd, .lb-str-rn, .lb-diff-col li,
  .lb-note p, .lb-thesis-tx p, .lb-field-list li, .lb-btn
) { letter-spacing: normal !important; text-transform: none !important; }

:root:is([data-skin="atlas"], [data-skin="lab"]) :is(.at-h1, .lb-h1) {
  font-family: var(--sec-display) !important; font-weight: 500 !important;
  font-size: 3.2vw !important; line-height: 1.15 !important;
}
:root:is([data-skin="atlas"], [data-skin="lab"]) :is(.at-h, .lb-h, .lb-say, .lb-reach-h) {
  font-family: var(--sec-display) !important; font-weight: 500 !important;
  font-size: 1.667vw !important; line-height: 1.5 !important;
}
:root[data-skin="lab"] .lb-readout b {
  font-family: var(--sec-display) !important; font-weight: 600 !important;
  font-size: 3.47vw !important; line-height: 1.2 !important;
}
:root:is([data-skin="atlas"], [data-skin="lab"]) :is(
  .at-step-t, .at-card b, .at-row dt, .lb-chan-t, .lb-panel b, .lb-str-row dt) {
  font-family: var(--sec-display) !important; font-weight: 500 !important;
  font-size: 1.389vw !important; line-height: 1.5 !important;
}
:root:is([data-skin="atlas"], [data-skin="lab"]) :is(
  .at-sub, .at-d, .lb-sub, .lb-d, .lb-reach-d) {
  font-family: var(--sec-body) !important; font-weight: 400 !important;
  font-size: 1.389vw !important; line-height: 1.5 !important;
}
:root:is([data-skin="atlas"], [data-skin="lab"]) :is(
  .at-step-d, .at-card p, .at-row dd, .at-note p,
  .lb-chan-d, .lb-panel p, .lb-hold-tx, .lb-str-row dd, .lb-diff-col li,
  .lb-note p, .lb-thesis-tx p, .lb-field-list li) {
  font-family: var(--sec-body) !important; font-weight: 400 !important;
  font-size: 1.25vw !important; line-height: 1.5 !important;
}
:root:is([data-skin="atlas"], [data-skin="lab"]) :is(
  .at-k, .at-tag, .at-step-n, .at-crumbs,
  .lb-k, .lb-tag, .lb-diff-k, .lb-scope-bar, .lb-crumbs, .lb-readout-s,
  .lb-readout-ch, .lb-chan-n, .lb-panel-n, .lb-hold-n, .lb-str-rn),
:root[data-skin="lab"] [data-ix]::before {
  font-family: var(--sec-body) !important; font-weight: 400 !important;
  font-size: 1.11vw !important; line-height: 1.5 !important;
  letter-spacing: normal !important; text-transform: none !important;
}
:root:is([data-skin="atlas"], [data-skin="lab"]) :is(.at-btn, .lb-btn, .lb-str-tab) {
  font-family: var(--sec-body) !important; font-weight: 500 !important;
  font-size: 1.11vw !important; line-height: 1.5 !important;
}
/* numbers that sit in a column still line up — that is alignment, not style */
:root[data-skin="lab"] :is(.lb-readout b, .lb-readout-ch, .lb-chan-n,
  .lb-panel-n, .lb-hold-n, .lb-str-rn),
:root[data-skin="lab"] [data-ix]::before { font-variant-numeric: tabular-nums !important; }

@media (max-width: 600px) {
  :root:is([data-skin="atlas"], [data-skin="lab"]) :is(.at-h1, .lb-h1) { font-size: 9.5vw !important; }
  :root:is([data-skin="atlas"], [data-skin="lab"]) :is(.at-h, .lb-h, .lb-say, .lb-reach-h) { font-size: 6.2vw !important; }
  :root[data-skin="lab"] .lb-readout b { font-size: 11vw !important; }
  :root:is([data-skin="atlas"], [data-skin="lab"]) :is(
    .at-step-t, .at-card b, .at-row dt, .lb-chan-t, .lb-panel b, .lb-str-row dt) { font-size: 4.6vw !important; }
  :root:is([data-skin="atlas"], [data-skin="lab"]) :is(
    .at-sub, .at-d, .lb-sub, .lb-d, .lb-reach-d) { font-size: 4.4vw !important; }
  :root:is([data-skin="atlas"], [data-skin="lab"]) :is(
    .at-step-d, .at-card p, .at-row dd, .at-note p,
    .lb-chan-d, .lb-panel p, .lb-hold-tx, .lb-str-row dd, .lb-diff-col li,
    .lb-note p, .lb-thesis-tx p, .lb-field-list li) { font-size: 4.1vw !important; }
  :root:is([data-skin="atlas"], [data-skin="lab"]) :is(
    .at-k, .at-tag, .at-step-n, .at-crumbs,
    .lb-k, .lb-tag, .lb-diff-k, .lb-scope-bar, .lb-crumbs, .lb-readout-s,
    .lb-readout-ch, .lb-chan-n, .lb-panel-n, .lb-hold-n, .lb-str-rn),
  :root[data-skin="lab"] [data-ix]::before { font-size: 3.6vw !important; }
  :root:is([data-skin="atlas"], [data-skin="lab"]) :is(.at-btn, .lb-btn, .lb-str-tab) { font-size: 4.1vw !important; }
}

/* ── section reveals ──────────────────────────────────────────────────
   Fade and rise, staggered per child, as each section enters.

   Scroll-driven, so it is the browser's own timeline: no observer, no
   listener, nothing running for a section nobody has reached. The stagger
   is done with animation-range rather than animation-delay, because a
   delay on a scroll timeline is time and this needs to be distance — each
   child starts a little further into the section's entry than the last, so
   it reads as a sequence at any scroll speed, including a flick.

   Only the five sections that were not already animating. #why and
   #stories run their own motion and are left alone rather than given a
   second one on top. The pinned panels scrub instead, which is a different
   contract and already handled.

   Where view() is unsupported nothing runs and every child is simply
   already there, which is the only acceptable failure mode for content. */
@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    :is(#produces, #services, #compare, #blog, #faq) > .wrap > *,
    #services .cards3 > * {
      animation: sec-rise linear both;
      animation-timeline: view();
      animation-range: entry 2% cover 20%;
    }
    :is(#produces, #services, #compare, #blog, #faq) > .wrap > *:nth-child(2) { animation-range: entry 5% cover 23%; }
    :is(#produces, #services, #compare, #blog, #faq) > .wrap > *:nth-child(3) { animation-range: entry 8% cover 26%; }
    :is(#produces, #services, #compare, #blog, #faq) > .wrap > *:nth-child(4) { animation-range: entry 11% cover 29%; }
    #services .cards3 > *:nth-child(2) { animation-range: entry 6% cover 24%; }
    #services .cards3 > *:nth-child(3) { animation-range: entry 10% cover 28%; }
  }
}
@keyframes sec-rise { from { opacity: 0; transform: translateY(22px); } }

/* ── stop the page scrolling sideways ─────────────────────────────────
   The landing page could be dragged 500px to the right. body already had
   overflow-x: hidden, which did not hold: overflow propagates from <html>
   to the viewport, and only falls through to <body> when html's is
   `visible` — and once it propagates, body stops clipping for itself, so
   the declaration was doing nothing at all.

   `clip`, not `hidden`, and on both. hidden makes a scroll container, and
   a scroll container is what position:sticky sticks to — putting hidden on
   html alone stops the sideways scroll and stops the hero pinning with it,
   which is measurably worse than the bug. clip does not create one, so
   every pin on the page still holds against the viewport.

   The overflow itself is legitimate: the testimonial marquee is wider than
   the screen by design and its own row clips it visually. This stops the
   viewport reporting that width as scrollable. */
html, body { overflow-x: clip; }

/* ── the footer wordmark, lit by a spotlight ──────────────────────────
   Two copies of the mark, stacked pixel-aligned: a base that is only just
   above the footer's ground, and a brand-gradient copy masked to a circle
   that follows the pointer.

   Inverted from the reference, which puts a near-white mark on a white
   footer. This footer is black, so the same relationship — a mark barely
   separated from its ground — means near-black, not near-white. A #F0F0F0
   mark here would be the loudest thing on the page rather than the
   quietest.

   The mark is a webp used as a mask, which is how .brand-mark already
   draws it, so both layers paint a colour THROUGH the artwork rather than
   tinting an image. That is what lets the lit copy carry a gradient at all.

   Two masks cannot go on one element without mask-composite, so the
   spotlight lives on a wrapper and the wordmark on the child. Nesting is
   better supported than compositing and reads more plainly.

   Both the stage height and the spotlight are viewport-relative, so they
   scale together and the reveal keeps its proportion at any width. */
.foot-stage {
  position: relative; z-index: 1;
  width: 100%;   /* height is set once, at the foot of this file */
  margin-top: clamp(28px, 5vw, 76px);
  overflow: hidden;                  /* the mark bleeds off the bottom edge */
  pointer-events: auto;
}
.foot-stage-base, .foot-stage-lit {
  position: absolute; inset: 0; display: block;
  -webkit-mask: url('assets/wordmark-word.webp') no-repeat bottom center / 100% auto;
          mask: url('assets/wordmark-word.webp') no-repeat bottom center / 100% auto;
}
.foot-stage-base { background: #141414; }
.foot-stage-lit  { background: linear-gradient(96deg, #ED9C00 0%, #B1060F 62%, #8A0007 100%); }

/* the spotlight. Solid to 50% and transparent at 130% gives a hard centre
   and a long soft edge that spills past the nominal circle. Off-canvas at
   rest, so nothing is lit until the pointer actually arrives — the
   reference defaults to 0 and leaves a blob in the corner until first
   move. */
.foot-stage-spot {
  position: absolute; inset: 0; display: block;
  -webkit-mask-image: radial-gradient(circle 8vw at var(--x, -999px) var(--y, -999px), #000 50%, transparent 130%);
          mask-image: radial-gradient(circle 8vw at var(--x, -999px) var(--y, -999px), #000 50%, transparent 130%);
}

/* Pointer-only by nature: a touch device never moves a cursor, so it sees
   the base alone. Stated rather than left to chance, and the stage keeps
   its height so the footer does not change shape between devices. */
@media (hover: none) {
  .foot-stage-spot { display: none; }
}
@media (prefers-reduced-motion: reduce) {
  .foot-stage-spot { display: none; }
}

/* ══ THE FOOTER, ON WHITE ══════════════════════════════════════════════
   The reference footer is the one section that leaves the page ground,
   and it leaves it upward: #FFFFFF against a near-white page, no border
   and no shadow at the boundary, just the colour change.

   This footer was black. Everything in it already reads through four
   tokens — ink, dim, faint, line — so the flip is those four plus the
   ground, not a rewrite. They are restated here rather than edited in
   place so the dark set stays intact and readable above: if the footer
   ever goes back to black, this block comes out and nothing else moves. */
.foot {
  --f-ink:   #212121;    /* headings and brand */
  --f-dim:   #1C1C1C;    /* links and body at rest — near-black */
  --f-faint: #262626;    /* fine print — near-black */
  --f-line:  #E7E7E7;    /* hairlines */
  --logo-ink: #212121;
  background: #FFFFFF;
  color: var(--f-dim);
}
.foot :is(.foot-cols a, .foot-contact a, .foot-legalrow a, .foot-top a):hover { color: var(--f-ink); }
.foot .foot-social a:hover { background: none; }

/* The call to action and the connect line were never brought across with
   the rest of the flip. They still read the PAGE's tokens — --ink, --muted
   and --brand — and this footer is white under every one of the skins,
   including the three that are black pages. So on /support/, /publication/,
   /project-training/ and /services/ the heading arrived as #F4F4F4 on
   #FFFFFF and the accent as the Lab skin's teal: 1.1 and 1.6 against the
   ground, which is to say invisible. The links beside them were fine,
   because links were in the token set and these were not.

   They are in it now. The accent is stated outright for the same reason
   the pricing heading states it: --brand is whatever the skin says it is,
   and the footer does not follow the skin. */
.foot { --f-accent: #AE0E20; }
.foot .foot-cta h2 { color: var(--f-ink); }
.foot .foot-cta .lede { color: var(--f-dim); }
.foot .foot-cta h2 em,
.foot .foot-connect-h { color: var(--f-accent); }

/* ── connect band ─────────────────────────────────────────────────────
   Centred under the columns. The heading is the display serif UPRIGHT —
   not italic, which is what the rest of the site uses it for — so it
   reads as a label rather than as another emphasised headline. */
.foot-connect { text-align: center; padding-top: 22px; }
.foot-connect-h {
  margin: 0 0 16px;
  font-family: var(--font-serif); font-style: normal; font-weight: 400;
  font-size: clamp(20px, 1.4vw, 24px); line-height: 36px;
  color: var(--brand);
}
.foot-social {
  display: flex; align-items: center; justify-content: center;
  gap: 22px; margin: 0; padding: 0 0 22px; list-style: none;
}
/* the rule sits on the item, not between items, and the last is
   suppressed — so the row stays one flex line and ends cleanly */
.foot-social li { display: flex; align-items: center; padding-right: 22px; }
.foot-social li:not(:last-child) { border-right: 1px solid var(--rule, #C4C4C4); }
.foot-social li:last-child { padding-right: 0; }
/* full-colour brand marks, so the tile is the icon: no border, no box */
.foot-social a {
  display: block; width: 34px; height: 34px;
  border: 0; background: none; border-radius: 0;
  transition: transform .2s var(--ease), opacity .2s var(--ease);
}
.foot-social a:hover { transform: translateY(-2px); opacity: .88; }
.foot-social svg { display: block; width: 100%; height: 100%; }

/* ── legal row ────────────────────────────────────────────────────────
   Deliberately the heaviest and darkest text down here: it is the row
   people go looking for, and it was the faintest. Each bullet is its own
   flex child so it wraps with the links rather than sticking to one. */
.foot-legalrow {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: center;
  gap: 14px; padding: 0 16px; border: 0; background: none;
}
.foot-legalrow a {
  font-size: 13px; font-weight: 600; color: #383838 !important;
  text-decoration: none; padding: 0;
}
.foot-legalrow a:hover { color: #000 !important; text-decoration: underline; }
.foot-legalrow span { color: var(--f-faint); font-size: 13px; line-height: 1; }
.foot-bar { justify-content: center; }
@media (min-width: 601px) {
  .foot-legalrow a { font-size: 13px; }
}
@media (max-width: 600px) {
  .foot-legalrow { padding-top: 29px; }
  .foot-legalrow a { font-size: 16px; line-height: 24px; }
  .foot-connect { padding-top: 0; }
  .foot-social { padding-bottom: 0; }
  .foot-social a { width: 36px; height: 36px; }
}

/* the ghost wordmark takes the reference's own near-white now that the
   ground is white — the inversion that made it #141414 is undone with it */
.foot-stage-base { background: #F0F0F0; }

/* The bar stacks and centres, as the reference does. It puts the legal row
   on its own centred line; this footer also carries a copyright note the
   reference has no equivalent for, so that goes beneath it rather than
   beside it — pushing the links off-centre to make room for it is the one
   thing that would stop it reading like the reference. */
.foot-bar {
  display: flex; flex-direction: column; align-items: center; gap: 12px;
  text-align: center;
}
.foot-bar-end { width: 100%; display: flex; justify-content: center; }
.foot-bar .foot-note { order: 2; }

/* ── the link grid, as the reference arranges it ──────────────────────
   The reference footer is one centred column of blocks: call to action,
   then the link columns as a centred group, then the connect band, then
   the legal row. There is no brand block beside the columns — which is
   what was pushing this grid off-centre and squeezing the columns into
   the right two thirds.

   This footer does carry a brand block, and it holds the registered
   company name and the address people actually write to, so it moves
   below the columns rather than being removed. Ordered with CSS, not by
   moving markup: the reading order stays brand-then-links for a screen
   reader, which is the better order to hear them in. */
.foot-in {
  display: flex; flex-direction: column; align-items: center;
  gap: clamp(34px, 4vw, 54px);
  grid-template-columns: none;
}
.foot-brand { order: 2; text-align: center; }
.foot-brand p { margin-inline: auto; }
.foot-brand .foot-contact { justify-content: center; }
.foot-cols {
  order: 1; width: fit-content; max-width: 100%;
  grid-template-columns: repeat(4, max-content);
  column-gap: clamp(32px, 5vw, 70px); row-gap: 20px;
}

/* Headings at the reference's size and case. They were 11px uppercase
   with 0.08em tracking — a label. The reference sets them as small
   headings, 16/24 at 700 in the ink colour, which is what makes the
   columns read as six titled lists rather than six captioned ones.
   Marked important to clear the type-role layer, which owns this
   selector at (0,1,0) and would otherwise win on its own !important. */
.foot .foot-cols :is(h4, summary h4) {
  font-size: 16px !important; line-height: 24px !important;
  font-weight: 700 !important; letter-spacing: normal !important;
  text-transform: none !important; color: var(--f-ink) !important;
  margin: 0 0 12px !important;
}

/* The rows are the hit area, as the reference builds them: the padding
   and the radius are on the link itself, so the hover tint is a shape
   around the label rather than an underline under it. */
.foot .foot-cols a {
  display: flex; align-items: center; gap: 8px;
  padding: 10px 8px; border-radius: 8px;
  font-size: 15px; line-height: 1.4;
  white-space: nowrap; text-align: left;
  transition: background .2s var(--ease), color .2s var(--ease);
}
.foot .foot-cols a:hover { background: #3838380D; color: var(--f-ink); }
.foot .foot-col-links { display: flex; flex-direction: column; }

@media (max-width: 900px) {
  .foot-cols { grid-template-columns: repeat(2, max-content); }
}
@media (max-width: 600px) {
  .foot-cols { grid-template-columns: minmax(0, 1fr); width: 100%; }
  .foot .foot-cols a { font-size: 16px; line-height: 24px; }
}

/* ── the wordmark, sized and placed as the reference does ─────────────
   Measured off the reference: the mark spans 17%→83% of the width, sits
   centred, and is complete — no crop at any edge, with about a fifth of
   its own height as clear space beneath it. This was full-bleed and cut
   off at the bottom, which is a different gesture: a mark running off the
   page rather than a mark resting on it.

   66% wide against a 5.708:1 word is 11.56vw of height, so the stage is
   that plus the clear space below. Both are vw, so the whole block keeps
   its proportion at any width. */
.foot-stage { margin-top: clamp(24px, 3.5vw, 60px); }
.foot-stage-base, .foot-stage-lit {
  -webkit-mask: url('assets/wordmark-word.webp') no-repeat center bottom 2vw / 66% auto;
          mask: url('assets/wordmark-word.webp') no-repeat center bottom 2vw / 66% auto;
}
/* the spotlight scales with the mark, as the reference's 8vw does */
.foot-stage-spot {
  -webkit-mask-image: radial-gradient(circle 7vw at var(--x, -999px) var(--y, -999px), #000 50%, transparent 130%);
          mask-image: radial-gradient(circle 7vw at var(--x, -999px) var(--y, -999px), #000 50%, transparent 130%);
}

/* ── the brand block, compacted ───────────────────────────────────────
   The reference has no brand block at all; this one was 245px of logo,
   paragraph, company name, address and email sitting between the columns
   and the connect band, which is most of why this footer ran 500px longer
   than the reference for the same content.

   It is not removed — it carries the registered name and the address
   people actually write to. The duplicate logo goes, because the mark is
   already in the header on every page and again, six times the size,
   directly below this. The rest is set small and tight so it reads as a
   colophon rather than as a column. */
.foot-brand .brand { display: none; }
.foot-brand p {
  margin: 0 auto; font-size: 13px; line-height: 1.55; max-width: 46ch;
}
.foot-brand .foot-legal { margin-top: 6px !important; }
.foot-brand .foot-contact {
  display: flex; flex-wrap: wrap; gap: 6px 20px;
  margin: 8px 0 0; padding: 0; list-style: none;
}
.foot-brand .foot-contact li { font-size: 13px; }

/* the whole column tightens to the reference's rhythm */
.foot-in { gap: clamp(26px, 2.6vw, 38px); padding-block: clamp(30px, 3.4vw, 46px); }
.foot-connect { padding-top: clamp(18px, 2.4vw, 34px); }

/* ── vertical rhythm, matched to the reference ────────────────────────
   Measured off it at 2000px wide against a ~1180px footer: 75px of air
   above the headline, 93px from the button down to the column headings,
   75px from the columns to the connect band, and 83px from the legal row
   to the top of the mark. As fractions of the width that is 3.75vw,
   4.65vw, 3.75vw and 4.15vw — so they are set in vw and the whole block
   keeps its proportions the way the reference's does.

   This footer was running about 8vw above the headline and 7vw below the
   button, which is why it measured 500px longer than the reference for
   less content. */
.foot-panel { padding-block: clamp(26px, 3.2vw, 52px) 0; }
.foot-cta { padding-block: clamp(30px, 3.75vw, 66px); }
.foot-in { gap: clamp(24px, 3.4vw, 52px); padding-block: clamp(28px, 4.4vw, 74px) 0; }
.foot-connect { padding-top: clamp(24px, 3.75vw, 62px); }
.foot-bar { padding-block: clamp(20px, 2.4vw, 34px) 0; }
.foot-stage { margin-top: clamp(26px, 4.15vw, 70px); }

/* The bar reads brand, copyright, legal — so the legal row is the last
   text before the mark, which is where the reference puts it. */
.foot-bar .foot-brand { order: 1; }
.foot-bar .foot-note  { order: 2; }
.foot-bar .foot-bar-end { order: 3; }
.foot-bar { gap: 10px; }
.foot-brand { text-align: center; }

/* the last of the rhythm, now that nothing sits between the columns and
   the connect band: the reference's 3.75 / 4.65 / 3.75 / 4.15 */
.foot-panel { padding-block: clamp(22px, 2.6vw, 44px) 0; }
.foot-cta   { padding-block: clamp(24px, 2.9vw, 52px); }
.foot-in    { padding-block: clamp(24px, 3.6vw, 62px) 0; gap: 0; }
.foot-connect { padding-top: clamp(22px, 3.4vw, 58px); }
.foot-stage { margin-top: clamp(22px, 3.4vw, 58px); }

/* Final pass on the rhythm. The clamp ceilings were what was binding at
   1920, not the vw terms, so the ceilings are what move: the reference's
   3.75 / 4.65 / 3.75 / 4.15vw work out to 72 / 89 / 72 / 80px there. */
.foot-panel   { padding-block: clamp(18px, 2.6vw, 32px) 0; }
.foot-cta     { padding-block: clamp(20px, 2.9vw, 40px); }
.foot-in      { padding-block: clamp(20px, 3.6vw, 49px) 0; }
.foot-connect { padding-top: clamp(26px, 3.75vw, 72px); }
.foot-stage   { margin-top: clamp(28px, 4.15vw, 80px); }

/* ── the mark, matched on height rather than width ────────────────────
   The reference's mark is 66% of the viewport wide and 14.5vw tall. Set
   to the same 66%, this one came out 11.56vw — a fifth shorter and
   visibly weaker — because the two words are not the same shape:
   "ambitio" is 4.55:1 and "Shaventra" is 5.71:1. Matching the number
   matched the wrong quantity.

   What carries the reference's presence is how tall the mark stands, not
   how far across it reaches. At 83% wide this one is 14.5vw tall — the
   reference's height exactly — and spans 9%→91%. The stage is that plus
   the reference's own 3vw of clear space beneath. */
/* No max-height. The mask is sized in % of the stage's width, so it grows
   with the viewport whether or not the stage is allowed to — a ceiling here
   crops the mark on a wide screen instead of containing it. The reference
   caps neither, for the same reason. */
/* The ONLY place this stage is sized, and there is no max-height on
   purpose. An earlier revision capped it at 300px; the mask is a
   percentage of the stage's WIDTH, so it kept growing while the box
   stopped, and past 1667px the mark was cropped through the top of its
   own letterforms — 81px of it at 2174, which is what made "Shaventra"
   read as "Snaventra". A pixel ceiling on a vw-driven mask does not
   contain it, it slices it. */
.foot-stage { height: 18vw; max-height: none; }   /* 14.5 mark + 3 clear + .5 headroom */
.foot-stage-base, .foot-stage-lit {
  -webkit-mask: url('assets/wordmark-word.webp') no-repeat center bottom 3vw / 83% auto;
          mask: url('assets/wordmark-word.webp') no-repeat center bottom 3vw / 83% auto;
}
/* the spotlight grows back with the mark, keeping the reference's ratio
   of light to letter */
.foot-stage-spot {
  -webkit-mask-image: radial-gradient(circle 8.5vw at var(--x, -999px) var(--y, -999px), #000 50%, transparent 130%);
          mask-image: radial-gradient(circle 8.5vw at var(--x, -999px) var(--y, -999px), #000 50%, transparent 130%);
}

/* ── no rules inside the footer ───────────────────────────────────────
   The reference draws none: it separates its blocks with space alone, and
   a hairline between them is the thing that makes a footer read as a
   stack of panels rather than one column. Two survived — one under the
   call to action, one over the bar — plus the <hr> that has now gone from
   the markup. The button keeps its own border; that is a control, not a
   divider. */
.foot .foot-cta { border-bottom: 0; }
.foot .foot-bar { border-top: 0; }
.foot .foot-rule, .foot hr { display: none; }
.foot .foot-legalrow { border: 0; }

/* the bar is one centred row again, now that it holds only the legal links */
.foot-bar { display: flex; justify-content: center; padding-block: 0; }

/* ── the page/footer division ─────────────────────────────────────────
   The reference draws no border here — it does not need one, because its
   page ground is #FAFAFA and its footer is #FFFFFF, so the boundary is a
   colour change. This page's ground is already white, so that mechanism
   has nothing to show: the footer began mid-air with no edge at all.

   A hairline is what the colour change was doing there. It sits at the
   very top of the footer, above the call to action, so the division falls
   exactly where the footer starts. */
.foot { border-top: 1px solid #E7E7E7; }

/* ── the footer in Host Grotesk ───────────────────────────────────────
   The spec's structural face, applied to everything structural. The
   accents stay on the display serif, which is what it asks for.

   One problem, and it is in the font file rather than in the CSS: the
   @font-face declares `font-weight: 300 800`, but the woff2 has no fvar
   table — it is a single static instance, so a browser maps every weight
   in that range onto the same outlines and synthesises nothing. Measured:
   400 through 800 all render at 254.8px where the current face moves
   229.3 -> 240. Applied naively, the 700 column headings, the 600 legal
   row and the 400 links would all come out identical, and the reference's
   hierarchy is built on exactly that difference.

   So the weight is drawn rather than selected: a hairline stroke in the
   text's own colour thickens the glyph the way a heavier cut would. It is
   the same workaround this codebase already uses for Host Grotesk
   elsewhere. The real fix is a font file with the weights in it. */
.foot :is(.foot-cta h2, .foot-cta .lede, .cta-pill, .foot-cols h4,
          .foot-cols a, .foot-legalrow a, .foot-legalrow span) {
  font-family: 'Host Grotesk', ui-sans-serif, system-ui, sans-serif !important;
}
/* the two roles the reference sets above 500 */
.foot .foot-cols h4 { -webkit-text-stroke: .55px currentColor; }
.foot .foot-legalrow a { -webkit-text-stroke: .35px currentColor; }
.foot .foot-cta h2 { -webkit-text-stroke: .3px currentColor; }

/* The serif accent has real weights of its own, so it must not inherit the
   stroke that stands in for Host Grotesk's missing ones — on a face that
   can already draw the weight, a stroke on top just smears it. */
.foot .foot-cta h2 em, .foot .foot-connect-h { -webkit-text-stroke: 0; }

/* the legal row is the heaviest text down here, per the reference */
.foot .foot-legalrow a { font-weight: 600 !important; }

/* ══ THE FOOTER MARK, GIVEN DEPTH AND A PULSE ═════════════════════════
   It was one flat tint that did nothing until a pointer arrived — and on
   a touch device, nothing ever. Three layers now, and they are the same
   material lit three ways rather than three effects stacked up.

   1. THE BASE is no longer a single grey. A vertical gradient across the
      letterforms, light at the cap line and a step darker at the baseline,
      gives the mark a surface instead of a silhouette. Two values, four
      points apart — enough to read as dimension, not as a gradient.

   2. THE IDLE SWEEP is the brand gradient under a band of light that
      travels across the mark on its own, slowly, at low strength. This is
      what makes it alive before anyone touches it, and it is the only part
      a touch device ever sees. 14 seconds, so it reads as drift rather
      than as an animation asking to be watched.

   3. THE SPOTLIGHT is unchanged: full strength, tracked 1:1. Arriving with
      a pointer now feels like taking hold of a light that was already
      moving, which is the whole point of keeping the idle layer weak.

   The sweep needs two masks — the wordmark and the travelling band — so it
   nests, exactly as the spotlight does. */
.foot-stage-base {
  background: linear-gradient(180deg, #F4F4F4 0%, #E8E8E8 100%);
}
.foot-stage-sweep, .foot-stage-idle {
  position: absolute; inset: 0; display: block; pointer-events: none;
}
.foot-stage-idle {
  background: linear-gradient(96deg, #ED9C00 0%, #B1060F 62%, #8A0007 100%);
  opacity: .22;
  -webkit-mask: url('assets/wordmark-word.webp') no-repeat center bottom 3vw / 83% auto;
          mask: url('assets/wordmark-word.webp') no-repeat center bottom 3vw / 83% auto;
}
/* the travelling band. 34% wide against a 200% track, so the lit part is
   always a slice and the mark is never washed through all at once. */
.foot-stage-sweep {
  -webkit-mask-image: linear-gradient(100deg, transparent 34%, #000 50%, transparent 66%);
          mask-image: linear-gradient(100deg, transparent 34%, #000 50%, transparent 66%);
  -webkit-mask-size: 200% 100%; mask-size: 200% 100%;
  -webkit-mask-repeat: no-repeat; mask-repeat: no-repeat;
}
@media (prefers-reduced-motion: no-preference) {
  .foot-stage-sweep {
    animation: foot-mark-sweep 14s linear infinite;
  }
}
@keyframes foot-mark-sweep {
  from { -webkit-mask-position: 140% 0; mask-position: 140% 0; }
  to   { -webkit-mask-position: -40% 0; mask-position: -40% 0; }
}
/* Reduced motion keeps the depth and the pointer light, and simply parks
   the sweep out of frame — the mark is decoration, so the honest response
   to "no motion" is less motion, not a frozen highlight sitting on it. */
@media (prefers-reduced-motion: reduce) {
  .foot-stage-sweep { -webkit-mask-position: 200% 0; mask-position: 200% 0; }
}


/* ══ OUTCOMES — "What the work produces" ══════════════════════════════
   Five cards on a warm white ground. The composition is asymmetric on
   purpose: an editorial image card holding the left third at full height,
   a signature figure and a stated principle across the top, the method and
   the closing count beneath. Cards differ in weight rather than in
   decoration — one is crimson, one carries a photograph, the rest are
   white with a hairline — so the eye is led by hierarchy and not by
   ornament.

   The detail work is meant to be found, not noticed: hairline rules, a
   6px accent tick under each heading, contour lines under the crimson,
   concentric rings behind the count, a dot field in the corner of the
   image card. Nothing above 12% opacity except the crimson itself.

   TYPE. The serif carries every figure, upright, and appears italic only
   in the two emphasised phrases. That split is the whole typographic
   system here: upright serif means a measured quantity, italic serif
   means an editorial voice, and the sans carries everything read rather
   than counted. */
.ob {
  --ob-ink: #1A1A1A;
  --ob-body: #1C1C1C;
  --ob-quiet: #262626;
  --ob-line: #ECEAE7;
  --ob-card: #FFFFFF;
  --ob-warm: #FBFAF9;
  --ob-red: #B1060F;
  --ob-red-deep: #4B0306;
  margin-top: clamp(56px, 7vw, 104px);
}

/* ── header ───────────────────────────────────────────────────────── */
.ob-head {
  display: flex; flex-direction: column; align-items: center; text-align: center;
  max-width: 1440px; margin-inline: auto; padding: 0 clamp(16px, 4vw, 40px);
}
.ob-pill {
  display: inline-flex; align-items: center; gap: 8px; margin: 0;
  border-radius: 999px; border: 1px solid #F1EEEC;
  background: linear-gradient(#FDFCFB, #FFFFFF);
  box-shadow: 0 1px 2px rgba(26, 20, 18, .03);
  padding: 7px 18px 7px 14px;
  font-size: 11.5px; font-weight: 600; letter-spacing: .13em; text-transform: uppercase;
  color: var(--ob-red);
}
.ob-pill i { display: block; width: 13px; height: 13px; }
.ob-pill svg { width: 100%; height: 100%; display: block; }
.ob-h {
  margin: clamp(14px, 1.6vw, 22px) 0 0; color: var(--ob-ink);
  font-size: clamp(28px, 3.1vw, 46px); font-weight: 400; line-height: 1.14;
  letter-spacing: -.022em; text-wrap: balance;
}
.ob-h em {
  font-family: var(--font-serif); font-style: italic; font-weight: 400;
  color: var(--ob-red); padding-right: 4px; letter-spacing: -.01em;
}
.ob-sub {
  margin: clamp(10px, 1.1vw, 15px) 0 0; max-width: 52ch;
  font-size: clamp(15px, 1.15vw, 17px); line-height: 1.6; color: var(--ob-body);
}
/* the diamond rule: two hairlines meeting on a rotated square */
.ob-rule {
  display: block; position: relative; width: 168px; height: 1px;
  margin-top: clamp(20px, 2.2vw, 30px);
  background: linear-gradient(90deg, transparent, #E3DEDB 22%, #E3DEDB 78%, transparent);
}
.ob-rule::after {
  content: ""; position: absolute; left: 50%; top: 50%;
  width: 5px; height: 5px; margin: -3px 0 0 -3px;
  background: var(--ob-red); transform: rotate(45deg);
}

/* ── grid ─────────────────────────────────────────────────────────── */
.ob-bento {
  display: none; max-width: 1188px; margin: clamp(30px, 3.6vw, 52px) auto 0;
  padding-inline: clamp(14px, 2vw, 24px); gap: 16px;
}
@media (min-width: 768px)  { .ob-bento { display: flex; flex-direction: column; } }
@media (min-width: 1180px) { .ob-bento { flex-direction: row; align-items: stretch; } }
.ob-col { display: flex; flex-direction: column; gap: 16px; flex: 1; min-width: 0; }
/* The bento stretches .ob-a and .ob-col to the same height, and it did —
   both 563. But the two rows inside the column added up to 495, so 68px
   of it went unused and the right-hand cards stopped short of the left
   one's foot. The rows grow into that space now, and the cards inside
   them stretch by default, so all five end on the same line. */
.ob-row { display: flex; gap: 16px; min-width: 0; flex: 1 1 auto; }
@media (max-width: 1023px) { .ob-row { flex-direction: column; } }

/* shared card surface — a hairline and a shadow soft enough to read as
   depth rather than as a drop */
.ob-a, .ob-c, .ob-d, .ob-e {
  position: relative; border-radius: 20px; background: var(--ob-card);
  border: 1px solid var(--ob-line); overflow: hidden;
  box-shadow: 0 1px 2px rgba(26, 20, 18, .03), 0 12px 28px -20px rgba(26, 20, 18, .10);
}

/* the 6px accent tick that opens every heading */
.ob-tick { display: block; width: 26px; height: 2px; background: var(--ob-red); border-radius: 2px; }

/* icon containers: three weights — solid on the image card, ringed on
   white, and inset on the crimson */
.ob-ic {
  display: grid; place-items: center; flex: none;
  width: 46px; height: 46px; border-radius: 13px; color: var(--ob-red);
}
.ob-ic svg { width: 21px; height: 21px; display: block; }
.ob-ic-sm { width: 38px; height: 38px; border-radius: 11px; }
.ob-ic-sm svg { width: 17px; height: 17px; }
.ob-ic-ring {
  border: 1px solid #F0E7E7; background: linear-gradient(160deg, #FEF8F8, #FFFFFF);
  box-shadow: inset 0 1px 0 #FFFFFF, 0 1px 2px rgba(177, 6, 15, .05);
}
.ob-ic-solid {
  color: #FFFFFF; background: linear-gradient(150deg, #C4121C, var(--ob-red) 60%, #8A0007);
  box-shadow: 0 6px 16px -8px rgba(177, 6, 15, .55);
}
.ob-ic-onred {
  color: #FFFFFF; border: 1px solid rgba(255, 255, 255, .26);
  background: rgba(255, 255, 255, .12);
  -webkit-backdrop-filter: blur(6px); backdrop-filter: blur(6px);
}

/* ── A — the editorial image card ─────────────────────────────────── */
.ob-a { display: flex; flex-direction: column; min-width: 0; }
@media (min-width: 1180px) { .ob-a { width: 330px; flex: none; } }
.ob-a-img { position: relative; margin: 0; overflow: hidden; aspect-ratio: 330 / 300; }
/* 330/300 is the ratio of a 330px-wide card, and above 1180 that is what
   this is. Between 768 and 1179 the bento stacks into one column, so the
   card becomes the whole rail and the same ratio turned a 330px photo
   into a 785px one 714 tall — a near-square slab most of a tablet screen
   deep. Full width it wants a landscape crop, not a portrait one. */
@media (min-width: 768px) and (max-width: 1179px) {
  /* capped as well as re-cropped: a ratio alone still grows with the
     rail, and by 1100 the same 21/9 was 1054x452 — a banner half a
     laptop deep. The cap holds it at a constant 340 across the range,
     so the card reads the same at 800 as at 1150. */
  .ob-a-img { aspect-ratio: 21 / 9; max-height: 340px; }
}
.ob-a-img img {
  display: block; width: 100%; height: 100%; object-fit: cover; object-position: center;
  /* the plate is warmed and quietened so the type below leads */
  filter: saturate(.72) contrast(1.04) brightness(1.03);
}
/* a warm veil and a soft foot, so the photograph resolves into the card
   rather than stopping at an edge */
.ob-a-veil {
  position: absolute; inset: 0;
  background: linear-gradient(190deg, rgba(255,251,249,.10) 0%, rgba(255,255,255,0) 42%,
              rgba(255,255,255,.86) 92%, #FFFFFF 100%);
}
.ob-a-tx { position: relative; display: flex; flex-direction: column; padding: 0 24px 26px; }
.ob-a-tx .ob-ic { margin: -30px 0 18px; position: relative; z-index: 1; }
.ob-a-n { margin: 0; }
.ob-a-n b {
  font-family: var(--font-serif); font-style: normal; font-weight: 400;
  font-size: clamp(38px, 3.3vw, 48px); line-height: 1; letter-spacing: -.012em;
  color: var(--ob-red);
}
.ob-a-n b span { font-size: .68em; }          /* the + rides smaller */
.ob-a-h {
  margin: 8px 0 0; font-size: 19px; font-weight: 600; line-height: 1.25;
  letter-spacing: -.012em; color: var(--ob-ink);
}
.ob-a-tx .ob-tick { margin: 14px 0 0; }
.ob-a-p { margin: 14px 0 0; font-size: 14.5px; line-height: 1.62; color: var(--ob-body); }
.ob-dots { position: absolute; right: 18px; bottom: 20px; width: 78px; height: 52px; opacity: .5; }

/* ── B — the signature figure ─────────────────────────────────────── */
.ob-b {
  position: relative; flex: none; border-radius: 20px; overflow: hidden; min-width: 0;
  background:
    radial-gradient(120% 140% at 12% 0%, #C8121D 0%, transparent 58%),
    linear-gradient(152deg, #B1060F 0%, #7C0410 54%, #4B0306 100%);
  box-shadow: 0 14px 34px -22px rgba(122, 4, 16, .7);
}
@media (min-width: 1024px) { .ob-b { width: 40%; } }
.ob-contour { position: absolute; inset: 0; width: 100%; height: 100%; }
/* the internal glow: soft, top-left, well under the contours */
.ob-glow {
  position: absolute; left: -12%; top: -34%; width: 74%; height: 116%;
  border-radius: 999px; filter: blur(46px);
  background: radial-gradient(closest-side, rgba(255, 214, 190, .30), transparent 72%);
}
.ob-b-body {
  position: relative; z-index: 1; height: 100%;
  display: flex; align-items: flex-start; gap: 16px; padding: 26px 24px;
}
.ob-b-tx { min-width: 0; }
.ob-b-n {
  margin: 0; font-family: var(--font-serif); font-style: normal; font-weight: 400;
  font-size: clamp(46px, 4vw, 58px); line-height: .96; letter-spacing: -.015em; color: #FFFFFF;
}
.ob-b-n span { font-size: .62em; }
.ob-b-cap {
  margin: 12px 0 0; font-size: 14.5px; line-height: 1.5; font-weight: 500;
  color: rgba(255, 255, 255, .90);
}

/* ── C — the stated principle ─────────────────────────────────────── */
.ob-c { flex: 1; min-width: 0; display: flex; align-items: flex-start; gap: 18px; padding: 26px 26px 28px; }
.ob-c-tx { min-width: 0; }
.ob-c-h { margin: 0; font-size: 19px; font-weight: 600; line-height: 1.25; letter-spacing: -.012em; color: var(--ob-ink); }
.ob-c .ob-tick { margin: 12px 0 0; }
.ob-c-p { margin: 13px 0 0; font-size: 14.5px; line-height: 1.62; color: var(--ob-body); }

/* ── D — the method and its evidence ──────────────────────────────── */
.ob-d { flex: 1; min-width: 0; display: flex; flex-direction: column; padding: 26px 26px 24px; background: linear-gradient(180deg, #FFFFFF 0%, var(--ob-warm) 100%); }
.ob-d-h { margin: 0; font-size: 20px; font-weight: 600; line-height: 1.25; letter-spacing: -.014em; color: var(--ob-ink); }
.ob-d-h em {
  font-family: var(--font-serif); font-style: italic; font-weight: 400;
  font-size: 1.12em; color: var(--ob-red); letter-spacing: -.01em;
}
.ob-d .ob-tick { margin: 12px 0 0; }
.ob-d-p { margin: 13px 0 0; font-size: 14.5px; line-height: 1.62; color: var(--ob-body); }
.ob-dash {
  display: block; height: 1px; margin: 20px 0 18px;
  background-image: repeating-linear-gradient(to right, #E3DEDB 0 5px, transparent 5px 11px);
}
.ob-d-stats { display: flex; align-items: flex-start; gap: 18px; margin-top: auto; }
.ob-stat { display: flex; align-items: flex-start; gap: 12px; flex: 1; min-width: 0; }
.ob-vr { flex: none; align-self: stretch; width: 1px; background: linear-gradient(#FFFFFF00, var(--ob-line) 22%, var(--ob-line) 78%, #FFFFFF00); }
.ob-stat-tx { display: flex; flex-direction: column; min-width: 0; }
.ob-stat-v {
  font-family: var(--font-serif); font-style: normal; font-weight: 400;
  font-size: 27px; line-height: 1; letter-spacing: -.01em; color: var(--ob-red);
}
.ob-stat-v b { font-weight: 400; }
.ob-stat-v b span { font-size: .68em; }
.ob-stat-l { margin-top: 5px; font-size: 15px; font-weight: 600; letter-spacing: -.01em; color: var(--ob-ink); }
.ob-stat-s { margin-top: 7px; font-size: 13px; line-height: 1.5; color: var(--ob-quiet); }

/* ── E — the closing count ────────────────────────────────────────── */
.ob-e {
  flex: none; min-width: 0; display: grid; place-items: center;
  padding: 26px 22px; text-align: center;
  background: radial-gradient(120% 90% at 50% 8%, #FEFAFA 0%, #FFFFFF 62%);
}
@media (min-width: 1024px) { .ob-e { width: 33%; } }
.ob-rings { position: absolute; left: 50%; top: 50%; width: 300px; height: 300px; transform: translate(-50%, -54%); }
/* A ping travelling out through the static rings rather than the rings
   themselves moving: they are already drawn at falling opacity from the
   centre outward, and animating that would either brighten a deliberate
   fade or dim strokes that are down at 1.8% alpha and would simply
   vanish. One circle grows from the innermost radius to the outermost
   and fades as it goes, and a second follows it half a cycle behind, so
   the figure reads as measuring something rather than sitting there.

   transform-box: view-box, or the origin resolves against the circle's
   own bounding box and the ping grows from its edge instead of the
   centre. */
.ob-ping {
  transform-box: view-box; transform-origin: 120px 120px;
  opacity: 0;
}
@media (prefers-reduced-motion: no-preference) {
  .ob-ping { animation: ob-ping 5.6s cubic-bezier(.33, 0, .3, 1) infinite; }
  .ob-ping-2 { animation-delay: 2.8s; }
}
@keyframes ob-ping {
  0%   { transform: scale(.25); opacity: 0; }
  14%  { opacity: 1; }
  100% { transform: scale(1); opacity: 0; }
}
.ob-e-body { position: relative; z-index: 1; display: flex; flex-direction: column; align-items: center; }
.ob-e .ob-tick { margin: 16px 0 0; }
.ob-e-n {
  margin: 12px 0 0; font-family: var(--font-serif); font-style: normal; font-weight: 400;
  font-size: clamp(44px, 3.6vw, 54px); line-height: 1; letter-spacing: -.012em; color: var(--ob-red);
}
.ob-e-cap { margin: 12px 0 0; font-size: 14px; line-height: 1.55; color: var(--ob-body); }

/* ── the closing rule ─────────────────────────────────────────────── */
/* Shown at every width. It was hidden below 768 with the bento, but the
   bento is a layout and this is a statement — the one on the page about
   what gets declared on assessed work. A phone is exactly where somebody
   checks that. */
.ob-note {
  display: flex; align-items: flex-start; gap: 13px;
  max-width: 1188px; margin: 16px auto 0;
  margin-inline: clamp(16px, 5vw, 28px);
  border: 1px solid var(--ob-line); border-radius: 18px; background: var(--ob-warm);
  padding: 16px 18px;
}
@media (min-width: 768px) { .ob-note { align-items: center; padding: 16px 22px; margin-inline: clamp(14px, 2vw, 24px); } }
@media (min-width: 1236px) { .ob-note { margin-inline: auto; } }
.ob-note p { margin: 0; font-size: 14.5px; line-height: 1.55; color: var(--ob-body); }
.ob-note b { font-weight: 600; color: var(--ob-ink); }

/* ── mobile: replaced, not reflowed ───────────────────────────────── */
.ob-mob { margin: 26px 0 0; padding: 0 clamp(16px, 5vw, 28px); list-style: none; display: grid; gap: 10px; }
@media (min-width: 768px) { .ob-mob { display: none; } }
.ob-mob li {
  display: grid; grid-template-columns: auto 1fr; align-items: baseline; column-gap: 14px;
  border: 1px solid var(--ob-line); border-radius: 16px; background: var(--ob-card);
  padding: 16px 18px; box-shadow: 0 1px 2px rgba(26, 20, 18, .03);
}
.ob-mob b {
  font-family: var(--font-serif); font-style: normal; font-weight: 400;
  font-size: 32px; line-height: 1; letter-spacing: -.012em; color: var(--ob-red);
}
.ob-mob b span { font-size: .68em; }
.ob-mob span { font-size: 15.5px; font-weight: 600; letter-spacing: -.01em; color: var(--ob-ink); }
.ob-mob em { grid-column: 2; font-style: normal; font-size: 13.5px; line-height: 1.5; color: var(--ob-body); margin-top: 3px; }

/* ── arrival ──────────────────────────────────────────────────────── */
@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    /* Every card used to rise from below. Nothing wrong with it, except
       that the reader has just come down from the hero, so five things
       moving up against that is five things moving the way the page
       already is — the arrival did not read as an arrival.

       They now come in from the three directions the hero is NOT: the
       tall card from the left edge it sits against, the right-hand pair
       in from the right, and only the middle pair from below. Each card
       enters from the side of the frame it lives on, so the bento
       assembles itself rather than sliding up in one block.

       Sideways travel is 34px against the rise's 18: horizontal motion
       across a wide card reads as far less than the same distance
       vertically, and at 18px it barely registered. It cannot widen the
       page — html and body are overflow-x: clip further down. */
    .ob-a, .ob-b, .ob-c, .ob-d, .ob-e, .ob-note, .ob-mob li {
      animation: ob-rise linear both; animation-timeline: view(); animation-range: entry 3% cover 22%;
    }
    .ob-a { animation-name: ob-in-left; }
    .ob-c, .ob-e { animation-name: ob-in-right; }

    .ob-b, .ob-mob li:nth-child(2) { animation-range: entry 6% cover 25%; }
    .ob-c, .ob-mob li:nth-child(3) { animation-range: entry 9% cover 28%; }
    .ob-d, .ob-mob li:nth-child(4) { animation-range: entry 12% cover 31%; }
    .ob-e, .ob-mob li:nth-child(5) { animation-range: entry 15% cover 34%; }
    .ob-note { animation-range: entry 18% cover 37%; }

    /* .ob-mob li is deliberately absent from both overrides above: the
       phone is one narrow column, and cards swinging in from the sides
       there reads as jitter rather than as assembly. It keeps the rise. */
  }
}
@keyframes ob-rise     { from { opacity: 0; transform: translateY(18px); } }
@keyframes ob-in-left  { from { opacity: 0; transform: translateX(-34px); } }
@keyframes ob-in-right { from { opacity: 0; transform: translateX(34px); } }

/* ══ WHO SHOULD WORK WITH US ══════════════════════════════════════════
   A crowd of identical faceless figures with exactly one of them in a
   crimson cap and gown, held still while the reader scrolls past it.

   THE PIN IS CSS. The range is the viewport plus the scroll distance and
   the panel sticks to the top of it, so the panel is held for exactly
   974px at every viewport height — the reference hard-codes its own
   819px panel there instead, which makes the pin longer on a tall screen
   and shorter on a short one. The script only reads how far through we
   are; it does not do the pinning.

   ONLY THE PICTURE MOVES. Across all 974px the eyebrow, the heading, the
   line under it and the seven chips do not shift by a pixel. That is the
   whole mechanism: one thing moving inside a still frame reads as the
   point, and a section where everything animates has no point.

   THE RESTING STATE IS THE PAYOFF, not the start — scale 1.14 and full
   saturation are what the CSS declares, and the script scrubs backwards
   from there. So with no JavaScript, under reduced motion, and below the
   pin breakpoint the reader gets the resolved image rather than a
   washed-out one waiting for a scroll that never comes. */
.ww { position: relative; margin-top: clamp(96px, 11vw, 200px); }

/* The pin is 120% OF THE PANEL, not a fixed 974px. Measured on the
   reference: a 536px panel pinned for 655px, and the spec's own 819/974
   is the same ratio at a wider viewport — ScrollTrigger `end: "+=120%"`,
   which is a percentage of the pinned element and therefore scales.

   Hard-coding the 974 stretched 31 frames over 178% of a 547px panel,
   which is one frame every 31px against the reference's 23. That gap is
   the whole of the steppiness; the frame count was never the problem
   (the reference paints 28 distinct frames, one fewer than this does).

   The script measures the panel and writes --ww-pin, so the distance it
   scrubs against and the distance the CSS reserves cannot disagree. */
.ww-range { height: calc(100dvh + var(--ww-pin, 660px)); }
.ww-pin {
  position: sticky; top: 0; height: 100dvh;
  display: flex; align-items: center; justify-content: center;
  /* the bar is sticky and opaque and never leaves, so the panel centres
     in what is left of the viewport rather than sliding under it */
  padding-top: var(--hdr);
  overflow: hidden;
}
.ww-panel { position: relative; width: 100%; max-width: 1440px; margin-inline: auto; }

/* ── the header block ─────────────────────────────────────────────── */
.ww-head { display: flex; width: 100%; flex-direction: column; align-items: center; text-align: center; }
.ww-eyebrow {
  margin: 0; border-radius: 36px; border: 1.5px solid var(--line-soft);
  background: linear-gradient(180deg, #F3F3F3, #FFFFFF);
  padding: 6px 16px; font-size: 14px; line-height: 21px; color: var(--muted);
}
.ww-h {
  margin: 12px 0 0; font-size: 32px; font-weight: 500; line-height: 48px;
  letter-spacing: -.01em; color: var(--ink); text-wrap: balance;
}
/* the site's own emphasis treatment: the display serif, italic, in the
   brand red. The reference's #B1060F is a second red a hair off this
   one, and two nearly-identical reds on a page read as a mistake rather
   than as a palette — so the gradient keeps its shape (lit at both ends,
   dark through the middle) and takes the brand's hue instead. */
.ww-h em {
  font-family: var(--font-serif); font-style: italic; padding-right: 4px;
  background: linear-gradient(90deg, #AE0E20, #8A0007, #AE0E20);
  -webkit-background-clip: text; background-clip: text; color: transparent;
  -webkit-box-decoration-break: clone; box-decoration-break: clone;
}
.ww-sub {
  /* 791px is what this sentence measures on one line; a ch-based cap
     resolved to 724 and broke it in two for no reason */
  margin: 8px 0 0; max-width: 800px; font-size: 18px; line-height: 27px;
  color: var(--muted); text-wrap: balance;
}

/* ── the stage ────────────────────────────────────────────────────── */
.ww-stage { position: relative; margin-top: 30px; display: flex; width: 100%; justify-content: center; }
/* sized in VIEWPORT units on purpose: the frame tracks the window and
   ignores the 1440 rail, which is what keeps it and the chip row locked */
.ww-media { position: relative; width: 75vw; max-width: 1440px; aspect-ratio: 1165 / 541; overflow: hidden; }
/* The camera move is in the frames — the rows parallax against each
   other and the cap descends onto the head, which is real 3D motion and
   not something a scale on one still can fake. So this element carries
   no transform of its own; the script swaps which frame is in it.

   The src in the markup is the LAST frame, so no JavaScript, reduced
   motion and every width below the pin all land on the resolved image
   rather than the opening one. */
/* Two of them, stacked. 31 frames over a 656px pin is a step every
   21px, and a step is what the eye reads as chunky however fine the
   pin is. So the frame after the current one sits on top at the
   fraction between them: at 0.4 of the way from frame 12 to 13 the
   picture is 60% of 12 and 40% of 13, and the change is continuous
   rather than held-then-jumped. Costs nothing — no extra files, no
   extra requests, two composited layers the GPU was going to draw
   anyway. */
.ww-crowd {
  position: absolute; inset: 0;
  display: block; width: 100%; height: 100%; object-fit: cover;
  /* Anchored to the TOP of the frame, not its middle. The source is 3:2
     and this box is 2.15:1, so a third of the height is cropped either
     way — the question is which third. Centred, the crop began at source
     y=78 and the mortarboard's topmost pixel is at y=76: it was clipped,
     and the 71px of source under the top fade washed out the rest of it.
     Anchored to the top, the cap lands 107px into the box with the fade
     ending at 100, so it clears. What is given up is the bottom of the
     front row, which the bottom fade and the chip row were covering. */
  object-position: center top;
}
.ww-crowd-b { opacity: 0; }

/* Four fades, each a fifth of the box, dissolving the picture into the
   page so it does not read as a cropped rectangle. Top and bottom
   overhang by 4 and 8px because subpixel rounding leaves a hairline of
   un-faded image along a horizontal edge; the vertical seams read far
   less, so left and right sit flush. */
.ww-fade { position: absolute; z-index: 10; pointer-events: none; }
.ww-fade-t { inset-inline: 0; top: -4px;    height: 20%; background: linear-gradient(180deg, var(--bg), transparent); }
.ww-fade-b { inset-inline: 0; bottom: -8px; height: 20%; background: linear-gradient(0deg,   var(--bg), transparent); }
.ww-fade-l { inset-block: 0;  left: 0;      width: 20%;  background: linear-gradient(90deg,  var(--bg), transparent); }
.ww-fade-r { inset-block: 0;  right: 0;     width: 20%;  background: linear-gradient(270deg, var(--bg), transparent); }

/* ── the chips ────────────────────────────────────────────────────── */
/* 75vw inside a 1440 rail: the row overhangs the container on both
   sides, deliberately — it is anchored to the frame, not to the text. */
.ww-chips {
  position: absolute; bottom: 0; left: 50%; transform: translateX(-50%); z-index: 20;
  display: flex; width: 75vw; flex-wrap: wrap; justify-content: center; gap: 20px;
  margin: 0; padding: 0; list-style: none;
}
.ww-chip {
  display: flex; align-items: center; gap: 8px;
  border-radius: 9999px; background: #FFFFFF;
  /* asymmetric — 13 left, 18 right, to balance the tick */
  padding: 9px 18px 9px 13px;
  box-shadow: 0 4px 6px -1px rgb(0 0 0 / .1), 0 2px 4px -2px rgb(0 0 0 / .1);
  white-space: nowrap; font-size: 16px; line-height: 24px; font-weight: 400;
  color: var(--muted);
}
/* a gradient-filled disc, not an icon: a flat circle loses the fall-off */
.ww-tick {
  flex: none; width: 20px; height: 20px; border-radius: 9999px; padding: 3px;
  display: grid; place-items: center;
  background: linear-gradient(180deg, #AE0E20, #4B0306);
}
.ww-tick svg { display: block; width: 14px; height: 14px; }

/* ── the chip row drops as the frame narrows ──────────────────────── */
/* At full width the chips overlap the crowd. As the frame shrinks there
   is no longer room for them inside it, so they step down below it. */
@media (max-width: 1149px) { .ww-chips { bottom: -70px; } }
@media (max-width: 1023px) { .ww-chips { bottom: -110px; } }
@media (max-width: 879px) {
  .ww-chips { bottom: -140px; }
  .ww-chip { padding: 11px 20px 11px 15px; font-size: 13px; }
  .ww-h { font-size: 28px; line-height: 40px; }
  .ww-sub { font-size: 16px; line-height: 24px; }
}

/* ── below the pin breakpoint, and under reduced motion ───────────── */
/* The reference hides the section outright here and ships a second copy
   of the same words for phones. One subtree that stops pinning says the
   same thing without handing every crawler and every screen reader the
   content twice — the picture already rests at its resolved state, so
   all that has to go is the pin itself. */
@media (max-width: 639px), (prefers-reduced-motion: reduce) {
  .ww-range { height: auto; }
  .ww-pin { position: static; height: auto; padding-top: 0; display: block; overflow: visible; }
  .ww-crowd { will-change: auto; }
}
/* ── the phone gets its own design, not a reflow ──────────────────── */
/* Measured off the reference at 375: it is not the desktop section made
   narrow. The band becomes a 427px-tall portrait crop with the graduate
   filling it, the chip row climbs 40px over that picture instead of
   sitting under it, the sub drops its last clause, and only four of the
   qualities are shown rather than all seven stacked into a column seven
   rows deep. All of that is this one subtree behaving differently — no
   second copy of the words. */
@media (max-width: 639px) {
  /* the stage is a flex ROW, and the chip row only stayed out of it by
     being absolutely positioned. Taking it back into flow made it a
     sibling of the picture and the two shared the width — the picture
     came out 128px wide. Stack them. */
  .ww-stage { flex-direction: column; align-items: center; margin-top: 20px; }

  .ww-head { padding: 50px 20px 0; }
  .ww-h { font-size: 28px; line-height: 115%; }
  .ww-sub { font-size: 16px; line-height: 123%; }
  /* 12 words on a phone, the same count the reference carries there */
  .ww-sub-more { display: none; }

  /* a tall crop, so the one figure in crimson is the size of the frame
     rather than a detail in a letterbox 174px deep */
  .ww-media { width: 100%; height: 427px; aspect-ratio: auto; }
  .ww-crowd { object-position: center; }

  /* Over the picture, not below it — and z-index needs a position to act
     on, so this stays relative rather than static.

     left and bottom have to be cleared with it. The desktop rule centres
     the row with `left: 50%` plus a -50% translate, and the narrow rule
     drops it with `bottom: -140px`; under `absolute` those place the box,
     but under `relative` they are offsets from where flow already put it.
     Cancelling only the transform left the row shifted 188px right — half
     the viewport — and 140px below the section it belongs to, which is
     how it came to be sitting on top of What we do. */
  .ww-chips {
    position: relative; z-index: 30;
    left: auto; bottom: auto; transform: none;
    width: 100%; margin-top: -40px; gap: 16px; padding-inline: 12px;
  }
  /* four, as the reference shows. Seven of these stacked is a column the
     reader scrolls past rather than a row they take in. */
  .ww-chip:nth-child(n+5) { display: none; }
}
/* ══ WHAT WE DO ═══════════════════════════════════════════════════════
   One section, three states: the three tracks, a transition, the deck.

   The base state — no class, no script — is the whole section readable:
   three tracks whose Explore controls are ordinary links to the pages
   they name, and the deck below them as a plain three-up grid carrying
   every paragraph and every deliverable. The script adds .is-js and only
   then does anything get hidden. Nothing here is content that exists at
   the mercy of a stage machine.

   The transition is the only part that is purely decoration, and it is
   marked as such: aria-hidden, no text, no focusable child, and removed
   from the tree whenever it is not playing. */
.wd {
  --wd-ease: cubic-bezier(.16, 1, .3, 1);
  /* ONE scale for the whole panel. The reference's ratios are fractions of
     the panel's width, so driving them from vw keeps them exact; taking
     the smaller of vw and its vh equivalent means a short screen shrinks
     the panel as a whole instead of overflowing the screen it is supposed
     to fit. At 1440x900 the two are equal, so nothing moves there. */
  --u: min(1vw, 1.62vh);
  /* asymmetric: the top has to clear the sticky header, the bottom does
     not, and the difference is what buys the panel its last 30px */
  --wd-pad-t: clamp(56px, calc(5.3 * var(--u)), 92px);
  --wd-pad-b: clamp(28px, calc(2.4 * var(--u)), 48px);
  --av-w: clamp(150px, 17.5vw, 300px);   /* her column; the panel yields exactly this + a gap */
  position: relative; overflow: clip;
  min-height: 100svh; display: grid; align-content: center;
  padding-block: var(--wd-pad-t) var(--wd-pad-b);
}
/* the header takes what it needs and the stages take the rest, so every
   stage is handed the same box and none of them has to guess a height */
.wd > .wrap {
  /* width:100% because .wrap centres with auto margins, and on a grid item
     auto margins beat stretch — without this the shell collapses to
     fit-content and the panel loses a third of its width */
  width: 100%;
  display: grid; grid-template-rows: auto minmax(0, 1fr);
  gap: clamp(10px, calc(1 * var(--u)), 26px);
  min-height: calc(100svh - var(--wd-pad-t) - var(--wd-pad-b));
}
/* Only once the script is driving do the stages share one cell. Without
   it two of them are on screen at once and stacking would bury one. */
/* NB .wd-body is the deck card's inner body — this container is
   .wd-stages, or it would inherit that card's padding */
.wd.is-js .wd-stages { display: grid; min-height: 0; position: relative; }
.wd.is-js .wd-stages > .wd-stage { grid-area: 1 / 1; min-height: 0; }
/* the stepper is centred in whatever height is left; the other two fill it */
.wd [data-wd-stage="tracks"] { display: grid; align-content: center; }
.wd:not(.is-js) .wd-stage + .wd-stage { margin-top: clamp(28px, 3.4vw, 46px); }
/* the header is centred over the two columns, as the reference has it —
   and as the section above it already is. It sits in the same height
   budget as the panel, so it answers to the same scale. */
.wd-head > .kicker, .wd-head > h2, .wd-head > .lede { text-align: center; }
.wd-head > h2 { font-size: clamp(26px, calc(3.2 * var(--u)), 48px); }
.wd-head > .lede { font-size: clamp(14px, calc(1.25 * var(--u)), 19px); }
/* the heading and lede both carry a max-width, so centring the text alone
   only centres it inside a box that is still hung on the left margin */
.wd-head > h2, .wd-head > .lede { margin-inline: auto; }

/* ── stage machinery ─────────────────────────────────────────────── */
.wd-stage { opacity: 1; }
.wd.is-js .wd-stage {
  opacity: 0; transform: translateY(12px);
  transition: opacity .42s ease, transform .5s var(--wd-ease);
}
.wd.is-js .wd-stage.is-on { opacity: 1; transform: none; }
/* !important because this has to beat every display rule below it, and
   one of them ties on specificity and wins on order: .wd.is-js
   .wd-deckwrap sets display:grid, which was re-displaying the hidden deck
   on top of the stepper. Invisible, because .wd-stage is opacity 0 — but
   still in the same grid cell, still hit-testable, and swallowing every
   click and hover meant for the stage underneath. `hidden` means hidden. */
.wd .wd-stage[hidden] { display: none !important; }

/* ── stage 1 · the stepper ───────────────────────────────────────────
   Measured off the reference rather than approximated. Three things carry
   the state, and they move together:

     the row     — a card of its own. Ahead and behind it lies flat on the
                   panel; current, it lifts onto white and grows a rail.
     the marker  — a filled disc, never an outline: white with a grey tick
                   for a step already read, white with its number for one
                   still ahead, and the brand red with a white tick for the
                   step being read now.
     the Explore — the same control at two strengths: a pale disc while the
                   row waits, solid while it is current.

   Proportions are taken from the reference as fractions of the panel
   rather than as pixels, so they hold at any width: marker ≈ 4.1% of the
   panel, title ≈ 1.9%, the rail 4px, the rows 8px apart. */
.wd-steps {
  display: grid; gap: clamp(14px, calc(2.15 * var(--u)), 31px);
  grid-template-columns: minmax(0, .58fr) minmax(0, 1fr);
  align-items: stretch;
  /* the reference panel's own proportion, kept at every viewport: it grows
     with the screen up to that shape and then stops, rather than stretching
     into a tall box the reference never had. Content still wins if it needs
     more, since aspect-ratio yields to min-content. */
  /* width first, or the stretched height becomes the definite axis and the
     ratio derives a width wider than the screen */
  width: 100%; min-width: 0;
  aspect-ratio: 1585 / 712; height: auto; max-height: 100%;
  border-radius: 28px;
  /* the panel is the ground the row cards sit on, so it is a shade off
     white — on a white section a white panel would have no depth to give */
  background: #F5F5F7;
  padding: clamp(13px, calc(1.95 * var(--u)), 28px);
  box-shadow: 0 1px 2px rgba(26, 19, 16, .03),
              0 30px 60px -42px rgba(26, 19, 16, .30);
}

/* the rows keep their own size and sit in the middle of a column that
   is now as tall as the screen, rather than stretching to fill it */
.wd-list { list-style: none; margin: 0; padding: 0;
  display: flex; flex-direction: column; justify-content: center; gap: 9px; }

.wd-step {
  position: relative; display: flex; align-items: flex-start;
  gap: clamp(11px, calc(1.35 * var(--u)), 19px);
  border-radius: 16px; overflow: hidden;
  padding: clamp(12px, calc(2.06 * var(--u)), 30px);
  background: rgba(255, 255, 255, .72);
  transition: background .45s ease, box-shadow .45s ease, opacity .45s ease;
}
/* ── the vein ────────────────────────────────────────────────────────
   The artwork is a single supplied frame — vessels and their glowing
   nodes already in it — laid down the left of every card and multiplied
   into the paper, which is what drops its white ground without needing an
   alpha channel it does not have.

   THE GROWTH is the point of it. A raster cannot be stroke-dashed, so the
   reveal is a radial mask anchored at the left edge whose radius is
   animated: the network appears out of the trunk and spreads outward to
   the tips, the way one actually grows, rather than being wiped in from
   the side. --wd-grow has to be registered with @property or it is a
   string to the animation engine and would jump from 0 to full in one
   frame instead of interpolating. */
@property --wd-grow {
  syntax: '<percentage>';
  inherits: false;
  initial-value: 0%;
}

.wd-vein {
  position: absolute; left: 0; top: 0; bottom: 0;
  width: 30%; min-width: 104px; max-width: 200px;
  pointer-events: none; z-index: 0; overflow: hidden;
  opacity: .55; transition: opacity .45s ease;
}
.wd-step.is-on .wd-vein { opacity: 1; }
.wd-vein i { position: absolute; inset: 0; display: block; }

.wd-vein-glow {
  background: radial-gradient(102% 78% at -6% 50%,
    rgba(226, 40, 64, .30), rgba(226, 40, 64, .09) 44%, rgba(226, 40, 64, 0) 76%);
}
.wd-step.is-on .wd-vein-glow {
  background: radial-gradient(110% 84% at -6% 50%,
    rgba(226, 40, 64, .46), rgba(226, 40, 64, .14) 46%, rgba(226, 40, 64, 0) 78%);
}

.wd-vein-tree, .wd-vein-lume {
  /* The artwork's vessels occupy its left half and it is empty to the
     right, so at natural scale they would stop halfway across this panel.
     192% is that half blown up to the panel's full width — the vessels
     then run the whole way to the text, and because the height is auto
     nothing is stretched: it simply overflows top and bottom and is
     centred, which costs a little of the artwork and no geometry. */
  background: url('assets/veins.webp') left center / 192% auto no-repeat;
  /* the file carries a real alpha channel, so it is composited straight:
     multiplying it into the paper would have killed the white glow at its
     nodes, which is the best thing in the artwork */
  --wd-grow: 134%;
  -webkit-mask-image:
    radial-gradient(126% 112% at -8% 50%, #000 var(--wd-grow),
                    transparent calc(var(--wd-grow) + 13%)),
    linear-gradient(90deg, #000 78%, transparent 100%);
  -webkit-mask-composite: source-in;
  mask-image:
    radial-gradient(126% 112% at -8% 50%, #000 var(--wd-grow),
                    transparent calc(var(--wd-grow) + 13%)),
    linear-gradient(90deg, #000 78%, transparent 100%);
  mask-composite: intersect;
}

/* the surge: the same artwork again, brighter, behind a band that runs
   out along the growth — so the intensification follows the vessels
   instead of drifting over them as a blob would */
.wd-vein-lume {
  filter: brightness(.72) saturate(1.5);
  --wd-grow: 40%;
  opacity: 0;
}

@media (prefers-reduced-motion: no-preference) {
  /* long enough to be watched, and eased so the front races out of the
     trunk and then slows as the capillaries fill — which is the shape of
     the thing it is imitating */
  .wd-vein-tree.is-growing { animation: wd-vein-grow 2.4s cubic-bezier(.12, .62, .24, 1) both; }
  .wd-vein-lume { animation: wd-vein-surge 5.2s cubic-bezier(.45, 0, .55, 1) infinite; }
  .wd-step.is-on .wd-vein-lume { animation-duration: 3.6s; }
  .wd-vein b {
    animation: wd-vein-lume 3.6s ease-in-out infinite;
    animation-delay: calc(var(--t, 0) * 1s);
  }
}
@keyframes wd-vein-grow { from { --wd-grow: 0%; } to { --wd-grow: 134%; } }
@keyframes wd-vein-surge {
  0%   { --wd-grow: 0%;   opacity: 0; }
  18%  { opacity: .8; }
  72%  { opacity: .8; }
  100% { --wd-grow: 118%; opacity: 0; }
}

/* the artwork carries its own nodes; these are the few that flare */
.wd-vein b {
  position: absolute; left: var(--x); top: var(--y);
  width: 5px; height: 5px; margin: -2.5px 0 0 -2.5px; border-radius: 999px;
  background: #FFFFFF; opacity: 0;
  box-shadow: 0 0 7px 2px rgba(255, 96, 118, .95),
              0 0 18px 7px rgba(226, 40, 64, .5);
}
@keyframes wd-vein-lume {
  0%, 100% { opacity: 0; transform: scale(.4); }
  46%      { opacity: 1; transform: scale(1); }
}

/* ── the marker ──────────────────────────────────────────────────── */
.wd-mark {
  flex: none; display: grid; place-items: center;
  width: clamp(34px, calc(3.85 * var(--u)), 56px); aspect-ratio: 1; border-radius: 999px;
  background: #FFFFFF; color: var(--faint);
  font-size: clamp(12px, calc(1.4 * var(--u)), 20px); font-weight: 500;
  font-variant-numeric: tabular-nums;
  box-shadow: 0 1px 2px rgba(26, 19, 16, .06), 0 6px 14px -8px rgba(26, 19, 16, .34);
  transition: background .35s ease, color .35s ease, box-shadow .35s ease;
}
.wd-mark i { display: none; width: 44%; height: 44%; }
.wd-mark svg { width: 100%; height: 100%; }
/* read already: the tick takes the number's place */
.wd-step.is-done .wd-mark b, .wd-step.is-on .wd-mark b { display: none; }
.wd-step.is-done .wd-mark i, .wd-step.is-on .wd-mark i { display: grid; place-items: center; }
.wd-step.is-done .wd-mark { color: var(--ink); }
/* being read: the one filled marker on the panel */
.wd-step.is-on .wd-mark {
  background: var(--brand); color: var(--on-brand);
  box-shadow: 0 2px 4px rgba(174, 14, 32, .22), 0 10px 20px -8px rgba(174, 14, 32, .55);
}

/* ── the words ───────────────────────────────────────────────────── */
.wd-mark, .wd-stext { position: relative; z-index: 1; }
.wd-stext { min-width: 0; }
.wd-t { margin: 0; font-size: clamp(16px, calc(1.77 * var(--u)), 26px); font-weight: 500;
  letter-spacing: -.018em; color: var(--ink); }
.wd-lead { margin: calc(.55 * var(--u)) 0 0; color: var(--muted); font-size: clamp(12.5px, calc(1.12 * var(--u)), 16.5px);
  line-height: 1.5; }

/* ── Explore: one control, two strengths ─────────────────────────── */
.wd-go {
  margin-top: calc(.9 * var(--u)); display: inline-flex; align-items: center; gap: 10px;
  font-size: clamp(11.5px, calc(.95 * var(--u)), 13.5px); font-weight: 600;
  color: var(--muted); text-decoration: none;
  transition: color .35s ease;
}
.wd-go i {
  display: grid; place-items: center; width: clamp(22px, calc(2.12 * var(--u)), 31px);
  aspect-ratio: 1; border-radius: 999px;
  /* waiting: the brand at a whisper, so the row still reads as one thing */
  background: rgba(174, 14, 32, .16); color: rgba(174, 14, 32, .55);
  transition: transform .38s var(--wd-ease), background .35s ease, color .35s ease;
}
.wd-go svg { width: 46%; height: 46%; }
.wd-step.is-on .wd-go { color: var(--ink); }
.wd-step.is-on .wd-go i { background: var(--brand); color: var(--on-brand); }
.wd-go:hover i, .wd-go:focus-visible i { transform: translateX(4px); }

/* ── the still ───────────────────────────────────────────────────── */
.wd-shots {
  position: relative; border-radius: 18px; overflow: hidden; background: #E9E7E4;
  min-height: min(46vh, 300px); perspective: 1100px;
  box-shadow: 0 2px 6px rgba(26, 19, 16, .06), 0 26px 50px -30px rgba(26, 19, 16, .44);
}
.wd-still { position: absolute; inset: 0; margin: 0; opacity: 0; }
.wd-still img { display: block; width: 100%; height: 100%; object-fit: cover; }
/* entering from beneath and tipping upright; leaving upward — the whole of
   the reference's image transition */
.wd.is-js .wd-still {
  transform: translateY(58px) rotateX(-16deg); transform-origin: 50% 100%;
  transition: opacity .5s ease, transform .55s var(--wd-ease);
}
.wd-still.is-on { opacity: 1; }
.wd.is-js .wd-still.is-on { transform: none; }
.wd.is-js .wd-still.is-out { opacity: 0; transform: translateY(-58px) rotateX(16deg); }
.wd-shots-fade { display: none; }

/* ── stage 2 · the transition ────────────────────────────────────────
   Twenty cards at 60x85, the size the component uses; everything that
   moves them is written by the script as one transform per frame, because
   the motion is a spring and a spring is not a CSS transition. The only
   thing CSS animates here is the flip, which is a hover state and belongs
   in a stylesheet. */
/* full-bleed: the fan is wider than the box on purpose, and the cut has
   to land at the edge of the screen the way it does in the component —
   cutting it at the panel's gutters instead reads as a mistake */
.wd-morph { position: relative; width: 100vw; margin-inline: calc(50% - 50vw); }
.wd-field {
  position: relative; height: 100%; min-height: 0;
  overflow: hidden; perspective: 1000px;
  cursor: grab;
  /* pan-y, not none: the touchmove handler cancels the gesture only while
     it still has virtual scroll to spend, and with touch-action:none the
     browser would refuse to scroll even when it does not — leaving a
     phone stuck on the ring with no way back up the page */
  touch-action: pan-y;
}

.wd-chip {
  position: absolute; left: 50%; top: 50%;
  width: 60px; height: 85px; margin: -42.5px 0 0 -30px;
  transform-style: preserve-3d;
  opacity: 0;              /* the script owns this from the first frame */
  will-change: transform, opacity;
}
.wd-chip-in {
  position: relative; width: 100%; height: 100%;
  transform-style: preserve-3d;
  transition: transform .6s cubic-bezier(.2, .9, .25, 1);
}
.wd-chip:hover .wd-chip-in { transform: rotateY(180deg); }
.wd-face, .wd-back {
  position: absolute; inset: 0; border-radius: 12px; overflow: hidden;
  -webkit-backface-visibility: hidden; backface-visibility: hidden;
  box-shadow: 0 1px 2px rgba(26, 19, 16, .1), 0 10px 22px -12px rgba(26, 19, 16, .55);
}
.wd-face { background: #EFEDEA; }
.wd-face img { display: block; width: 100%; height: 100%; object-fit: cover; }
/* the veil lifts on hover, so the card brightens as it turns */
.wd-face::after {
  content: ''; position: absolute; inset: 0; background: rgba(0, 0, 0, .10);
  transition: background .3s ease;
}
.wd-chip:hover .wd-face::after { background: transparent; }
.wd-back {
  transform: rotateY(180deg);
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 2px; padding: 8px; text-align: center;
  background: #14100C; border: 1px solid #2E2A25;
}
.wd-back b {
  font-size: 8px; font-weight: 700; letter-spacing: .18em; text-transform: uppercase;
  color: #E2566B;
}
.wd-back i { font-style: normal; font-size: 11px; font-weight: 500; color: #FFFFFF; }

/* ── the two things the transition says ──────────────────────────── */
.wd-intro, .wd-arcnote {
  position: absolute; left: 50%; transform: translateX(-50%);
  width: min(560px, 88%); text-align: center; pointer-events: none;
}
/* the intro sits inside the ring, and takes its measure from it: the
   script publishes the ring's inner width as --ring-w */
.wd-intro { top: 50%; margin-top: -2.5em; z-index: 0;
  width: min(560px, var(--ring-w, 560px)); }
.wd-arcnote { top: 6%; z-index: 3; }
.wd-intro-h {
  margin: 0;
  /* scaled from the ring, not the viewport, so it never outgrows the
     circle it is standing in */
  font-size: clamp(15px, calc(var(--ring-w, 560px) * .062), 34px);
  font-weight: 500; letter-spacing: -.02em; color: var(--ink);
}
.wd-intro-s {
  /* it stands inside the ring too, so it is measured from the ring as
     well — a fixed size touched the chips once the circle got small */
  margin: calc(.06 * var(--ring-w, 560px)) 0 0;
  font-size: clamp(8px, calc(var(--ring-w, 560px) * .021), 11.5px);
  font-weight: 700;
  letter-spacing: .2em; text-transform: uppercase; color: var(--faint);
}
.wd-arcnote-h {
  margin: 0; font-size: clamp(26px, calc(3.4 * var(--u)), 48px);
  font-weight: 600; letter-spacing: -.025em; color: var(--ink);
}
.wd-arcnote-s {
  margin: 12px 0 0; font-size: clamp(14px, calc(1.15 * var(--u)), 17px);
  line-height: 1.6; color: var(--muted);
}

/* ── stage 3 · the deck ──────────────────────────────────────────── */
/* Without the script this is a plain grid of three readable cards. */
.wd-deck {
  display: grid; gap: clamp(12px, 1.4vw, 20px);
  grid-template-columns: repeat(3, minmax(0, 1fr));
}
.wd-card {
  display: flex; flex-direction: column; overflow: hidden;
  border: 1px solid var(--line); border-radius: 22px; background: #fff;
  box-shadow: 0 1px 2px rgba(26, 19, 16, .04), 0 22px 44px -30px rgba(26, 19, 16, .34);
}
.wd-shot { margin: 0; aspect-ratio: 16 / 10; overflow: hidden; background: #EFEDEA; }
.wd-shot img { display: block; width: 100%; height: 100%; object-fit: cover; }
.wd-body { display: flex; flex-direction: column; gap: 8px; padding: clamp(18px, 1.8vw, 26px); }
.wd-body p { margin: 0; color: var(--muted); font-size: 15px; line-height: 1.6; }
.wd-body .ticks { margin: 4px 0 0; }
.wd-link {
  margin-top: auto; padding-top: 14px;
  display: inline-flex; align-items: center; gap: 8px;
  font-size: 14px; font-weight: 600; color: var(--brand); text-decoration: none;
}
.wd-link i { display: grid; place-items: center; width: 16px; height: 16px; }
.wd-link svg { width: 100%; height: 100%; }
.wd-link:hover { text-decoration: underline; text-underline-offset: 3px; }

/* the controls only mean anything once the script is driving the deck */


/* ── with the script: the deck becomes a deck ────────────────────── */
.wd.is-js .wd-deck {
  display: block; position: relative;
  height: 100%; min-height: min(72vh, 560px);
  perspective: 1500px;
}
.wd.is-js .wd-deck:focus-visible { outline: 2px solid var(--brand); outline-offset: 8px; border-radius: 24px; }
.wd.is-js .wd-card {
  position: absolute; inset: 0 auto 0 50%;
  width: min(400px, 30vw, 82vw); margin-left: calc(min(400px, 30vw, 82vw) / -2);
  /* --p is the card's place relative to the centre: -1, 0 or 1 */
  transform: translateX(calc(var(--p, 0) * 64%))
             scale(var(--sc, 1))
             rotateY(calc(var(--p, 0) * -14deg));
  opacity: var(--op, 1); filter: blur(var(--bl, 0px));
  z-index: var(--zi, 1);
  transition: transform .62s var(--wd-ease), opacity .62s ease,
              filter .62s ease, box-shadow .62s ease;
}
/* with the arrows and dots gone, the flanking card IS the control: it
   stays clickable, and only the link inside it is taken out of reach so
   that a blurred, half-visible link is never what Tab finds next. */
.wd.is-js .wd-card[data-off] {
  --sc: .84; --op: .42; --bl: 3px; --zi: 1;
  cursor: pointer;
}
.wd.is-js .wd-card[data-off] .wd-link { pointer-events: none; }
.wd.is-js .wd-card[data-off]:hover { --op: .62; --bl: 2px; }
.wd.is-js .wd-card:not([data-off]) {
  --sc: 1; --op: 1; --bl: 0px; --zi: 10;
  box-shadow: 0 2px 6px rgba(26, 19, 16, .06), 0 40px 70px -36px rgba(26, 19, 16, .46);
}

/* ── the deal ────────────────────────────────────────────────────────
   The arc has just swept away and these three arrive in its place, so
   they arrive from somewhere rather than fading up on the spot: out of
   depth, face-down-ish, swinging onto their own axis as they land.

   Only a `from` is written. The implicit `to` is each card's own computed
   transform, so the keyframe never has to repeat — or drift from — the
   resting position the deck already sets. The flanking pair land first
   and the centre a beat later, which is what puts it on top rather than
   leaving three cards to arrive as one flat set. */
@keyframes wd-deal {
  from {
    opacity: 0;
    transform: translate3d(calc(var(--p, 0) * 14%), 46%, -760px)
               rotateX(44deg)
               rotateY(calc(var(--p, 0) * -42deg))
               rotateZ(calc(var(--p, 0) * 7deg))
               scale(var(--sc, 1));
  }
}
/* and the whole table tips upright under them */
@keyframes wd-deal-table {
  from { transform: perspective(1500px) rotateX(13deg) translateY(26px); }
}
@media (prefers-reduced-motion: no-preference) {
  .wd.is-js .wd-deckwrap.is-dealt .wd-deck {
    animation: wd-deal-table 1.05s cubic-bezier(.16, 1, .3, 1) both;
  }
  .wd.is-js .wd-deckwrap.is-dealt .wd-card {
    animation: wd-deal .92s cubic-bezier(.16, 1, .3, 1) both;
    animation-delay: calc(var(--d, 0) * 110ms);
  }
}
/* the deck is the last state; the wrapper only centres what is left */
.wd.is-js .wd-deckwrap { text-align: center; display: grid; align-content: center; }
.wd.is-js .wd-card { text-align: left; }

/* ── narrower ────────────────────────────────────────────────────── */
@media (max-width: 900px) {
  /* below this the panel is taller than a phone screen anyway, so pinning
     the section to one is only a way of cropping it */
  .wd { min-height: 0; display: block; }
  .wd > .wrap { display: block; min-height: 0; }
  .wd.is-js .wd-stages { display: block; }
  .wd.is-js .wd-stages > .wd-stage:not(.is-on) { display: none; }
  /* The panel keeps its desktop shape here: steps in the left column,
     the track image in the right, the vein drawing beside the numbers.
     It stops holding it at 600 — see below. */
  .wd-deck { grid-template-columns: minmax(0, 1fr); }
  /* a taller field on a phone: the ring is sized from the box's smaller
     side, and a 260px box made a 232px circle of 18px chips */
  .wd-field { height: min(62vh, 440px); }
  .wd.is-js .wd-deck { height: auto; min-height: 78vh; }
  .wd.is-js .wd-card { width: min(340px, 86vw); margin-left: calc(min(340px, 86vw) / -2); }
  .wd-head { margin-bottom: clamp(24px, 4vw, 40px); }
  .wd.is-js .wd-card[data-off] { --op: .2; }
  /* the narrower rule also has to grow, or it clamps the ring back down */
  .wd-field { height: min(58vh, 400px); }
  /* no chip override here. The script scales every chip from ONE base
     size, so a second base on narrow screens made its arithmetic wrong
     exactly where the ring was already tightest. */
}

/* ── under 600, the two columns stop fitting ──────────────────────────
   The panel holds its desktop arrangement as far down as it can. Below
   600 it cannot: the steps take .58 of the panel, so at 375 that column
   is about 120px, and a row with a 34px badge in it leaves roughly 75px
   for "Project Training". That is two characters a line — not a smaller
   version of the desktop layout, a broken one.

   So this is the one width where it differs: the image goes above the
   list and the rows run full width. The vein comes off with it, because
   a stacked row is short and wide and the 1024x1536 artwork rendered
   into a landscape slot is a red smear behind the number rather than a
   network. Everything from 600 up looks like the desktop. */
@media (max-width: 599px) {
  .wd-steps,
  .wd.is-js .wd-stages:has(.wd-avatar.is-ready) .wd-steps {
    grid-template-columns: minmax(0, 1fr);
    aspect-ratio: auto; height: auto; max-height: none;
  }
  /* only the list survives. The track image needs half a panel to be
     worth showing; stacked above three rows on a phone it is a banner
     the reader scrolls past, and it pushed the rows themselves below
     the fold. The rows are the section — they carry the three tracks,
     the copy and the links. */
  .wd-shots { display: none; }

  /* the vein stays, and it has to stay PORTRAIT to read. Its box is 30%
     of the row, so on a full-width row it hits the 200px cap while the
     row is only ~95 tall — a 1024x1536 network rendered into a landscape
     slot, which is the red smear. Capped to the row's own height here,
     so the artwork keeps roughly the proportion it was drawn at. */
  .wd-vein { width: 32%; min-width: 92px; max-width: 116px; }
}

/* Reduced motion: the deck still works, it simply arrives. The morph is
   not shortened — a 2.4s transformation is precisely what this preference
   is asking not to be shown — so the script skips it outright. */
@media (prefers-reduced-motion: reduce) {
  .wd.is-js .wd-stage, .wd.is-js .wd-card, .wd.is-js .wd-still,
  .wd-chip, .wd-mark, .wd.is-js .wd-step { transition-duration: .01ms; }
}

/* ── the presenter ────────────────────────────────────────────────────
   She stands at the right of the section, on its floor, beside the
   stepper rather than on top of it.

   HER IDLE MOTION USES translate / rotate / scale — the standalone
   properties — and NEVER transform. That is the whole reason she can be
   moved at all. An infinite animation owns the property it animates: while
   it runs, every transform rule on the element loses to it, so an idle
   float written as `transform: translateY(...)` silently kills her entry,
   her exit, and any action anyone adds later. Because the four properties
   compose (translate, then rotate, then scale, then transform), the idle
   can breathe on three of them while transform stays entirely free for
   the script.

   The script drives that free transform through --av-x/y/r/s, so an
   action is four numbers and never has to know the idle exists.

   SHE DOES NOT MOVE. Not a float, not a sway, not a breath. Every one of
   those is a transform on the whole image, and a transform on a whole
   image moves the whole body — the legs stretch with a scale and swing
   with a rotation, whatever the pivot is. There is no way to breathe a
   flat full-body photograph without its legs breathing too.

   So the only thing that changes while she speaks is which frame is
   showing, and the frames differ only in her face. Her legs are the same
   pixels in every one of them, which is the only guarantee that they are
   still that actually holds.

   transform is still hers to be moved by — see ShvAvatar.move() — it is
   simply never used for idling.

   The column costs nothing until she exists: .is-ready is set only after
   a frame has genuinely loaded, and every rule that takes width from the
   panel hangs off it. */
.wd-avatar {
  /* Out of the grid flow entirely. In it she sized the row, so capping
     her height against the row was circular — the row was however tall
     she was, and the section grew past one screen to fit her. Absolute,
     she cannot grow anything, and 100% now resolves against a height that
     is decided without her. */
  position: absolute; right: 0; bottom: 0;
  margin: 0; z-index: 2; pointer-events: none;
  /* Her width is what the panel reserves; her height is capped to the row
     so a taller frame set cannot push the section past one screen. When
     the cap binds she narrows within the reserved column, which only
     widens the gap — it never overlaps the panel. */
  width: var(--av-w); aspect-ratio: 949 / 1658;
  max-height: 100%; min-height: 0;
  opacity: 0; visibility: hidden;
  /* the script's to command; the idle never touches it */
  /* the script's to command, and nothing else's — her arrival is a plain
     fade, because even an entrance that slides is an entrance whose legs
     slide */
  transform: translate3d(var(--av-x, 0px), var(--av-y, 0px), 0)
             rotate(var(--av-r, 0deg)) scale(var(--av-s, 1));
  transition: opacity .6s ease, transform .8s var(--wd-ease);
}
.wd-avatar.is-ready { opacity: 1; visibility: visible; }

/* the nine stack; exactly one is opaque. The frames are drawn on flat
   white, so multiply keys that ground away against the section's own
   white without needing an alpha channel cut into nine files. */
.wd-avatar img {
  position: absolute; inset: 0; display: block; width: 100%; height: 100%;
  object-fit: contain; object-position: bottom center;
  mix-blend-mode: multiply;
  opacity: 0;
}
.wd-avatar img.is-on { opacity: 1; }
/* Her shoes, not the bottom of the box. The frame carries empty margin
   below her, so pivoting at 100% would put the hinge underneath the floor
   and swing her feet sideways; 93% is where she actually meets it. */
.wd-avatar { transform-origin: 50% 93%; }

/* Only now does the panel give up the width — and the list takes a larger
   share of what is left. Narrowing the panel without this wrapped every
   lead onto a second line, which grew each row, which grew the panel, which
   pushed the section past the one screen it is supposed to fit. The height
   was the symptom; the wrap was the cause. */
.wd.is-js .wd-stages:has(.wd-avatar.is-ready) [data-wd-stage="tracks"] {
  padding-right: calc(var(--av-w) + 22px);
}
.wd.is-js .wd-stages:has(.wd-avatar.is-ready) .wd-steps {
  grid-template-columns: minmax(0, .88fr) minmax(0, 1fr);
}

/* she belongs to the stepper; the transition is full-bleed and the deck
   is centred, and she would be in the way of both */
.wd-stages:has([data-wd-stage="tracks"][hidden]) .wd-avatar { opacity: 0; }

@media (max-width: 1023px) {
  /* no room beside the panel at this width, and she would only squeeze it */
  .wd-avatar { display: none; }
  .wd.is-js .wd-stages:has(.wd-avatar.is-ready) [data-wd-stage="tracks"] { padding-right: 0; }
}



/* ══ THE PLANS ════════════════════════════════════════════════════════
   Built to the reference: a light ground with white cards lifted off it,
   a coloured wash bleeding down from the top of each card into white, a
   ceramic icon tile, a two-tone description, and one near-black pill
   button on every card so the eye is not asked to choose between four
   competing calls to action.

   Each plan owns a hue, set once as --a on the card and used by the wash,
   the tile, the name and the check marks. Nothing else is coloured. That
   is the whole reason four cards can each have a colour without the
   section turning into a paint chart.

   Equal heights are the grid's doing — the row stretches and each card is
   a flex column — and the rows inside line up because every card carries
   the same parts in the same order, including the ones that are empty. */
.pricing {
  --pc-ink: #16181D;
  --pc-body: #1C1C1C;
  --pc-quiet: #6B7280;
  --pc-line: #EAEBEE;
  --pc-btn: #232427;
  /* white, so the band is continuous with the rest of the page. The cards
     were reading against a grey ground; on white it is their shadow and
     their own wash that separates them, which both already carry. */
  background: #FFFFFF;
}
.pc-gold    { --a: #C98A08; --a-soft: 201, 138, 8; }
.pc-crimson { --a: #AE0E20; --a-soft: 174, 14, 32; }
.pc-blue    { --a: #1D6FD0; --a-soft: 29, 111, 208; }
.pc-indigo  { --a: #3B3A8F; --a-soft: 59, 58, 143; }

/* ── the header ────────────────────────────────────────────────────── */
.pricing-head { text-align: center; margin-bottom: clamp(30px, 4vw, 54px); }
.pc-pill {
  display: inline-block; margin: 0 0 clamp(16px, 1.8vw, 24px);
  border-radius: 999px; border: 1px solid var(--pc-line); background: #FFFFFF;
  padding: 7px 18px; font-size: 13px; color: var(--pc-quiet);
  box-shadow: 0 1px 2px rgba(22, 24, 29, .04);
}
.pricing-head h2 {
  max-width: 26ch; margin-inline: auto; color: var(--pc-ink);
  font-family: var(--font-display); font-weight: 400;
}
/* the emphasis the reference puts on the opening phrase: the serif voice
   the rest of this site already uses for it, in the brand red */
/* NOT var(--brand): the Lab skin redefines that to its signal green, and
   this section appears on a Lab page — the emphasis turned teal there.
   The brand red is stated outright so it survives any skin. */
.pricing-head h2 em {
  font-family: var(--font-serif); font-style: italic; color: #AE0E20;
}
.pc-sub {
  max-width: 56ch; margin: clamp(12px, 1.4vw, 18px) auto 0;
  font-size: clamp(15px, 1.15vw, 17px); line-height: 1.6; color: var(--pc-quiet);
}

/* ── the card ──────────────────────────────────────────────────────── */
.pc-grid {
  display: grid; gap: clamp(14px, 1.5vw, 22px);
  grid-template-columns: repeat(4, minmax(0, 1fr));
  align-items: stretch;
}
.pc {
  position: relative; isolation: isolate; overflow: hidden;
  display: flex; flex-direction: column;
  border-radius: 24px; background: #FFFFFF;
  box-shadow: 0 1px 2px rgba(22, 24, 29, .05),
              0 22px 40px -28px rgba(22, 24, 29, .32);
  transition: transform .42s cubic-bezier(.16, 1, .3, 1),
              box-shadow .42s ease;
}
.pc:hover {
  transform: translateY(-6px);
  box-shadow: 0 2px 5px rgba(22, 24, 29, .06),
              0 40px 66px -30px rgba(22, 24, 29, .42);
}
/* the wash: brightest at the top right, gone by the middle of the card */
.pc-wash {
  position: absolute; inset: 0 0 auto; height: 46%; z-index: -1;
  background:
    radial-gradient(120% 100% at 78% 0%, rgba(var(--a-soft), .30), rgba(var(--a-soft), 0) 68%),
    radial-gradient(90% 80% at 18% 6%, rgba(var(--a-soft), .16), rgba(var(--a-soft), 0) 70%);
  -webkit-mask: linear-gradient(180deg, #000 42%, transparent 100%);
          mask: linear-gradient(180deg, #000 42%, transparent 100%);
  transition: opacity .42s ease;
}
.pc:hover .pc-wash { opacity: 1.15; }
.pc-body { display: flex; flex-direction: column; flex: 1;
  padding: clamp(20px, 1.9vw, 28px); }

.pc-top { display: flex; align-items: flex-start; justify-content: space-between; gap: 10px; }
/* a ceramic tile, lit from above, as in the reference */
.pc-ic {
  display: grid; place-items: center; width: 52px; aspect-ratio: 1;
  border-radius: 16px; color: var(--a);
  background: linear-gradient(180deg, #FFFFFF, #FBFBFC);
  box-shadow: inset 0 1px 0 #FFFFFF, 0 1px 2px rgba(22, 24, 29, .06),
              0 10px 18px -10px rgba(var(--a-soft), .45);
  transition: transform .42s cubic-bezier(.16, 1, .3, 1);
}
.pc-ic svg { width: 24px; height: 24px; }
.pc:hover .pc-ic { transform: scale(1.07); }
.pc-badge {
  border-radius: 999px; padding: 5px 11px; background: #FFFFFF;
  border: 1px solid rgba(var(--a-soft), .28); color: var(--a);
  font-size: 10.5px; font-weight: 600; letter-spacing: .07em; text-transform: uppercase;
  white-space: nowrap;
}

.pc-name {
  margin: 20px 0 0; font-size: clamp(19px, 1.55vw, 23px); font-weight: 600;
  letter-spacing: -.018em; color: var(--a);
}
/* two-tone, as the reference has it: the audience in ink, the rest quiet */
.pc-desc { margin: 10px 0 0; min-height: 4.7em;
  font-size: 14px; line-height: 1.55; color: var(--pc-quiet); }
.pc-desc b { font-weight: 600; color: var(--pc-ink); }

.pc-price { margin: 18px 0 0; display: flex; flex-direction: column; gap: 2px; }
.pc-pre { font-size: 11.5px; font-weight: 600; letter-spacing: .07em;
  text-transform: uppercase; color: var(--pc-quiet); }
/* Custom Quote carries no label, so its line is empty and holds the space */
.pc-pre:empty::before { content: '\00a0'; }
.pc-price b { font-size: clamp(25px, 2.2vw, 32px); font-weight: 700;
  letter-spacing: -.03em; color: var(--pc-ink); font-variant-numeric: tabular-nums; }

/* one button, one colour, on all four — four coloured buttons would make
   the reader choose between calls to action instead of between plans */
.pc-cta {
  margin-top: 20px; display: block; text-align: center; text-decoration: none;
  border-radius: 14px; padding: 14px 18px;
  font-size: 14.5px; font-weight: 600; color: #FFFFFF;
  background: linear-gradient(180deg, #34363B, var(--pc-btn));
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .12),
              0 1px 2px rgba(22, 24, 29, .18),
              0 12px 22px -12px rgba(22, 24, 29, .55);
  transition: transform .3s cubic-bezier(.16, 1, .3, 1), box-shadow .3s ease, filter .3s ease;
}
.pc-cta:hover { filter: brightness(1.12); transform: translateY(-1px);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .16),
              0 2px 4px rgba(22, 24, 29, .2),
              0 18px 30px -14px rgba(22, 24, 29, .6); }

.pc-rule { margin: 22px 0 0; border: 0; height: 1px;
  background: linear-gradient(90deg, var(--pc-line), rgba(234, 235, 238, 0)); }
.pc-label { margin: 20px 0 0; font-size: 13.5px; font-weight: 600; color: var(--pc-ink); }

.pc-list { list-style: none; margin: 16px 0 0; padding: 0;
  display: flex; flex-direction: column; gap: 12px; }
.pc-list li { display: flex; align-items: flex-start; gap: 11px;
  font-size: 13.5px; line-height: 1.5; color: var(--pc-body); }
.pc-list i { flex: none; display: grid; place-items: center;
  width: 19px; height: 19px; margin-top: 1px; border-radius: 999px;
  color: #FFFFFF; background: var(--a);
  box-shadow: 0 2px 5px -2px rgba(var(--a-soft), .7); }
.pc-list svg { width: 11px; height: 11px; }


/* ── on the Lab pages, which are black ─────────────────────────────── */
/* /support/pricing-and-payment/ carries this section too, and that page's
   ground is #0A0A0A. A white band dropped into it would read as a mistake,
   so the same structure is restated on the dark: panel instead of paper,
   a light button instead of a dark one, and the washes turned up because
   a soft tint disappears against black. */
:root[data-skin="lab"]:not([data-lab="light"]) .pricing { background: transparent; }
:root[data-skin="lab"]:not([data-lab="light"]) .pricing-head h2 { color: var(--lb-ink, #F6F3F1); }
:root[data-skin="lab"]:not([data-lab="light"]) .pc-pill,
:root[data-skin="lab"]:not([data-lab="light"]) .pc-ic { background: var(--lb-panel-2, #1A1A1A);
  border-color: var(--lb-line, rgba(255,255,255,.11)); color: var(--lb-faint, #B9B2AA); }
:root[data-skin="lab"]:not([data-lab="light"]) .pc-ic { color: var(--a); box-shadow: none;
  border: 1px solid rgba(var(--a-soft), .34); }
:root[data-skin="lab"]:not([data-lab="light"]) .pc {
  background: var(--lb-panel, #141414);
  box-shadow: 0 1px 2px rgba(0, 0, 0, .5), 0 24px 44px -28px rgba(0, 0, 0, .9);
}
:root[data-skin="lab"]:not([data-lab="light"]) .pc-wash {
  background:
    radial-gradient(120% 100% at 78% 0%, rgba(var(--a-soft), .55), rgba(var(--a-soft), 0) 68%),
    radial-gradient(90% 80% at 18% 6%, rgba(var(--a-soft), .28), rgba(var(--a-soft), 0) 70%);
}
:root[data-skin="lab"]:not([data-lab="light"]) .pc-name { color: #FFFFFF; }
:root[data-skin="lab"]:not([data-lab="light"]) .pc-desc { color: var(--lb-faint, #B9B2AA); }
:root[data-skin="lab"]:not([data-lab="light"]) .pc-desc b,
:root[data-skin="lab"]:not([data-lab="light"]) .pc-label,
:root[data-skin="lab"]:not([data-lab="light"]) .pc-price b { color: #FFFFFF; }
:root[data-skin="lab"]:not([data-lab="light"]) .pc-list li { color: var(--lb-muted, #E3DED8); }
/* on the dark ground the badge needs its own body — an outline in the
   accent over a coloured wash reads as almost nothing */
:root[data-skin="lab"]:not([data-lab="light"]) .pc-badge {
  background: rgba(255, 255, 255, .10); color: #FFFFFF;
  border-color: rgba(255, 255, 255, .26);
}
:root[data-skin="lab"]:not([data-lab="light"]) .pricing-head h2 em { color: #FF6B7D; }
:root[data-skin="lab"]:not([data-lab="light"]) .pc-rule {
  background: linear-gradient(90deg, var(--lb-line, rgba(255,255,255,.11)), transparent); }
:root[data-skin="lab"]:not([data-lab="light"]) .pc-cta {
  background: linear-gradient(180deg, #FFFFFF, #EFEFEF); color: #14100C;
  box-shadow: 0 1px 2px rgba(0, 0, 0, .4), 0 12px 22px -12px rgba(0, 0, 0, .8); }
:root[data-skin="lab"]:not([data-lab="light"]) .pc-sub { color: var(--lb-faint, #B9B2AA); }

@media (prefers-reduced-motion: reduce) {
  .pc, .pc-ic, .pc-cta { transition: none; }
  .pc:hover, .pc-cta:hover { transform: none; }
}
/* Four across, the four cards must agree on where each row sits — the
   price, the button, the rule. They cannot agree by reserving a fixed
   height for the description, because how many lines that description
   takes depends on the width: Project Pro runs to four lines at 1440 and
   to seven at 960, and no single reserved height is right for both.

   So the row measures itself. The grid carries the eight parts as eight
   real tracks and each card maps onto them with subgrid, which makes
   every track as tall as the tallest card's contribution to it. The
   description row is then exactly as deep as the longest description at
   whatever width the reader happens to have, and the buttons line up
   because they are literally in the same track.

   .pc-body stops being a box and becomes its contents, so the eight
   parts are the card's own children and can take the tracks. Its padding
   moves up to the card. Only here: below this width the cards wrap onto
   more than one row, the tracks would have to be counted per band, and
   two side-by-side cards at a much wider column rarely disagree anyway. */
@media (min-width: 1181px) {
  .pc-grid { grid-template-rows: repeat(8, auto); row-gap: 0; }
  .pc {
    grid-row: span 8;
    display: grid; grid-template-rows: subgrid;
    padding: clamp(20px, 1.9vw, 28px);
  }
  .pc-body { display: contents; }
  .pc-desc { min-height: 0; }
}

/* ── the landing page's shorter card ──────────────────────────────── */
/* Same card, less of it. No figure, no badge, five highlights, and the
   button at the foot rather than mid-card — which is where the reference
   puts it and which also lets all four buttons sit on one line however
   unevenly the copy above them runs.

   Only from 1180 up, because below that this section already switches to
   the compact card, which has none of these parts to begin with. */
@media (min-width: 1180px) {
  .pricing.is-brief .pc-price,
  .pricing.is-brief .pc-badge { display: none; }
  /* the fifth is the last the reference shows; the rest live on the
     pricing page, which is where the card now sends people */
  .pricing.is-brief .pc-list li:nth-child(n + 6) { display: none; }
  .pricing.is-brief .pc-cta { order: 9; margin-top: auto; }
  /* the rule and label lead the list, so they move up with it */
  .pricing.is-brief .pc-rule { order: 6; margin-top: 22px; }
  .pricing.is-brief .pc-label { order: 7; }
  .pricing.is-brief .pc-list { order: 8; margin-bottom: 22px; }
}

@media (max-width: 1180px) { .pc-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }

/* ── small screens get a different card, not a squeezed one ────────── */
/* Measured off the reference at 375: a 12px-radius card, 20px of padding,
   a 135deg wash running from the face colour at 40% out to the plan's own
   hue at 8% opacity, and a soft shadow thrown down and to the left.
   Inside it, only four things — a 44px icon tile, an arrow, the plan name
   at 22/700, and two lines of description.

   The price stays. The reference is a programme picker and has no prices
   in it; this is a pricing section, and one that hides its prices on a
   phone is broken rather than minimal. What goes is the rule, the
   "key highlights" label and the five-to-thirteen bullets under it —
   that list is what made these cards several screens tall on a phone,
   and it is what the card now links THROUGH to rather than carries.

   The call to action becomes that arrow. It is the same anchor, moved
   and restyled, so there is still exactly one link per card and it still
   announces itself by name. */
@media (max-width: 1179px) {
  .pc-grid { gap: 20px; }

  .pc {
    --pc-face: #FFFFFF;
    --pc-tint: .082;
    border-radius: 12px;
    box-shadow: -5px 6px 12px rgba(0, 0, 0, .05), -3px 4px 8px rgba(0, 0, 0, .04);
    padding: 20px;
  }
  /* the lab selector is repeated on the wash because that skin sets its own
     flat panel background at (0,2,0); a bare .pc here is (0,1,0) and lost,
     so the dark cards came out with no accent in them at all */
  .pc,
  :root[data-skin="lab"]:not([data-lab="light"]) .pc {
    background: linear-gradient(135deg, var(--pc-face) 40%, rgba(var(--a-soft), var(--pc-tint)) 100%);
  }
  /* The reference does not carry its colour on a flat diagonal alone. It
     puts a large soft disc of the plan's hue behind the content, off the
     right edge and blurred, and the linear wash only tints what is left.
     That bloom is what makes the card read as lit rather than as shaded,
     and it is what was missing. .pc-wash already exists for the wide
     card's top glow, so it is re-aimed here rather than duplicated. */
  .pc-wash {
    display: block; inset: -26% -26% auto auto;
    width: 68%; height: auto; aspect-ratio: 1; z-index: 0;
    border-radius: 999px;
    background: radial-gradient(circle, rgba(var(--a-soft), .22) 0%, rgba(var(--a-soft), 0) 72%);
    -webkit-mask: none; mask: none;
    filter: blur(20px);
  }
  .pc-body { position: relative; z-index: 1; padding: 0; }

  .pc-top { align-items: center; min-height: 44px; }
  .pc-ic {
    width: 44px; border-radius: 10px;
    background: rgba(var(--a-soft), .10);
    box-shadow: none;
  }
  .pc-ic svg { width: 22px; height: 22px; }
  /* two of the four carry one, so it reads as noise rather than as rank */
  .pc-badge { display: none; }

  .pc-name { margin-top: 16px; font-size: 22px; font-weight: 700; letter-spacing: -.02em; }
  .pc-desc { margin-top: 8px; min-height: 0; font-size: 16px; line-height: 24px; }
  /* The reference card carries no price, and that is the shape asked for.
     The figure is one tap away behind the arrow, which is where the rest
     of the plan already lives. */
  .pc-price { display: none; }

  /* the anchor, moved to the corner and left as the only control */
  .pc-cta {
    position: absolute; top: 20px; right: 20px; z-index: 2;
    margin: 0; display: grid; place-items: center;
    width: 30px; height: 30px; padding: 0; border-radius: 999px;
    background: none; box-shadow: none; color: var(--a);
    font-size: 19px; line-height: 1;
  }
  .pc-cta::after { content: "\2192"; }
  .pc-cta:hover { filter: none; transform: none; box-shadow: none; }
  /* the words stay for assistive tech; only the arrow is drawn */
  .pc-cta span {
    position: absolute; width: 1px; height: 1px; overflow: hidden;
    clip-path: inset(50%); white-space: nowrap;
  }

  .pc-rule, .pc-label, .pc-list { display: none; }

  /* the black pages keep their own face under the same wash */
  :root[data-skin="lab"]:not([data-lab="light"]) .pc { --pc-face: var(--lb-panel, #141414); --pc-tint: .34; }
  :root[data-skin="lab"]:not([data-lab="light"]) .pc-ic { border: 0; }
  /* the bloom has to be stronger against #141414 than against white */
  :root[data-skin="lab"]:not([data-lab="light"]) .pc-wash {
    background: radial-gradient(circle, rgba(var(--a-soft), .58) 0%, rgba(var(--a-soft), 0) 70%);
  }
}
@media (max-width: 700px)  {
  .pc-grid { grid-template-columns: minmax(0, 1fr); }
  .pc-desc { min-height: 0; }
}

/* ══ WHAT'S INCLUDED — THE COMPARISON TABLE ═══════════════════════════
   One page uses this, and that page is white, so it is written against
   the lab tokens rather than a second palette.

   A real table, so it scrolls sideways rather than reflowing: forty-one
   rows by four plans has no honest single-column form, and the usual
   trick of restacking each row into a card turns a grid you read across
   into 164 cards you read down. The row header stays put while the
   columns move, which is the whole reason a comparison table works. */
.pcmp-scroll {
  overflow-x: auto; overscroll-behavior-x: contain;
  border: 1px solid var(--lb-line); border-radius: 16px;
  background: var(--lb-panel);
  box-shadow: var(--pr-shadow);
}
.pcmp-scroll:focus-visible { outline: 2px solid var(--lb-sig); outline-offset: 3px; }
.pcmp-t { width: 100%; min-width: 800px; border-collapse: collapse; }
.pcmp-t th, .pcmp-t td {
  padding: 13px 16px; text-align: center; vertical-align: middle;
  border-top: 1px solid var(--line-soft);
}
.pcmp-t thead th { border-top: 0; }

/* the row header: sticky, and opaque so the columns pass under it */
.pcmp-t tbody th[scope="row"],
.pcmp-t .pcmp-h {
  position: sticky; left: 0; z-index: 1;
  text-align: left; font-weight: 400; color: var(--lb-w70);
  background: var(--lb-panel);
  /* a border on a sticky cell scrolls away with the cell it is drawn on,
     so the edge is a shadow, which does not */
  box-shadow: 1px 0 0 var(--line-soft);
}
.pcmp-t .pcmp-h { font-weight: 600; color: var(--lb-ink); }

/* the plan columns */
.pcmp-t thead .pcmp-c { padding-block: 18px; }
.pcmp-n { display: block; font-weight: 600; color: var(--lb-ink); }
.pcmp-p { display: block; margin-top: 3px; color: var(--lb-w52); font-variant-numeric: tabular-nums; }
.pcmp-c.is-pop { background: rgba(174, 14, 32, .035); }

.pcmp-in {
  display: inline-grid; place-items: center; width: 21px; height: 21px;
  border-radius: 999px; background: var(--lb-sig); color: #FFFFFF;
}
.pcmp-in svg { width: 12px; height: 12px; }
.pcmp-no { color: var(--lb-w52); }

/* the buttons on the floor of the table */
.pcmp-t tfoot td { padding-block: 20px; }
.pcmp-cta {
  display: inline-block; padding: 10px 18px; border-radius: 999px;
  border: 1px solid var(--lb-line-2); background: var(--lb-panel); color: var(--lb-ink);
  text-decoration: none; white-space: nowrap;
  transition: background .18s var(--ease), border-color .18s var(--ease);
}
.pcmp-cta:hover { background: var(--hover); border-color: var(--lb-ink); }
.pcmp-c.is-pop .pcmp-cta {
  background: var(--lb-sig); border-color: var(--lb-sig); color: #FFFFFF;
}
.pcmp-c.is-pop .pcmp-cta:hover { background: var(--lb-sig-2); border-color: var(--lb-sig-2); }
.pcmp-cta:focus-visible { outline: 2px solid var(--lb-ink); outline-offset: 2px; }

/* the hint only exists where the thing it describes is true */
.pcmp-hint { display: none; }
@media (max-width: 700px) {
  .pcmp-t { min-width: 560px; }
  .pcmp-t th, .pcmp-t td { padding: 10px 10px; }
  /* fixed, not max-width: the labels are long enough that the browser
     would otherwise hand this column half the window and leave one plan
     visible at a time */
  .pcmp-t tbody th[scope="row"], .pcmp-t .pcmp-h { width: 148px; }
  .pcmp-hint {
    display: block; margin: 0 0 10px; color: var(--lb-w52);
  }
  .pcmp-hint::after { content: " \2192"; }
}

/* ── the inheritance line, and the plan people pick ───────────────────
   "Everything in Research Assist, plus:" is a different kind of
   statement from "Key highlights include:" — one names a plan you are
   being told you also get, the other introduces a list. The reference
   marks that one with a tick and an underline; so does this. */
.pricing .pc-label.is-inherit {
  display: flex; align-items: center; gap: 8px;
  color: var(--ink); font-weight: 600;
}
.pricing .pc-label.is-inherit::before {
  content: ""; flex: none; width: 16px; height: 16px; border-radius: 999px;
  background: var(--a-strong, #AE0E20);
}
.pricing .pc-label.is-inherit > span { text-decoration: underline; text-underline-offset: 3px; }

/* the popular card, lifted rather than banner-topped: the four cards
   align on a subgrid, and a banner above one of them would push its
   eight rows out of step with the other three for the sake of a label
   the badge already carries. */
@media (min-width: 1180px) {
  .pricing .pc:has(.pc-badge) {
    border-color: rgba(174, 14, 32, .42);
    box-shadow: 0 18px 44px -26px rgba(174, 14, 32, .55);
  }
}

/* "Partial" — its own state, and it should read as neither of the other
   two. Amber rather than the brand red or the absent grey: a reader
   scanning a column needs to catch that this one is qualified, and a
   red word in a column of red ticks does not catch. */
.pcmp-part {
  display: inline-block; padding: 3px 10px; border-radius: 999px;
  background: rgba(138, 91, 12, .10); color: var(--lb-amber);
  white-space: nowrap;
}

/* The section paints its own white, the way .pricing above it does.
   Without it the section is transparent and the page's graticule runs
   under and around the table — the panel reads as white, the margin
   around it reads as ruled paper, and the two look like a mistake
   rather than a choice. Same literal as .pricing so the two sections
   cannot end up a shade apart where they meet. */
.pcmp { background: #FFFFFF; }

/* ── no ruling on the white pages ─────────────────────────────────────
   Lab draws two grids: a fixed one on <body> at 120 and 24px, and the
   hero's graticule over it. Both are read as instrument panel against
   black. On white they are graph paper, and graph paper behind a
   pricing table is one ruled grid too many. The wash stays — it is a
   glow, not a rule. */
:root[data-skin="lab"][data-lab="light"] body { background-image: none; }
:root[data-skin="lab"][data-lab="light"] .lb-graticule { display: none; }

/* The globe is a pre-rendered dark sphere with warm dotted landmasses,
   and it is on seventeen pages — most of them still black — so it
   cannot be swapped for a light file. Inverted it is very nearly the
   image we would have rendered: the near-black body becomes near-white
   and the warm dots go cold, which the hue-rotate puts back. Alpha is
   untouched, so the sphere keeps its edge. No second asset, no extra
   bytes, and the dark pages are not involved. */
:root[data-skin="lab"][data-lab="light"] .lb-globe img {
  filter: invert(1) hue-rotate(180deg);
}

/* the lift is measured with every marker switched on for a frame; this
   stops that one frame from animating visibly */
.ap-canvas.is-measuring .ap-mark,
.ap-canvas.is-measuring .ap-mark * { transition: none !important; }

/* ── the roadmap on Atlas ─────────────────────────────────────────────
   The Services pages draw the same curve as the section's opening page
   now. It declares three colours and a ground on .ap-jrn; Atlas points
   them at its own. Its red is #FF3B4E, which measures 5.5:1 on the
   Atlas ground — Nova's amber would be a second accent on a page that
   already has one. The streaks belong to Nova's starfield and there
   isn't one here. */
:root[data-skin="atlas"] .ap-jrn {
  --gold: var(--at-red);
  --track: var(--at-line-2);
  --idle: var(--at-w50);
  --road-ground: var(--at-bg);
}
:root[data-skin="atlas"] .ap-jrn-h { color: var(--at-ink); }
:root[data-skin="atlas"] .ap-jrn-sub { color: var(--at-w50); }
:root[data-skin="atlas"] .ap-streaks { display: none; }

/* ══ THE ROADMAP ON A TABLET ══════════════════════════════════════════
   The narrow drawing runs to 860px, so a tablet in portrait gets it —
   and gets it sized for a phone. The type rules under that breakpoint
   are vw and were tuned against a 375px screen ("or they would be
   unreadable", says the note on them), so at 768 they come out at
   roughly double: a 32px marker title on a drawing whose own lines are
   two pixels wide, and one mirrored label hanging 26px off the side of
   the canvas.

   This band is 601 to 860 and nothing else. Below it the phone keeps
   every value it has; above it the wide drawing is untouched. */
@media (min-width: 601px) and (max-width: 860px) {
  /* the abbreviated subtitle exists because a 375px screen has no room
     for the sentence. This one does. */
  .ap-mark-tx .ap-d { display: block; }
  .ap-mark-tx .ap-d-s { display: none; }

  /* capped, or the same problem returns at the top of the band: pure vw
     would be 16.8px at 601 and 24px at 860, and the drawing does not
     grow with it */
  .ap-mark-tx.ap-mark-tx b { font-size: min(2.8vw, 19px) !important; }
  .ap-mark-tx.ap-mark-tx em { font-size: min(2.2vw, 15px) !important; }
  :root[data-skin="nova"] .ap-mark-tx.ap-mark-tx b { font-size: min(3vw, 20px) !important; }
  :root[data-skin="nova"] .ap-mark-tx.ap-mark-tx em { font-size: min(2.3vw, 15.5px) !important; }

  /* smaller type needs less measure, and the narrower block is what
     brings the mirrored label back inside the canvas */
  .ap-mark-tx { width: 34vw; }
  .ap-mark.is-mirror-m .ap-mark-lab { width: 34vw; }

  /* the phone's per-stage lift is for a two-line label at 375; at this
     width the sentence fits differently and nothing needs raising */
  .ap-mark { --dy-m: 0px; }

  /* Nova overrides the narrow gap between heading and canvas down to
     10.5vw, which is 80px here — and its first label hangs further above
     the canvas than that, so it landed 8px into the sub-line. The Atlas
     pages take the 34vw the narrow rule gives them and clear it easily;
     this brings Nova nearer that. */
  :root[data-skin="nova"] .ap-jrn-head + .ap-canvas { margin-top: 20vw; }
}


/* ══ TYPE ON A TABLET ═════════════════════════════════════════════════
   The type scale is vw, and the phone overrides stop at 600px. Between
   601 and the desktop widths the raw vw values are what render, and at
   768 that is 8.5px for a kicker, 10.7px for body copy and 12.8px for a
   section heading — measured, not estimated. Some sections read fine
   there only because they happen to use a larger role.

   Every one of those roles gets a floor here, proportional to itself so
   the scale keeps its shape: a role sized 11.5px per vw unit, which is
   what it would have had at a 1150px window. Nothing above 2vw is
   touched — display type is already large enough at this width, and a
   floor would only inflate it.

   The band is 601 to 1024 and nothing else: below it the phone keeps
   every value it has, above it the desktop keeps every value it has.
   Generated from the 47 top-level vw font-size rules in this file
   rather than hand-listed, so no role is missed. */
@media (min-width: 601px) and (max-width: 1024px) {
  .ap-jrn-h.ap-jrn-h.ap-jrn-h { font-size: max(1.667vw, 19.17px) !important; }
  .ap-jrn-sub.ap-jrn-sub { font-size: max(1.389vw, 15.97px) !important; }
  .ap-mark-tx.ap-mark-tx b { font-size: max(1.389vw, 15.97px) !important; }
  .ap-mark-tx.ap-mark-tx em { font-size: max(1.11vw, 12.77px) !important; }
  :root[data-skin="nova"] .ap-jrn-h.ap-jrn-h.ap-jrn-h,
:root[data-skin="nova"] .ap-sec-h.ap-sec-h.ap-sec-h,
:root[data-skin="nova"] .ap-say.ap-say.ap-say,
:root[data-skin="nova"] .abstand-say.abstand-say.abstand-say { font-size: max(1.667vw, 19.17px) !important; }
  :root[data-skin="nova"] .ap-cell.ap-cell b,
:root[data-skin="nova"] .ap-mark-tx.ap-mark-tx b { font-size: max(1.389vw, 15.97px) !important; }
  :root[data-skin="nova"] :is(.ap-jrn-sub, .ap-sub, .ap-sec-d).ap-jrn-sub,
:root[data-skin="nova"] .ap-sub.ap-sub, :root[data-skin="nova"] .ap-sec-d.ap-sec-d { font-size: max(1.389vw, 15.97px) !important; }
  :root[data-skin="nova"] :is(.ap-cell p, .ap-intent-tx p, .ap-str-row dd,
                            .ap-vs-col li, .ap-hold-list li, .ap-note p) { font-size: max(1.25vw, 14.38px) !important; }
  :root[data-skin="nova"] .ap-mark-tx.ap-mark-tx em,
:root[data-skin="nova"] :is(.ap-eyebrow, .ap-sec-k, .ap-vs-k, .ap-tick-list li, .ap-stats span, .ap-badge-txt) { font-size: max(1.11vw, 12.77px) !important; }
  :root[data-skin="nova"] .ap-str-tab.ap-str-tab { font-size: max(1.11vw, 12.77px) !important; }
  :root[data-skin="nova"] .ap-cta .btn { font-size: max(1.11vw, 12.77px) !important; }
  :root[data-skin="nova"] .ap-badge-txt { font-size: max(1.2vw, 13.8px) !important; }
  :root[data-skin="nova"] .ap-hero .ap-stats span { font-size: max(1.25vw, 14.38px) !important; }
  :root[data-skin="nova"] .ap-hero .ap-crumbs :is(a, span) { font-size: max(0.97vw, 11.15px) !important; }
  :root[data-skin="nova"] #cfq h2 { font-size: max(1.667vw, 19.17px) !important; }
  :root[data-skin="nova"] #cfq .lede { font-size: max(1.389vw, 15.97px) !important; }
  :root[data-skin="nova"] #cfq .fq-q { font-size: max(1.25vw, 14.38px) !important; }
  :root[data-skin="nova"] #cfq .fq-p :is(p, .fq-list) { font-size: max(1.25vw, 14.38px) !important; }
  :root[data-skin="nova"] #cfq .fq-all a { font-size: max(1.11vw, 12.77px) !important; }
  :root[data-skin="nova"] .ap-reach-d { font-size: max(1.25vw, 14.38px) !important; }
  :root[data-skin="atlas"] .at-crumbs :is(a, span) { font-size: max(0.97vw, 11.15px) !important; }
  :root[data-skin="atlas"] .at-tag { font-size: max(1.04vw, 11.96px) !important; }
  :root[data-skin="atlas"] .at-sub { font-size: max(1.32vw, 15.18px) !important; }
  :root[data-skin="atlas"] .at-btn { font-size: max(1.11vw, 12.77px) !important; }
  :root[data-skin="atlas"] .at-step-n { font-size: max(0.97vw, 11.15px) !important; }
  :root[data-skin="atlas"] .at-step-t { font-size: max(1.6vw, 18.4px) !important; }
  :root[data-skin="atlas"] .at-step-d { font-size: max(1.11vw, 12.77px) !important; }
  :root[data-skin="atlas"] .at-card b { font-size: max(1.32vw, 15.18px) !important; }
  :root[data-skin="atlas"] .at-card p { font-size: max(1.11vw, 12.77px) !important; }
  :root[data-skin="atlas"] .at-k { font-size: max(0.97vw, 11.15px) !important; }
  :root[data-skin="atlas"] .at-d { font-size: max(1.25vw, 14.38px) !important; }
  :root[data-skin="atlas"] .at-row dt { font-size: max(1.18vw, 13.57px) !important; }
  :root[data-skin="atlas"] .at-row dd { font-size: max(1.11vw, 12.77px) !important; }
  :root[data-skin="atlas"] .at-note p { font-size: max(1.11vw, 12.77px) !important; }
  :root:is([data-skin="atlas"], [data-skin="lab"]) :is(.at-h, .lb-h, .lb-say, .lb-reach-h) { font-size: max(1.667vw, 19.17px) !important; }
  :root:is([data-skin="atlas"], [data-skin="lab"]) :is(
  .at-step-t, .at-card b, .at-row dt, .lb-chan-t, .lb-panel b, .lb-str-row dt) { font-size: max(1.389vw, 15.97px) !important; }
  :root:is([data-skin="atlas"], [data-skin="lab"]) :is(
  .at-sub, .at-d, .lb-sub, .lb-d, .lb-reach-d) { font-size: max(1.389vw, 15.97px) !important; }
  :root:is([data-skin="atlas"], [data-skin="lab"]) :is(
  .at-step-d, .at-card p, .at-row dd, .at-note p,
  .lb-chan-d, .lb-panel p, .lb-hold-tx, .lb-str-row dd, .lb-diff-col li,
  .lb-note p, .lb-thesis-tx p, .lb-field-list li) { font-size: max(1.25vw, 14.38px) !important; }
  :root:is([data-skin="atlas"], [data-skin="lab"]) :is(
  .at-k, .at-tag, .at-step-n, .at-crumbs,
  .lb-k, .lb-tag, .lb-diff-k, .lb-scope-bar, .lb-crumbs, .lb-readout-s,
  .lb-readout-ch, .lb-chan-n, .lb-panel-n, .lb-hold-n, .lb-str-rn),
:root[data-skin="lab"] [data-ix]::before { font-size: max(1.11vw, 12.77px) !important; }
  :root:is([data-skin="atlas"], [data-skin="lab"]) :is(.at-btn, .lb-btn, .lb-str-tab) { font-size: max(1.11vw, 12.77px) !important; }
}
