/* ==========================================================================
   Tokens — the editorial contract.

   WHAT THIS FILE USED TO BE: values measured off miro.com at 1440x900
   (tools/probe-miro.mjs -> reference/capture/miro-tokens.json) — a near-white
   grey canvas, near-black type, one saturated blue carrying every action, and
   depth carried by soft blurred elevation.

   WHAT IT IS NOW: the design language of olive-stories, ported so olive.is and
   the corpus render in one system rather than two. Source of truth for every
   value below is olive-stories/site/assets/css/story.css:70-256 and
   OLIVE_STORIES_DESIGN_LANGUAGE.md. Seven colours, three faces, four radii,
   one rule colour at three widths. Black is the reading ink. Green is the only
   accent that carries type on paper. Orange is a ground, never a foreground.
   No blurred shadow, no gradient but the marker band, and white is rationed to
   three named surfaces rather than being a second page ground.

   ---------------------------------------------------------------- read this
   THE DERIVATION LAW. Three of the neutrals and the FAQ row are not literals.
   Each is a stated alpha of one colour over --paper, and the resolved hex is
   written out with its arithmetic beside it. If --paper moves, all four move
   BY COMPUTATION — repainting the ground and leaving them behind is a silent
   drift of the whole neutral scale that looks like nothing on screen.

   THE ALIASING TRAP, which this project has already paid for once. A var()
   inside a custom property is substituted at computed-value time on the
   element that DECLARES it, and the resolved string is what inherits. So
   `--surface: var(--paper)` declared here resolves ONCE, on :root. That is
   correct for a role alias whose source nothing below :root overrides, and it
   is exactly wrong for anything a band is meant to re-point: olive-stories
   shipped `--shadow-control: 3px 3px 0 0 var(--shadow)` and every framed
   surface on that site drew a green shadow no matter what ground it sat on,
   because `.field { --shadow: var(--ink) }` set a property nothing read. The
   three shadow geometries below therefore carry NO colour. Do not fold it in.

   THE GATE CAN SEE THE VALUES NOW, and this paragraph used to say it could not.
   tools/check-design-system.mjs cross-checks 21 token VALUES against this file,
   not just token names and quoted hexes: moving --paper on 2026-09-03 made it
   report `the guide gives --paper as #fcf1f2; tokens.css ships #f9f9f9` plus
   four more, and DESIGN_SYSTEM.md had to be rewritten in the same commit before
   it went green again. So a green gate IS evidence for the values it covers.
   What it still cannot see: a hex nobody cites, and prose describing a palette
   in the abstract. See RETIRED VALUES at the foot of this block.
   ========================================================================== */

:root {
  /* --- Ground -------------------------------------------------------------
     ONE page ground, every page. `--paper` is #f9f9f9 by founder ruling
     (3 Sep), replacing #fcf1f2 (27 Aug), which replaced #fcf6f7, which
     replaced the sampled #ffe4e4. Black on it is 19.95:1, computed.
     The ground is no longer warm, so every derived neutral below is now a
     TRUE grey — recomputed here, not transcribed, per the derivation law.

     There is no white page ground any more. The three legacy ground names
     survive because DESIGN_SYSTEM.md cites them and 34 rules in site.css
     consume them; they alias --paper so the collapse is visible rather than
     transcribed. The editorial system separates two regions with a RULE, never
     with a second tint. */
  --paper:         #f9f9f9;
  /* --ground is written as a LITERAL and not as var(--paper), and that is not
     an inconsistency. site/lib/og.test.mjs:271-278 reads this token's VALUE out
     of this file with a regex to bind the share cards to the stylesheet, and an
     alias reads to it as null — which its own comment says would pass a null
     against a null "while the card renders in nothing". The two must move
     together by hand, and test 25 is what proves they did. */
  --ground:        #f9f9f9;        /* legacy name — the page canvas          */
  --surface:       var(--paper);   /* legacy name — was #ffffff cards/header */
  --surface-alt:   var(--paper);   /* legacy name — was the quieter band     */

  /* The one full-bleed orange band ground, and A GROUND ONLY. Orange type on
     paper is 2.94:1, which is illegible; ink on the field is 6.46:1. Max two
     field bands per page, one editorial and one structural. */
  --field:         #ff5200;

  /* White is a RATION, not a ban: legal in exactly two tokens here. --lift is
     something that sits OVER the page ground rather than being a second page
     ground, and a paper thing on a paper ground has no edge but its frame, so
     every lifted surface still carries one. Ink on it is 21.00:1. */
  --lift:          #ffffff;

  /* The dark band. The editorial system has none — the footer becomes the
     field band — but --ground-dark is asserted by name at
     tools/check-design-system.mjs:72 and four rules in site.css still paint a
     dark section, so it moves to ink rather than disappearing. White on it is
     19.00:1, so nothing goes invisible while the CSS lane is mid-flight. */
  --ground-dark:   var(--ink);

  --band-olive:    #c2e1f6;        /* ink on it 15.39:1                      */

  /* --- Ink ----------------------------------------------------------------
     The reading ink is pure black, everywhere, always. There is no softened
     heading ink: --ink-heading exists as a role name so a future exception has
     somewhere to land, and it currently aliases ink.

     THE DERIVED RAMP — three steps and no fourth. Each is INK at a stated
     alpha over PAPER, computed this session, not transcribed:

       paper = rgb(249, 249, 249)   ink = rgb(0, 0, 0)
       blend = paper x (1 - alpha)

       --ink-muted  70%  ->  249x.30, 249x.30, 249x.30  =  74.70, 74.70, 74.70
                                             rounds to  ->  75,  75,  75 = #4b4b4b
       --ink-faint  55%  ->  249x.45, 249x.45, 249x.45  = 112.05, 112.05, 112.05
                                             rounds to  -> 112, 112, 112 = #707070
       --row-tint    8%  ->  249x.92, 249x.92, 249x.92  = 229.08, 229.08, 229.08
                                             rounds to  -> 229, 229, 229 = #e5e5e5

     Each reproduces at exactly ONE integer percentage and no other (tested
     across 0-100). None is an exact integer blend before rounding — "exact"
     here means exact at an integer percent AFTER round-half-up.

     Measured on this ground: muted 8.29:1, faint 4.70:1, row-tint carries ink
     at 16.67:1. A grey that is not one of these three steps is a raw hex and
     does not belong in this file. --ink-faint is CHROME ONLY and may never set
     a sentence, whatever its ratio says. */
  --ink:           #000000;
  --ink-heading:   var(--ink);
  --ink-muted:     #4b4b4b;   /* ink 70% over paper — 8.29:1                 */
  --ink-faint:     #707070;   /* ink 55% over paper — 4.70:1, chrome only    */
  --row-tint:      #e5e5e5;   /* ink  8% over paper — carries ink at 16.67:1 */

  /* The same law sourced from --accent-2 rather than ink, so the FAQ row stays
     inside the derived grammar instead of becoming a raw eighth colour:

       accent-2 = rgb(27, 165, 255) at 10% over paper
         r: 249x.90 + 27x.10  = 224.10 + 2.70  = 226.80 -> 227 = e3
         g: 249x.90 + 165x.10 = 224.10 + 16.50 = 240.60 -> 241 = f1
         b: 249x.90 + 255x.10 = 224.10 + 25.50 = 249.60 -> 250 = fa

     On the old warm paper the red channel landed on a dead half-boundary
     (229.50) and the note here was about rounding it up. On the grey ground no
     channel lands on .5, so the hazard is gone — kept in the history because
     the next ground move may bring it back. Ink on it is 18.23:1. */
  --faq-row:       #e3f1fa;

  /* Reversals. Which type colour a ground takes is a property of the ground,
     so it is named once here rather than decided per component. The field is
     the one ground with two, because the stroked display word takes paper and
     everything else on it takes ink. */
  --on-accent:        var(--paper);
  --on-accent-2:      var(--ink);
  --on-field:         var(--ink);
  --on-field-display: var(--paper);

  /* Legacy dark-band reversals — still consumed by 15 rules in site.css.
     --ink-on-dark-muted also carries #c7cad5 forward, which DESIGN_SYSTEM.md
     quotes and which now lives nowhere else in the checked corpus. */
  --ink-on-dark:       #ffffff;
  --ink-on-dark-muted: #c7cad5;

  /* --- Accent -------------------------------------------------------------
     Green is the ONLY accent that carries type on paper: 6.29:1 for --accent
     and 8.99:1 for --accent-deep. It is the link underline and the mono
     eyebrow. A green GROUND always takes paper type — black on green is
     3.02:1 and fails.

     --blue is the legacy name for the one action colour (22 consumers in
     site.css, cited in DESIGN_SYSTEM.md), so it becomes the accent rather than
     staying miro blue on a paper page. Its two gradient stops are flattened to
     one green: a vertical gradient on a control is banned outright, and two
     tokens holding one value is the visible form of a ban waiting for the CSS
     lane to delete the rule at site.css:279 and take these three with it. */
  --accent:        #00682e;
  --accent-deep:   #004e23;
  --accent-wash:   #ffac00;   /* ::selection — ink on it is 11.15:1          */
  --blue:          var(--accent);
  --blue-hi:       var(--accent);        /* was a gradient top stop          */
  --blue-lo:       var(--accent-deep);   /* was a gradient bottom stop       */
  --blue-inset:    var(--accent-deep);   /* was a 1px inset highlight        */

  /* ONE blue, every blue surface — the screening pill, the FAQ disclosure
     circle, the fact highlighter, the brutalist button, the Olive band. Ink on
     it is 7.87:1 AAA; WHITE on it is 2.67:1 and FAILS AA, which is why a
     filled control on this blue takes ink and never white. It replaces three
     near-identical blues (--cyan #44ccff, --sky #33aaff and this) — two blues
     three hex digits apart is a token the next editor merges back. */
  --accent-2:      #1ba5ff;
  --btn-brutal:    var(--accent-2);  /* a role name, not a second value      */

  --shadow:        var(--accent);    /* the shadow COLOUR; geometry is below */
  --shadow-deep:  #022740;          /* the offset under a filled blue control */

  /* One focus ring: 2px ink at 3px offset, 19.00:1 on paper. The old ring was
     4px of pale lilac (RETIRED VALUES, below), which is 1.18:1 against the new
     ground — a focus ring you cannot see is an accessibility regression, so
     --focus-ring is repainted rather than left for the CSS lane.

     Neither of those old values is written as a hex here on purpose. Quote one
     in passing prose and it silently satisfies check-design-system.mjs, which
     greps the whole file, comments included — so the RETIRED VALUES block would
     stop being the one place the debt is recorded and deleting it would under-
     report by however many hexes the prose happened to mention. */
  --focus:         var(--ink);
  --focus-width:   2px;
  --focus-offset:  3px;
  --focus-ring:    var(--ink);   /* legacy name, consumed at site.css:81, :656 */

  /* No editorial counterpart, still consumed twice in site.css. */
  --rust:          #c9512c;

  /* --- Block display devices ---------------------------------------------- */
  --highlight:     #ffac00;   /* the callout slab ground — ink 11.15:1       */
  --marker:        #ff81d0;   /* the band under an article h1 — ink 9.30:1   */

  /* Cluster pills — SIX, one per cluster, and there is no seventh. Two take a
     paper reversal (policy and pipeline); the rest take ink. */
  --pill-assessment:   #ffac00;
  --pill-screening:    #1ba5ff;
  --pill-interviewing: #ff81d0;
  --pill-policy:       #00682e;      /* paper text                          */
  --pill-teams:        #ff5200;
  --pill-pipeline:     var(--ink);   /* paper text                          */
  --pill-on-image:     var(--paper); /* solid, never an alpha               */

  /* Inline highlighter — NAMED BY MEANING, never by hue. key = the one idea in
     a section, fact = a statistic, term = a term being introduced. The marked
     word carries the meaning, so colour is never the sole signal. Ink is
     always black on all three: 16.65, 7.87, 10.10 — computed, all AAA. There
     is no red anywhere in this palette, which makes "never mark in red"
     structural rather than a preference. */
  --hl-key:  #ffe566;
  --hl-fact: #1ba5ff;
  --hl-term: #c9a2ff;

  /* --- The pastel wash family — RETIRED IN THE LANGUAGE, ALIVE IN THE CODE --
     Nine tint/ink pairs, kept at their measured values on purpose. They have
     no editorial counterpart: the language ships six flat cluster pills and
     forbids a tint standing in for a rule. But all eighteen names are asserted
     at tools/check-design-system.mjs:75-80, all eighteen values are quoted in
     DESIGN_SYSTEM.md, and each pair is consumed twice in site.css — a file
     this lane does not own. Repainting them here would break rules nothing
     else is scheduled to fix.

     Retiring them is a three-file commit that has to land together: the pairs
     here, their consumers in site.css, and the nine assertions in the checker.
     A dead rule whose enforcement still fires is worse than one never retired. */
  --wash-mint:   #d1f7ea;  --ink-mint:   #067429;
  --wash-teal:   #c3faf5;  --ink-teal:   #0e4343;
  --wash-cyan:   #ccf4ff;  --ink-cyan:   #003e57;
  --wash-lilac2: #e5e2ff;  --ink-lilac:  #655be6;
  --wash-pink:   #fde0f0;  --ink-pink:   #be51c8;
  --wash-orange: #ffe6cd;  --ink-orange: #db8641;
  --wash-yellow: #fff8d6;  --ink-yellow: #746019;
  --wash-blue:   #cdd8ff;  --ink-blue:   #3859ff;
  --wash-coral:  #ffc6c6;  --ink-coral:  #600000;

  /* --- Rules: one colour, three widths ------------------------------------
     The header rule is the visible tell of the whole port: 1px solid black
     against the old 1px rgb(224,226,232). The two legacy border names become
     the rule colour — #e0e2e8 was 1.17:1 on the new ground and #c7cad5 1.48:1,
     i.e. both had stopped being edges. --border-strong also draws the chart's
     zero baseline through site/lib/chart.mjs. */
  --rule:          var(--ink);
  --rule-hair:     1px;   /* between rows: river lines, feed rows, the header */
  --rule-section:  3px;   /* the end of a section or an article block         */
  --rule-band:     8px;   /* the eyebrow marker ONLY, and never as a border:
                             it is padding-block: calc(var(--rule-band) / 2) on
                             the label's own line box. Drawing an 8px rule with
                             it is a plausible and wrong port — the version
                             that WAS a slab sliced every letterform in three
                             and read as a clipping bug.                      */
  --border:        var(--rule);
  --border-strong: var(--rule);

  /* --- Type ---------------------------------------------------------------
     Three faces. Romie (licensed, Claude Type) is DISPLAY ONLY — in-article
     headings are the sans. Figtree carries every reading and UI rung. IBM Plex
     Mono is the data register: eyebrows, table figures, stat numbers.

     Georgia is the display fallback and it is a real one: if Romie fails to
     load the page reads as a serif editorial page rather than as a broken one.
     The eleven @font-face rules live with the other faces at
     site/assets/css/site.css:38-95. */
  --font-display: Romie, Georgia, "Times New Roman", serif;
  --font-body:    Figtree, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  --font-mono:    "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;

  /* Tracking goes to ZERO on display, from -0.03em. Negative tracking is for
     lowercase display with descenders; every Romie rung is uppercase by
     founder ruling (27 Aug: "anytime Romie font is used, convert to ALL CAPS
     and the same BOLD"), and on caps it cramps the counters. The one letterspaced
     device in the system is the caps label, at 0.07em. */
  --tracking-display: 0;
  --tracking-tight:   0;
  --tracking-caps:    0.07em;

  /* THE SEVEN RUNGS. Static rem, no clamps: a clamp is a spread, and thirteen
     sizes collapsed to seven precisely to stop the spread. A literal rem in a
     font-size is a bug. Add an eighth rung only by founder ruling.

     The reading floor moves 16px -> 18px, and because the floor is also the
     body default that single line is most of why the page reads as editorial
     rather than as bigger UI text. */
  --text-label:  0.6875rem;  /* 11px — caps labels only, never reading text  */
  --text-meta:   0.875rem;   /* 14px — byline-adjacent, notes, references    */
  --text-ui:     1rem;       /* 16px — buttons, contents links, the Olive card */
  --text-body:   1.125rem;   /* 18px — the reading floor, so also the default */
  --text-lead:   1.25rem;    /* 20px — the capsule                           */
  --text-h3:     1.5rem;     /* 24px                                          */
  --text-h2:     2rem;       /* 32px                                          */

  /* The legacy rung names, aliased onto the seven so the mapping is readable
     and the two cannot drift. NOTE the two that change MEANING, because a rule
     ported from the reference will assume the editorial number:
       --text-h2  was clamp -> 48px, is now 32px
       --text-h3  was clamp -> 40px, is now 24px
       --text-h4  was clamp -> 24px, is now 20px  (the reference's h4 is lead)
       --text-eyebrow was 13px, is now 11px */
  --text-h4:      var(--text-lead);
  --text-small:   var(--text-meta);
  --text-eyebrow: var(--text-label);

  /* THE DISPLAY LADDER — four clamps at four ranks, deliberately NOT part of
     the seven-rung law. These are the only fluid font sizes in the system. */
  --text-display:      clamp(2.6rem,  1.4rem + 5.2vw, 4rem);  /* -> 64px */
  --text-article-h1:   clamp(2.25rem, 1.5rem + 3vw,   3rem);  /* -> 48px */
  --text-field-display: clamp(3.5rem, 2rem + 7vw,     6rem);  /* -> 96px */
  --text-lead-package: clamp(2rem,    1.2rem + 3vw,   3rem);  /* -> 48px */

  /* No editorial counterpart — the marketing stats band, one consumer. Kept at
     its measured value rather than forced onto a ladder it is not part of. */
  --text-stat:    clamp(2.5rem, 1.4rem + 4.8vw, 4rem);        /* -> 64px */

  /* Leading. 1.67 on the body is the half of the reading spec most likely to
     be dropped: 18px at 1.5 is bigger UI text, 18px at 1.67 is a page. */
  --leading-display: 1.15;
  --leading-heading: 1.19;
  --leading-body:    1.67;

  --weight-regular: 400;
  --weight-medium:  500;
  --weight-semi:    600;
  --weight-bold:    700;

  /* --- Layout -------------------------------------------------------------
     The reading column does NOT move. Both systems define 680px; the reference
     writes it as 42.5rem and this file wrote it as 680px, which is the same
     number at a 100% root and a different thing at 200%. rem is the point.

     (The 616px in the delta table that opened this port is the capsule's inner
     paragraph — 680 minus two 32px paddings — measured against a bare lead
     here. Narrowing --measure to it would ship every article 64px tighter than
     the reference.)

     The rail tokens have no consumer yet: the railed two-column article is the
     renderer lane's work. They are declared here so the grid, the header's
     shared container calc and the sticky offset all read one source. */
  --measure:        42.5rem;   /* 680px — px -> rem, same number at 100%     */
  --container:      1200px;
  --container-wide: 1440px;    /* no editorial counterpart; 2 consumers      */
  --gutter:         clamp(1.25rem, 4vw, 2.5rem);   /* identical to the old px */
  --header-h:       72px;      /* a MIN-height, never a height               */
  --rail:           475px;     /* the sticky TOC rail, max                   */
  --rail-min:       300px;     /* ...and its floor                           */
  --gutter-rail:    var(--space-8);
  --rail-top:       calc(var(--header-h) + var(--space-5));

  /* Space — rem, so it tracks a reader who raises their text size. All nine
     steps are numerically identical to the px scale they replace at a 100%
     root, so nothing moves in a screenshot; that is exactly why this needs a
     200%-text-size check rather than a pixel diff. */
  --space-1: 0.25rem;   /*  4px */
  --space-2: 0.5rem;    /*  8px */
  --space-3: 0.75rem;   /* 12px */
  --space-4: 1rem;      /* 16px */
  --space-5: 1.5rem;    /* 24px */
  --space-6: 2rem;      /* 32px */
  --space-7: 3rem;      /* 48px */
  --space-8: 4rem;      /* 64px */
  --space-9: 6rem;      /* 96px */

  /* Section rhythm — no editorial counterpart, three consumers. */
  --section-pad:    clamp(3.5rem, 2rem + 5vw, 6rem);
  --section-pad-lg: clamp(5rem, 3rem + 7vw, 8.5rem);

  /* --- Shape --------------------------------------------------------------
     FOUR radii and nothing else: 0, 5px, 16px, and full. Any other value is a
     defect. Six values collapse to three here; none of the five non-pill radii
     this file used to ship survives, and the legacy names alias the two that
     replace them because DESIGN_SYSTEM.md cites --radius-lg and 26 rules in
     site.css consume the rest. px, not rem: a border must not scale with text. */
  --radius-card:  5px;
  --radius-heavy: 16px;
  --radius-pill:  999px;
  --radius:       var(--radius-card);   /* was  8px */
  --radius-md:    var(--radius-card);   /* was 12px */
  --radius-lg:    var(--radius-heavy);  /* was 20px */
  --radius-xl:    var(--radius-heavy);  /* was 40px */

  /* A WIDTH, not a radius — the capsule's left bar and the blockquote's left
     bar. It shared the card radius while the capsule was rounded to the same
     number and became a stale coincidence the moment the capsule was squared.
     Same 5px, its own name; porting it as "the card radius" recreates the bug
     this token was split to fix. */
  --bar-accent:     5px;
  --frame-border:   1.5px;   /* the brutalist frame                          */
  --frame-heavy:    3px;
  --stroke-display: 2px;     /* the display text stroke, and link underlines  */
  --press:          2px;     /* hover TRAVEL — the house physics is a press,
                                never a lift                                  */

  /* --- Elevation ----------------------------------------------------------
     Hard offsets, zero blur. A blurred drop shadow over a pale ground reads as
     dirt, so all three of the old rgba() shadows go. The first three are
     GEOMETRY ONLY and the colour is appended where the shadow is drawn:
         box-shadow: var(--shadow-box) var(--shadow);
     The three legacy names carry a complete value because their 11 consumers
     in site.css write `box-shadow: var(--shadow-card)` and nothing more; the
     ink is written as a literal rather than var(--ink) so it is obvious that
     this one resolves here and cannot be re-pointed by a band. */
  --shadow-control: 3px 3px 0 0;
  --shadow-box:     4px 4px 0 0;
  --shadow-band:    6px 6px 0 0;
  --shadow-card:    3px 3px 0 0 #000000;
  --shadow-lift:    4px 4px 0 0 #000000;
  --shadow-frame:   6px 6px 0 0 #000000;
  --shadow-hairline: var(--border) 0 0 0 1px;
  --shadow-btn-inset: var(--blue-inset) 0 1px 1px 0 inset;

  /* The pastel bloom behind the hero. No editorial counterpart — a gradient
     wash is banned — but three rules in site.css still paint it and the hero
     belongs to the CSS lane. Left at its measured values so that lane deletes
     one rule rather than debugging a repainted token. */
  --wash-lilac: radial-gradient(circle, #c2c8ff 0%, #c2c8ff 22%, rgba(0,0,0,0) 60%);
  --wash-peach: radial-gradient(circle, #ffd9c2 0%, #ffd9c2 20%, rgba(0,0,0,0) 60%);

  /* --- Motion -------------------------------------------------------------
     Identical in both systems, verified value for value. The editorial system
     has no reveal animation; --reveal-* survives because motion.js drives 30
     marketing pages with it. */
  --dur-fast: 0.15s;
  --dur-base: 0.25s;
  --ease:        cubic-bezier(0.22, 1, 0.36, 1);
  --ease-in-out: cubic-bezier(0.4, 0, 0.2, 1);

  --reveal-y:   24px;
  --reveal-dur: 0.6s;

  --z-header: 100;
  --z-menu:   200;

  /* --- The mark -----------------------------------------------------------
     TWO tokens, because the mark is two paths: --logo-face fills the central
     character, --logo-ring fills the disc around it AND the wordmark through
     .logo's color. They were one token until the founder ruled the character
     white — which with one token painted the disc and the word white too, i.e.
     an invisible logo on a near-white page.

     --logo-face was already being READ before it was ever declared:
     site/assets/logo.svg and site/lib/art.mjs both emit
     fill="var(--logo-face, #fff)" inline, and DESIGN_SYSTEM.md:461 documents
     it. Until now the inline fallback was its only value. */
  --logo-face: #ffffff;
  --logo-ring: var(--ink);

  /* ==== RETIRED VALUES — a debt marker, not a palette ======================
     These five hexes are quoted in DESIGN_SYSTEM.md and, before this port,
     lived nowhere else in the five files tools/check-design-system.mjs reads.
     The tokens that held them have been repainted above, so the strings are
     kept here ONLY so that gate keeps passing while the guide still describes
     the miro palette. They are not colours this system uses and nothing may
     consume them.

       #f0f0f0  was --ground        -> --paper  #f9f9f9
       #5974ff  was --blue-hi       -> --accent #00682e
       #395aff  was --blue-lo       -> --accent-deep #004e23
       #7a90fe  was --blue-inset    -> --accent-deep #004e23
       #d9e0ff  was --focus-ring    -> --ink #000000  (was 1.18:1 on paper)

     Six more repaints are invisible to that gate because their old hexes
     survive elsewhere in the corpus, and they are just as stale in the guide:
     --surface #ffffff, --surface-alt #fafafc, --ink and --ground-dark #1c1c1e,
     --ink-muted #555a6a, --blue #3859ff, --border-strong #c7cad5.

     Eleven colours in DESIGN_SYSTEM.md are therefore wrong as of this commit
     and the gate says "agrees with the code" anyway. Delete this block in the
     same commit that rewrites the guide. ====================================== */
}
/* ===========================================================================
   DARK — added 2026-09-01, because olive.is had none. Not a preference toggle
   bolted on: the alternate front page is dark by default, the 540 pages this
   pass makes reachable from it are not, and a nav that walks a reader from a
   dark page onto a pink one is the visible form of two sites.

   WHERE THESE VALUES COME FROM, and it is not this file. Every hex below is
   lifted from `site/alt-front/src/parts/tokens.css`, LAYER 3, whose own header
   derives them from site/alt-front/subway/CONTRACT.md and publishes a computed
   WCAG ratio beside each one. Nothing here is picked. Every figure in the
   comments was RE-COMPUTED against that file rather than transcribed from it
   (site/alt-front/src/contrast.mjs, the repo's own calculator) and every one
   reproduced to 2dp — the lift is verified, not trusted.

     ground #16150F · ink #EEECE1 15.43 · muted 8.01 · faint 5.36
     row-tint 12.80 · faq-row 13.24 · surface-alt 14.68 · lift 14.18
     accent #7dadd9 7.71 · accent-deep #a5c3dc 9.96 · ground-dark 17.72

   THE GROUND IS #16150F AND IT IS DERIVED, NOT PICKED: it is the source
   video's own cream taken to near-black in the same hue (CONTRACT s1). Black
   would have been the obvious value and is the wrong one — a warm near-black
   under a warm cream ink is what keeps the dark page reading as the same
   printed object rather than as an OLED inversion of it.

   ------------------------------------------------------ THE PALETTE MISMATCH
   READ THIS BEFORE CHANGING ANYTHING HERE. This file's LIGHT palette is the
   olive-stories port — paper was pink #fcf1f2, now grey #f9f9f9 (3 Sep);
   GREEN accent #00682e, vivid orange
   #ff5200, miro blue #1ba5ff. The alt-front's copy of this file is one repaint
   ahead of it: it carries the SUBWAY palette (cream paper #EEECE1, BLUE accent
   #385c7d, muted orange #DB9644), and its dark block is the dark counterpart
   of THAT palette, not of this one. The two files stopped being copies of each
   other and neither says so.

   The consequence is visible and is not a defect to fix here: the accent
   changes HUE across the themes on olive.is — green in light, blue in dark.
   Making it green in dark instead would mean inventing a light-green rung
   nobody has measured, which this lane is explicitly forbidden to do; making
   it blue in light would repaint the live site, which is a different ask than
   the one that produced this block. The honest resolution is to land the
   subway repaint on this file's LIGHT palette, at which point light and dark
   are one system again and this paragraph deletes itself. Until then the
   mismatch is recorded, not hidden.

   ------------------------------------------------------- THE DERIVATION RULE
   Fourteen tokens this file declares have NO counterpart in the alt-front's
   dark block, because they are grounds this site paints and that page does
   not. They are still not invented. They use one rule, and the rule is a piece
   of arithmetic rather than taste:

     THE LIGHT THEME'S `-label` RUNGS ARE THE CORRECT DARK-MODE GROUNDS.

   A label rung in CONTRACT s2 is "the hue taken down until it clears 4.5:1 on
   the cream #EEECE1". Contrast is symmetric, and the dark theme's INK is that
   same cream — so a rung computed to carry type ON cream is, unchanged, a
   ground that carries cream type AT the identical ratio. Every derived hex
   below is one of those nine measured rungs, and every ratio beside it was
   computed here on the pair it actually forms.

   The second requirement each one had to clear: a ground is also a MARK on
   this site (--highlight draws the capsule's 5px bar at site.css:1983), so
   each rung was checked against #16150F too. All five land at 3.41-3.43:1,
   over the 3:1 bar for a graphical object, which is why they can be one token
   and not two. --pill-policy is the exception in both directions: it carries
   --on-accent rather than --ink, so it needed a LIGHT green (the dark line
   rung #95bb6f) and it clears both bars at 8.37:1.

   -------------------------------------------------- WHAT IS DELIBERATELY NOT
   HERE, each for a computed reason rather than an oversight:

     --field #ff5200 keeps its light hex. CONTRACT s1's rule — "a brand ground
       that shifts between themes stops being the brand ground" — and it needs
       no help: --on-field #16150F on it is 5.62:1, up from black's 6.46:1
       being the article's worst reading pair in light.
     --rust #c9512c keeps its light hex: 4.09:1 against this ground, over the
       3:1 an input's error border needs, and story/engine.js reads it as a
       chart's `bad` fill where the same figure applies.
     --hl-key #ffe566 and --hl-term #c9a2ff keep their light hexes. A
       highlighter is a bright swipe on any ground and both stay bright here
       (14.50:1 and 8.79:1 against #16150F). They carry BLACK ink by
       construction, and neither has a single consumer in the shipped CSS, so
       nothing reads them today — but a future rule must set their type to
       var(--on-accent) #16150F (14.50:1, 8.79:1) and never var(--ink), which
       is cream here and 1.06:1 on the yellow.
     The nine --wash-n / --ink-n pastel pairs keep their light values.
       (Written with an 'n' rather than a glob: a '*' before the '/' closes
       THIS comment, and the rule below it is then eaten whole by the CSS
       parser's error recovery — which is exactly how the dark block first
       shipped as dead code. No build step parses CSS, so nothing caught it.) They are
       self-contained ground+ink pairs read by story/engine.js:53-60 as canvas
       series fills, so each stays legible against its own ink on any page
       ground. A pale plate on a dark page is a light island by design.
     --btn-brutal, --pill-pipeline, --pill-on-image, --shadow-hairline and
       --shadow-btn-inset carry no line of their own because they are var()
       aliases DECLARED ON :root, and this block re-points their sources on
       THAT SAME ELEMENT — substitution resolves against whichever declaration
       wins the cascade there, so they follow with nothing written. Three are
       restated below anyway, for a reader; the two shadow geometries are not,
       because restating a geometry invites someone to fold a colour into it
       and this file's header explains what that cost last time.

   NO prefers-color-scheme, anywhere, on purpose. `(prefers-color-scheme:
   light)` also matches a browser stating NO preference, so a media query would
   hand the default to a reader who never expressed one. Dark is the default
   and `data-theme` is the only switch. Setting that attribute is another
   lane's file.
   ======================================================================== */
:root[data-theme="dark"] {
  color-scheme: dark;

  /* --- Ground ------------------------------------------------------------ */
  --paper:         #16150F;
  /* A LITERAL, not var(--paper), for the same reason line 65 is one — except
     the reason is inverted here. site/lib/og.test.mjs:272-278 reads --ground
     with `css.match()`, which returns the FIRST match in the file; this block
     sits after :root, so the share card still binds to the light #f9f9f9 and
     the test is unaffected. Written as a literal anyway so the two grounds are
     diffable side by side rather than one being an alias of a name the reader
     has to go and resolve. */
  --ground:        #16150F;
  --surface:       #16150F;
  --surface-alt:   #1c1a14;   /* ink on it 14.68:1                           */
  --lift:          #201d17;   /* ink on it 14.18:1 — the white ration inverts */
  --ground-dark:   #000000;   /* the one band darker than the page, 17.72:1  */

  /* DERIVED. The article's Olive band (site.css:565, :2805) is a ground
     carrying --on-field. Its light #c2e1f6 would survive here — #16150F on it
     is 13.41:1 — but it is a pale ice slab with no other home in this palette,
     and every --ink-coloured edge inside .bg-dark (the ghost button's border
     at site.css:673, the link-arrow rule at :703, the band button's --shadow
     at :2812) would draw cream on it at 1.15:1, i.e. vanish. The dark blue
     line rung is the same band one rung deeper into this theme's own accent:
     text lands at 7.71:1 and those cream edges improve to 2.00:1.
     2.00 IS STILL A FAIL, and this token cannot fix it: .bg-dark takes its
     TYPE from --on-field (dark) and its EDGES from --ink (cream), and no
     single ground satisfies both once those two tokens stop being the same
     colour. That is a site.css repair, named in this lane's report. */
  --band-olive:    #7dadd9;   /* on-field 7.71:1 · cream edges 2.00:1 (see above) */

  /* --- Ink ---------------------------------------------------------------
     The ramp is RECOMPUTED by the light block's own derivation law, with the
     cream as ink this time, not transcribed:

       ground = rgb(22, 21, 15)   ink = rgb(238, 236, 225)
       channel = round( ground_c x (1 - alpha) + ink_c x alpha )   HALF UP

       --ink-muted  70%  ->  173.20, 171.50, 162.00  -> 173,172,162 = #adaca2
       --ink-faint  55%  ->  140.80, 139.25, 130.50  -> 141,139,131 = #8d8b83
       --row-tint    8%  ->   39.28,  38.20,  31.80  ->  39, 38, 32 = #272620

     --ink-faint's blue channel lands on 130.50, a dead half-boundary. ROUND
     HALF UP gives 131 (0x83). CONTRACT s1 prints #8d8b82 there and is wrong by
     its own stated law; the alt-front ships #8d8b83 for that reason and so
     does this. The visible difference is nil (5.36:1 against 5.35:1); the
     arithmetic is not, and honouring the law in one place and not the other is
     how a derivation law stops being one. */
  --ink:           #EEECE1;
  --ink-heading:   #EEECE1;
  --ink-muted:     #adaca2;   /* ink 70% over ground — 8.01:1                */
  --ink-faint:     #8d8b83;   /* ink 55% over ground — 5.36:1, chrome only   */
  --row-tint:      #272620;   /* ink  8% over ground — carries ink 12.80:1   */

  /* The same law sourced from --accent-2 rather than ink, exactly as the light
     --faq-row is — but from THIS theme's blue, rgb(125,173,217), at 10%:
       r: 22x.90 + 125x.10 = 19.80 + 12.50 = 32.30 -> 32 = 20
       g: 21x.90 + 173x.10 = 18.90 + 17.30 = 36.20 -> 36 = 24
       b: 15x.90 + 217x.10 = 13.50 + 21.70 = 35.20 -> 35 = 23
     Ink on it is 13.24:1. */
  --faq-row:       #202423;

  /* Reversals. Every ground in this theme that carries type is dark-on-light,
     so all four collapse to the ground colour. Computed, each on the ground it
     actually lands on:
       --on-accent    #16150F on --accent   #7dadd9  ->  7.71:1  PASSES
       --on-accent-2  #16150F on --accent-2 #7dadd9  ->  7.71:1  PASSES
       --on-field     #16150F on --field    #ff5200  ->  5.62:1  PASSES
     --on-field-display is the STROKE around a display word, not type on a
     ground, and is not a text pair — but it happens to improve, from paper's
     2.94:1 in light to 5.62:1 here. */
  --on-accent:        #16150F;
  --on-accent-2:      #16150F;
  --on-field:         #16150F;
  --on-field-display: #16150F;

  /* The legacy dark-band reversals, re-pointed onto this theme's own ink so a
     .bg-dark section is not the only place on the page still painting #ffffff.
     #c7cad5 would have survived (11.18:1) and is dropped anyway: it is a cool
     grey inside a warm ramp, and the muted rung already exists. */
  --ink-on-dark:       #EEECE1;
  --ink-on-dark-muted: #adaca2;

  /* --- Accent ------------------------------------------------------------
     BLUE, and see THE PALETTE MISMATCH above before treating that as settled.
     In dark, the measured line and its label rung are THE SAME HEX: a blue
     that reads 2.78:1 on cream has to be lifted to be seen on near-black at
     all, and lifting it past 4.5:1 makes the label rung redundant. So --accent
     and --accent-2 hold one value here where light holds two. That is the
     palette telling the truth, not two tokens that want merging.

     --accent-deep is the line rung lightened 35% toward this theme's ink:
       125 + .35x(238-125) = 164.55 -> 165   (and 195, 220)  = #a5c3dc
     9.96:1 — DEEPER contrast than --accent, which is what "deep" means on a
     dark ground and the opposite of what it means on paper. Rules written for
     the light theme that reach for --accent-deep as "the darker one" still get
     the more emphatic one, which is the property they were actually after. */
  --accent:        #7dadd9;   /* 7.71:1 on ground                            */
  --accent-deep:   #a5c3dc;   /* 9.96:1 on ground                            */
  --accent-2:      #7dadd9;   /* one rung, not two — see above               */
  --blue:          #7dadd9;
  --blue-hi:       #7dadd9;
  --blue-lo:       #a5c3dc;
  --blue-inset:    #a5c3dc;
  --btn-brutal:    var(--accent-2);   /* restated for a reader; it follows anyway */

  /* DERIVED — the gold label rung. ::selection (site.css:140), .toc a:hover
     (:2903) and .ai__continue:hover (:3017) all paint this ground and set
     their type to var(--ink), which is cream here, so the light amber #ffac00
     would read 1.06:1. The gold rung carries cream at 4.52:1 and is still a
     visible wash on the page at 3.41:1. */
  --accent-wash:   #7d6923;   /* ink on it 4.52:1 · on ground 3.41:1         */

  --shadow:        #7dadd9;   /* the shadow COLOUR; geometry is in :root     */
  /* DERIVED. The light #022740 is 1.19:1 on this ground — a hard offset and a
     border-color (site.css:629-630) that both disappear. --accent-deep is the
     same role one rung up: 9.96:1, and it is already the deep blue this theme
     names, so the offset under a filled control stays inside the blue family
     rather than becoming a sixth value. */
  /* THE FRAME MUST OUT-CONTRAST THE FACE IT FRAMES. In light this is
     #022740 on #1ba5ff = 5.76:1 — a brutalist edge doing real work. Carried
     across as #a5c3dc it landed on #7dadd9 at 1.29:1, which is not an edge,
     it is a suggestion. Near-black gives 7.71:1 on the same face. */
  --shadow-deep:  #16150F;   /* 9.96:1 on ground                            */

  /* One focus ring, inverted: 2px cream at 3px offset, 15.43:1 on the ground.
     Width and offset do not move — they are geometry, not colour. */
  --focus:         #EEECE1;
  --focus-ring:    #EEECE1;

  /* --- Rules -------------------------------------------------------------
     One colour at three widths, inverted. The widths stay in :root. */
  --rule:          #EEECE1;
  --border:        #EEECE1;
  --border-strong: #EEECE1;   /* also the chart zero baseline, site/lib/chart.mjs */

  /* --- Block display devices — BOTH DERIVED -------------------------------
     Both are grounds that carry var(--ink), so both had to move: cream on the
     light amber is 1.06:1 and on the light pink 1.66:1. Gold and magenta are
     the two label rungs nearest their light hues.
     --highlight is also a MARK (the capsule's 5px bar, site.css:1983), which
     is the second bar it had to clear: 3.41:1 on the ground.
     --marker has NO consumer in the shipped CSS — it is given a value anyway
     so the token is not the one thing in this block that lies about its
     theme. */
  --highlight:     #7d6923;   /* ink on it 4.52:1 · on ground 3.41:1         */
  --marker:        #a24f7a;   /* ink on it 4.50:1 · on ground 3.43:1         */

  /* --- Cluster pills — FIVE DERIVED, ONE ALIAS ----------------------------
     A pill is `background: var(--cluster-ground); color: var(--ink)`
     (site.css:728), and two of the six reverse to var(--on-accent) instead
     (:748, :750). Both of those type tokens flip in this theme, in OPPOSITE
     directions — --ink becomes cream, --on-accent becomes the near-black — so
     four grounds had to go DARK and one had to go LIGHT. Nothing about the
     mapping is hue nostalgia; the reversal each pill carries is what picks it.
     The same five grounds are re-read by .wash-* (site.css:1752-1757), where
     the same reversals apply, so one value serves both.

     Computed, each on the type it actually carries:
       assessment   ink        on #7d6923 gold      4.52:1
       screening    ink        on #476e93 blue      4.52:1
       interviewing ink        on #a24f7a magenta   4.50:1
       policy       on-accent  on #95bb6f green     8.37:1
       teams        ink        on #8e622c orange    4.50:1
       pipeline     on-accent  on the cream ink    15.43:1  (alias, unmoved)

     FOUR OF THE SIX SIT AT 4.50-4.52:1, which passes AA for the 11px uppercase
     the pill is set in and passes it by two hundredths. That is not slack this
     lane spent — it is the label rung's own design: CONTRACT s2 computes every
     one to land JUST over 4.5:1 so the hue stays as close to the measured line
     as legality allows. Do not "round them off" to nicer hexes; each is the
     lightest legal member of its hue and moving it either walks it off the
     palette or under the bar. */
  --pill-assessment:   #7d6923;
  --pill-screening:    #476e93;
  --pill-interviewing: #a24f7a;
  --pill-policy:       #95bb6f;      /* on-accent text — a LIGHT ground      */
  --pill-teams:        #8e622c;
  --pill-pipeline:     var(--ink);   /* on-accent text; follows the cream    */
  --pill-on-image:     var(--paper); /* solid, never an alpha                */

  /* --hl-fact has always held --accent-2's hex verbatim, and it is the one
     highlighter with consumers (site.css:1356, :1816, :2096), all three
     setting their type to var(--on-accent-2). Following the accent keeps that
     pair at 7.71:1 and stops a stranded miro blue from being the only #1ba5ff
     left on a dark page. --hl-key and --hl-term are deliberately absent — see
     the header. */
  --hl-fact:       #7dadd9;   /* on-accent-2 on it 7.71:1                    */

  /* --- Elevation ---------------------------------------------------------
     Only the three that carry a colour. The geometry-only trio and the two
     var() shadows stay in :root and follow. The ink is a literal here for the
     same reason it is one there: it resolves at :root and must not read as
     something a band can re-point. */
  --shadow-card:   3px 3px 0 0 #EEECE1;
  --shadow-lift:   4px 4px 0 0 #EEECE1;
  --shadow-frame:  6px 6px 0 0 #EEECE1;

  /* DERIVED — the hero blooms. Left alone they are the worst pair in the file:
     cream over the unchanged #c2c8ff is 1.36:1 and over #ffd9c2 is 1.11:1, so
     any hero line crossing a bloom disappears. Re-pointed onto two colours
     this theme already names, at an alpha low enough that they read as a glow
     rather than as a second ground, with the composite computed rather than
     eyeballed:
       #7dadd9 at 0.18 over #16150F -> #293033, ink on it 11.32:1
       #ff5200 at 0.14 over #16150F -> #371e0d, ink on it 13.09:1
     Geometry is copied stop for stop (lilac 22%, peach 20%) so the only thing
     that moved is colour. Three rules in site.css still paint these and the
     hero belongs to the CSS lane; if that lane deletes them, delete these. */
  --wash-lilac: radial-gradient(circle, rgba(125,173,217,0.18) 0%, rgba(125,173,217,0.18) 22%, rgba(0,0,0,0) 60%);
  --wash-peach: radial-gradient(circle, rgba(255,82,0,0.14) 0%, rgba(255,82,0,0.14) 20%, rgba(0,0,0,0) 60%);

  /* --- The mark ----------------------------------------------------------
     The two paths swap, which is the whole reason they were split into two
     tokens: the disc and wordmark take the cream, the central character takes
     the page ground, so the mark inverts rather than going invisible. */
  --logo-face:     #16150F;
  --logo-ring:     #EEECE1;
}

@media (prefers-reduced-motion: reduce) {
  :root {
    --dur-fast: 0.01s;
    --dur-base: 0.01s;
    --reveal-y: 0px;
    --reveal-dur: 0.01s;
  }
}


/* ==========================================================================
   Base
   ========================================================================== */

@font-face {
  font-family: Satoshi;
  src: url(../fonts/satoshi-400.woff2) format("woff2");
  font-weight: 400; font-style: normal; font-display: swap;
}
@font-face {
  font-family: Satoshi;
  src: url(../fonts/satoshi-500.woff2) format("woff2");
  font-weight: 500; font-style: normal; font-display: swap;
}
@font-face {
  font-family: Satoshi;
  src: url(../fonts/satoshi-700.woff2) format("woff2");
  font-weight: 700; font-style: normal; font-display: swap;
}
@font-face {
  font-family: Inter;
  src: url(../fonts/inter-latin-400-normal.woff2) format("woff2");
  font-weight: 400; font-style: normal; font-display: swap;
}
@font-face {
  font-family: Inter;
  src: url(../fonts/inter-latin-500-normal.woff2) format("woff2");
  font-weight: 500; font-style: normal; font-display: swap;
}
@font-face {
  font-family: Inter;
  src: url(../fonts/inter-latin-700-normal.woff2) format("woff2");
  font-weight: 700; font-style: normal; font-display: swap;
}

/* --- The editorial faces -------------------------------------------------
   ELEVEN rules, eleven files, one per rule. Ported from olive-stories
   (site/assets/css/story.css:29-63) so olive.is and the corpus render in one
   type system rather than two.

   The six rules above are NOT dead and must not be swept: site/lib/og.mjs
   sets `font-family:Inter` and `font-family:Satoshi` as literal strings in the
   share-card CSS, tools/og-cards.mjs reads the three files by name out of
   dist, and site/lib/layout.mjs preloads two of them on every page. Deleting a
   face something still reads is how a build starts synthesising a fallback
   with nothing reporting it.

   Every src is a relative path into ../fonts/ ending .woff2. No CDN, no Google
   Fonts link, no absolute path — a built page has to render off disk with the
   network off. There are zero .ttf files here; a src pointing at one, or a
   format("truetype") hint, names nothing and fails silently, taking the whole
   type system with it.

   Write those two facts as prose and not as example code: tools/cutover/
   link-check.mjs greps the BUILT stylesheet for url() with a plain regex and
   does not strip comments, so an illustrative reference in a comment is
   resolved against dist like a real one and fails the gate.

   Declare the real font-weight and font-style on every rule or the browser
   synthesises a fake bold or oblique instead of reaching for the drawn one.
   That matters most for Romie: the family on disk is Medium, MediumItalic and
   Bold — there is NO Romie-BoldItalic, so an <em> inside a 700 display rung
   asks for a face that does not exist and gets a synthesised mush. The display
   italic is pinned to 500 in the component sheet for exactly that reason. */

/* Reading + UI — Figtree (SIL OFL, text vendored as OFL-Figtree.txt) */
@font-face { font-family: Figtree; src: url(../fonts/Figtree-Regular.woff2)  format("woff2"); font-weight: 400; font-style: normal; font-display: swap; }
@font-face { font-family: Figtree; src: url(../fonts/Figtree-Medium.woff2)   format("woff2"); font-weight: 500; font-style: normal; font-display: swap; }
@font-face { font-family: Figtree; src: url(../fonts/Figtree-SemiBold.woff2) format("woff2"); font-weight: 600; font-style: normal; font-display: swap; }
@font-face { font-family: Figtree; src: url(../fonts/Figtree-Bold.woff2)     format("woff2"); font-weight: 700; font-style: normal; font-display: swap; }

/* Display — Romie (Margot Leveque / Claude Type). A LICENSED face, not an
   open one, which is why no OFL-Romie.txt sits beside the other two. The
   licence in ROMIE-INTEGRATION.md is the Claude Type all-in-one: priced by
   company headcount, perpetual, and it covers web with no per-domain or
   per-view metering — so serving these from olive.is is the same grant that
   covers olive-stories, not a new one. Two clauses bind the build: no
   modification (so do NOT subset these to cut payload without asking
   concierge@claudetype.com first), and no format conversion beyond what is
   strictly necessary for embedding. The shipped WOFF2 covers the web; do not
   generate other formats.

   Three cuts, not twelve. All twelve weights are 862K of WOFF2 — far too much
   for a display face. Medium carries the italic, Bold carries every upright
   rung (founder, 2026-08-27: every Romie rung is 700 and uppercase). */
@font-face { font-family: Romie; src: url(../fonts/Romie-Medium.woff2)       format("woff2"); font-weight: 500; font-style: normal; font-display: swap; }
@font-face { font-family: Romie; src: url(../fonts/Romie-MediumItalic.woff2) format("woff2"); font-weight: 500; font-style: italic;  font-display: swap; }
@font-face { font-family: Romie; src: url(../fonts/Romie-Bold.woff2)         format("woff2"); font-weight: 700; font-style: normal; font-display: swap; }

/* Data + labels — IBM Plex Mono (SIL OFL, text vendored as OFL-IBMPlexMono.txt) */
@font-face { font-family: "IBM Plex Mono"; src: url(../fonts/IBMPlexMono-Regular.woff2)  format("woff2"); font-weight: 400; font-style: normal; font-display: swap; }
@font-face { font-family: "IBM Plex Mono"; src: url(../fonts/IBMPlexMono-Medium.woff2)   format("woff2"); font-weight: 500; font-style: normal; font-display: swap; }
@font-face { font-family: "IBM Plex Mono"; src: url(../fonts/IBMPlexMono-SemiBold.woff2) format("woff2"); font-weight: 600; font-style: normal; font-display: swap; }
@font-face { font-family: "IBM Plex Mono"; src: url(../fonts/IBMPlexMono-Bold.woff2)     format("woff2"); font-weight: 700; font-style: normal; font-display: swap; }

*, *::before, *::after { box-sizing: border-box; }
* { margin: 0; padding: 0; }

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: auto;      /* Lenis owns scrolling */
}

body {
  font-family: var(--font-body);
  font-size: var(--text-body);
  line-height: var(--leading-body);
  font-weight: var(--weight-regular);
  color: var(--ink);
  background: var(--ground);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  overflow-x: hidden;
}

html.lenis, html.lenis body { height: auto; }
.lenis.lenis-smooth { scroll-behavior: auto !important; }
.lenis.lenis-stopped { overflow: hidden; }

img, svg, canvas, video { display: block; max-width: 100%; }
img { height: auto; }
a { color: inherit; text-decoration: none; }
button { font: inherit; color: inherit; background: none; border: 0; cursor: pointer; }
ul, ol { list-style: none; }

:focus-visible {
  /* One focus ring for the whole site: 2px ink at 3px offset. It replaces a
     4px soft box-shadow halo, which a system with one rule colour has no
     vocabulary for — and which a `border-radius` had to be reapplied to on
     every shape it landed on. `outline` follows the element's own box, so the
     radius line is gone with it. Ink on paper is 19.00:1. */
  outline: var(--focus-width) solid var(--focus);
  outline-offset: var(--focus-offset);
}

/* Amber, not the accent: a green selection behind green link underlines
   erases the one device that marks a link. Ink on it is 11.15:1 on the light
   amber #ffac00 and 4.52:1 on the dark theme's gold #7d6923 — the GROUND
   moves with the theme and the type rides it, so this pair needs no reversal
   of its own. See the note at `.pill--mint` for the surfaces where that is
   true and the one band where it is not. */
::selection { background: var(--accent-wash); color: var(--ink); }

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

.skip-link {
  position: absolute; top: -100px; left: var(--space-5); z-index: 999;
  padding: var(--space-3) var(--space-4);
  background: var(--lift); color: var(--ink);
  border: var(--frame-border) solid var(--rule);
  border-radius: 0;
  box-shadow: var(--shadow-control) var(--shadow);
  transition: top var(--dur-fast) var(--ease);
}
.skip-link:focus { top: var(--space-5); }

/* ==========================================================================
   Typography
   ========================================================================== */

/* THE DISPLAY FACE IS TWO RANKS DEEP, NOT SIX. Romie is set in CAPS at 700
   wherever it is used (founder, 2026-08-27), and that ruling is what decides
   which headings may take it: a rank that carries a headline can shout, a rank
   that labels a card cannot. Measured across the 538 built pages, h3 and h4 are
   not headline ranks here at all — 4,248 of the 6,880 h3s are footer column
   heads, 2,343 are the wrapper around an accordion question, 148 are card
   titles, 47 are step titles, and every one of the 1,062 h4s is the mega-menu's
   feature card. Setting those in a caps serif would put a display voice on a
   nav label. So h1 and h2 keep the display face; h3 and h4 take the body face
   at 600, which is the reference's own card-title rung.

   No rung is a synthesised bold: Romie-Bold.woff2 ships. Tracking is 0 and not
   negative — negative tracking closes up lowercase descenders and shoulders; on
   caps it cramps the counters. */
h1, h2, h3, h4, h5, h6 {
  line-height: var(--leading-heading);
  color: var(--ink-heading);
  text-wrap: balance;
}

h1, h2, .t-display, .t-h2 {
  font-family: var(--font-display);
  font-weight: var(--weight-bold);
  text-transform: uppercase;
  letter-spacing: var(--tracking-display);
}

h3, h4, h5, h6 {
  font-family: var(--font-body);
  font-weight: var(--weight-semi);
  letter-spacing: 0;
}

h1, .t-display { font-size: var(--text-display); line-height: var(--leading-display); }
h2, .t-h2 { font-size: var(--text-h2); }
h3, .t-h3 { font-size: var(--text-h3); }
h4, .t-h4 { font-size: var(--text-h4); letter-spacing: 0; }

/* THE ARTICLE'S OWN TWO RANKS. An answer page is the only page shape that puts
   its h1 and its appended block titles inside `.measure` — verified across all
   538 built pages: `.measure h1` matches the 454 answers and nothing else, and
   every marketing headline is `.c-hero_title` or `.c-page-hero_inner h1`. So
   the reading page can take its own rungs without a renderer change and without
   a class the generator does not emit.

   The h1 drops a rung, 64px to 48px: the display rung is the section front's,
   and an article headline sits under it. */
.measure h1 {
  font-size: var(--text-article-h1);
  line-height: 1.1;
  margin-block: var(--space-4) var(--space-5);
}
/* Common questions, Important notes, References. The reference splits these
   from a reading section deliberately: they take the h2 TYPE without the
   outdented rule, because the rule marks a section of the argument and an
   appended block is not one. */
.measure > h2 {
  font-family: var(--font-body);
  font-weight: var(--weight-semi);
  font-size: var(--text-h2);
  line-height: var(--leading-heading);
  text-transform: none;
  letter-spacing: 0;
}

/* One word per headline is set ITALIC — the single expressive accent in the
   display face. IT IS PINNED TO 500 AND THAT IS THE WHOLE DECLARATION: the two
   ranks it sits inside are 700 now, `em` inherits weight, and there is no
   Romie-BoldItalic on disk (exactly three Romie files ship: Medium,
   MediumItalic, Bold). At 700 the browser would smear the Medium italic into
   the faux bold Romie-Bold was bought to avoid. Add any new display rung to
   this rule in the same edit that gives it a weight. */
.t-em { font-weight: var(--weight-medium); font-style: italic; }

.t-lead {
  font-family: var(--font-body);
  font-size: var(--text-lead);
  /* 1.36, not 1.45, and ink rather than ink-muted. A standfirst is the first
     sentence a reader reads; setting it in the secondary ink made the loudest
     paragraph on the page the palest thing on it. */
  line-height: 1.36;
  color: var(--ink);
  letter-spacing: 0;
  text-wrap: pretty;
}

.t-body { font-size: var(--text-body); line-height: var(--leading-body); }
.t-small { font-size: var(--text-small); line-height: 1.45; color: var(--ink-muted); }

/* THE BYLINE IS THE CAPS-LABEL SPEC EXACTLY, not a near-copy of it. It is
   emitted as `byline t-small`, so it was rendering at the meta rung in
   sentence case — a line that names an author and a date reading as body copy
   rather than as the page's metadata. It takes the same 11px / 600 / 0.07em /
   uppercase every other label on the site takes; the `.t-small` on the element
   is overridden here rather than in the renderer.

   The two separators are chrome and do two different jobs: the pipe divides
   the byline's halves, the middle dot divides two facts inside one line. Both
   are `--ink-faint`, which is what the faint step is FOR and the only place it
   is allowed — it may never set a sentence (4.67:1). */
.byline, .byline.t-small {
  font-family: var(--font-body);
  font-size: var(--text-label);
  font-weight: var(--weight-semi);
  line-height: 1.55;
  text-transform: uppercase;
  letter-spacing: var(--tracking-caps);
  color: var(--ink-muted);
}
.byline time { font-variant-numeric: tabular-nums; }
/* The renderer writes the separator as an inline `opacity:.45` on a bare
   span, which is an unnamed fourth grey; the faint step is the named one, and
   `color` on the parent cannot reach past an inline opacity, so the opacity is
   neutralised here and replaced by the token. */
.byline > span[aria-hidden], nav.t-eyebrow > span[aria-hidden] {
  opacity: 1 !important;
  color: var(--ink-faint);
}
/* The author's name stays TYPE — no colour of its own and no pill, because
   the pill palette is spoken for by the six clusters. The only affordance
   left is the underline: a hairline in its own ink, going accent on hover. */
.byline a, nav.t-eyebrow a {
  border-bottom: var(--rule-hair) solid currentColor;
  transition: border-bottom-color var(--dur-fast) var(--ease);
}
.byline a:hover, nav.t-eyebrow a:hover { border-bottom-color: var(--accent); }

/* `.chart__axis` rides this selector rather than restating it. SVG text is
   painted with `fill` and not `color`, so the class cannot simply be reused on
   a <text> node — but the rung is the same rung, and a second copy of 13px /
   0.04em / uppercase is a second copy that will one day disagree. The one
   property SVG needs is added beside the device, in Takeaways. */
.t-eyebrow, .chart__axis {
  font-family: var(--font-body);
  font-size: var(--text-eyebrow);
  font-weight: var(--weight-semi);
  /* 0.07em, from the token, and it is the ONE tracking value the caps-label
     device has. It was 0.04em here and the reference measures 0.77px at 11px,
     which is 0.07em exactly. A second letterspacing for the same device is how
     two labels beside each other stop looking like one device. */
  letter-spacing: var(--tracking-caps);
  text-transform: uppercase;
  color: var(--ink-muted);
  line-height: 1.55;
}

/* The mono register of the same device — a caps label carrying DATA rather
   than a section name. Same size, same tracking, same caps; the face and the
   accent colour are what say "this is a figure, not a heading". */
.t-eyebrow--mono {
  font-family: var(--font-mono);
  font-size: var(--text-meta);
  font-weight: var(--weight-medium);
  letter-spacing: var(--tracking-caps);
  text-transform: uppercase;
  color: var(--accent);
}

/* THE ITALIC IS GONE FROM HERE, AND IT IS NOT A TASTE CALL. This rung asked
   for the display face at 700 AND italic in one declaration, which names a
   Romie Bold Italic that does not exist on disk — the browser would slant the
   Bold and the result at 64px is the smear the drawn 700 was bought to avoid.
   The display italic is legal at 500 only (see `.t-em`), and a 64px number is
   not the place to spend the page's one expressive accent. Caps 700 with
   tabular figures, so a row of stats lines up. */
.t-stat {
  font-family: var(--font-display);
  font-size: var(--text-stat);
  font-weight: var(--weight-bold);
  text-transform: uppercase;
  font-variant-numeric: tabular-nums;
  letter-spacing: var(--tracking-display);
  line-height: 1;
}

/* THE READING SPEC. Body ink, not the secondary — this was `--ink-muted`,
   which made every paragraph on the site the palest text on its own page and
   reserved full ink for the words around it. The reading floor is also the
   baseline: 18px at 1.67, black on paper at 19.00:1. */
.prose { color: var(--ink); font-size: var(--text-body); line-height: var(--leading-body); }
.prose > * + * { margin-top: 1em; }
.prose p + p { margin-top: var(--space-5); }
.prose strong { color: var(--ink); font-weight: var(--weight-bold); }
.prose em { font-style: italic; }

/* THE LINK IS THE ONLY LINK DEVICE IN THE SYSTEM — there is no quiet,
   secondary or muted variant. A 2px accent rule under a 700 word is the
   promise that a phrase goes somewhere; the hairline-grey underline it
   replaces was the same weight as the text's own leading and read as an
   artefact. `border-bottom` rather than `text-decoration` so the rule sits at
   a fixed distance instead of riding the descenders. */
.prose a,
.c-acc_a a,
.figure__foot a {
  color: var(--ink);
  font-weight: var(--weight-bold);
  text-decoration: none;
  border-bottom: var(--stroke-display) solid var(--accent);
  transition: color var(--dur-fast) var(--ease), border-bottom-color var(--dur-fast) var(--ease);
}
.prose a:hover,
.c-acc_a a:hover,
.figure__foot a:hover { color: var(--accent-deep); border-bottom-color: var(--accent-deep); }

/* The list marker is an EM DASH, not a disc. A coloured dot is a second
   accent spent on punctuation; the dash is type, so it costs the palette
   nothing and survives at any text size. */
.prose ul { margin: 0; }
.prose ul li { position: relative; padding-left: 1.5em; line-height: 1.56; }
.prose ul li + li { margin-top: var(--space-4); }
.prose ul li::before {
  content: "\2014";
  position: absolute; left: 0; top: 0;
  color: var(--ink-muted);
  background: none; width: auto; height: auto; border-radius: 0;
}
.prose ol { counter-reset: n; margin: 0; }
.prose ol li { counter-increment: n; position: relative; padding-left: 1.9em; line-height: 1.56; }
.prose ol li + li { margin-top: var(--space-4); }
.prose ol li::before {
  content: counter(n) ".";
  position: absolute; left: 0; top: 0;
  color: var(--ink-muted); font-weight: var(--weight-semi);
  font-variant-numeric: tabular-nums;
}

/* IN-ARTICLE HEADINGS ARE THE SANS, NOT THE SERIF. The display face is for
   headlines; a heading inside the reading column is structure, and setting it
   in a caps serif would give a subsection the same voice as the page title.

   The left rule is part of the type spec rather than decoration: it hangs into
   the margin so the heading's stem aligns the column, and it doubles as the
   section divider — which is why there is no <hr> anywhere in this file. The
   outdent is `-1 * --space-3 - --frame-border`, computed and not typed, so it
   tracks both tokens if either moves. */
.prose h2, .prose h3, .prose h4 {
  font-family: var(--font-body);
  font-weight: var(--weight-semi);
  text-transform: none;
  letter-spacing: 0;
  color: var(--ink);
}
.prose h2 {
  font-size: var(--text-h2);
  line-height: var(--leading-heading);
  margin-bottom: var(--space-5);
  border-left: var(--frame-border) solid var(--rule);
  margin-left: calc(-1 * var(--space-3) - var(--frame-border));
  padding-left: var(--space-3);
  scroll-margin-top: var(--rail-top);
}
.prose h3 { font-size: var(--text-h3); line-height: 1.29; margin-bottom: var(--space-4); scroll-margin-top: var(--rail-top); }
.prose h4 { font-size: var(--text-lead); line-height: 1.36; margin-bottom: var(--space-3); }

/* Inside a band the outdent would hang into the band's own padding. */
.bg-dark .prose h2, .c-acc_a .prose h2 { border-left: 0; margin-left: 0; padding-left: 0; }

/* The citation superscript. It is a link, so it carries the link device at its
   own size — there is no quieter variant. `line-height: 0` keeps a raised
   figure from opening the line it sits in. */
.prose sup {
  font-family: var(--font-mono);
  font-size: var(--text-label);
  line-height: 0;
  vertical-align: super;
}
.prose sup a { border-bottom-width: var(--rule-hair); }

/* THE CONTENTS NAV IS CHROME, NOT READING TEXT. It ships as `nav.prose`, so
   it inherited the whole reading treatment: an em-dash marker before every
   entry and the 700-weight, 2px-accent-underlined link device on each one — a
   table of contents shouting louder than the argument it indexes. It takes the
   navigation weight instead: 400 at the UI rung, with a quiet underline at
   3px offset that goes accent on hover. */
nav.prose ul li::before { content: none; }
nav.prose ul li { padding-left: 0; }
nav.prose ul li + li { margin-top: var(--space-3); }
nav.prose a {
  font-weight: var(--weight-regular);
  font-size: var(--text-ui);
  border-bottom: 0;
  text-decoration: underline;
  text-decoration-color: var(--ink-faint);
  text-underline-offset: 3px;
  transition: text-decoration-color var(--dur-fast) var(--ease);
}
nav.prose a:hover { color: var(--ink); text-decoration-color: var(--accent); }
nav.prose > .t-eyebrow { margin-bottom: var(--space-4); }

/* THE MARGINALIA, MOVED OFF A FACE THAT WAS NEVER LOADED. This asked for
   "Nanum Pen Script" and nothing in the repo ever shipped it — no @font-face,
   no <link>, no file in site/assets/fonts — so the "whiteboard personality"
   the old comment claimed was Bradley Hand on this Mac and generic cursive
   everywhere else. A fourth face is not available to buy back: the system has
   three, and the aside voice among them is the mono. Set at the meta rung, no
   rotation — a tilted line of text is a sticker, and this is an aside. */
.t-note {
  font-family: var(--font-mono);
  font-size: var(--text-meta);
  color: var(--ink-muted);
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
}
.t-note svg { width: 34px; height: 26px; overflow: visible; }
.t-note svg path { fill: none; stroke: var(--ink-muted); stroke-width: 1.6; stroke-linecap: round; }

/* ==========================================================================
   Layout
   ========================================================================== */

.container {
  width: 100%;
  max-width: calc(var(--container) + var(--gutter) * 2);
  margin-inline: auto;
  padding-inline: var(--gutter);
}
.container--wide { max-width: calc(var(--container-wide) + var(--gutter) * 2); }
.measure { max-width: var(--measure); }
.measure-center { max-width: var(--measure); margin-inline: auto; text-align: center; }

.section { padding-block: var(--section-pad); position: relative; }
.section--lg { padding-block: var(--section-pad-lg); }

/* The intro block above a section's content — eyebrow, h2, lead. One role, so
   one value. It was set as an inline style in six places at three different
   values (64/48/32px), which both varied without a reason and bypassed the
   token scale this file is supposed to own. 64px was already the majority. */
.c-intro { margin-bottom: var(--space-8); }

/* Two bands of the same ground read as one band twice as tall: both pay a full
   --section-pad and no colour change marks the seam, so 96+96=192px of unbroken
   ground opened up on /, /what-we-score and /benchmarks at 1280. A seam between
   DIFFERENT grounds still pays twice — there the colour change is what the
   space is for.

   THE RULE IS UNCHANGED AND ITS REACH IS WIDER, BECAUSE THE PREMISE MOVED.
   It used to name three same-class pairs, since `bg-ground`, `bg-surface` and
   `bg-alt` were three different colours and only a repeated one had no seam.
   They are all `--paper` now, so EVERY adjacent pair of them is a same-ground
   pair and every one was paying twice — measured at 192px of dead ground
   between the article's head and its body, and again between the pricing hero
   and the plans. The exclusions are the two grounds that are still a real
   colour change: the field band and a wash band each keep both pads. */
.section:not(.bg-dark):not(.bg-wash) + .section:not(.bg-dark):not(.bg-wash),
.bg-surface + .bg-surface,
.bg-ground  + .bg-ground,
.bg-alt     + .bg-alt { padding-top: 0; }

/* THE THREE GROUNDS ARE ONE GROUND NOW, and the three class names survive
   only because 538 built pages name them. `--surface` and `--surface-alt` both
   resolve to `--paper`, so a band is separated from its neighbour by a rule or
   by space and never by a second tint — which is why the doubled-padding rule
   above stopped being an optimisation and became the only thing marking a
   seam between two same-ground bands. */
.bg-ground  { background: var(--ground); }
.bg-surface { background: var(--surface); }
.bg-alt     { background: var(--surface-alt); }

/* THE DARK BAND IS THE FIELD BAND. There is no black band in this language —
   `--ground-dark` resolves to ink and a full-bleed black section on a paper
   page reads as a hole rather than as emphasis. The structural loud band is
   orange, carrying INK and never the muted ink (2.77:1 on the field); black on
   the field is 6.46:1. Every reversal below therefore inverts: what used to
   go pale on black now goes full ink on orange. The page still spends exactly
   two of these — this and the footer. */
/* --on-field, not --ink, for the shadow and the mark, AND THIS IS THE ONE
   BAND WHERE THAT DISTINCTION IS LOAD-BEARING. Everywhere else on the site a
   ground and its type invert together — paper/ink, lift/ink, row-tint/ink, all
   six cluster pills — so `var(--ink)` is right and needs no help. This band is
   the exception: it takes its TYPE from --on-field, which is near-black in
   BOTH themes (black on #ff5200 at 6.46:1, #16150F on it at 5.62:1), while
   every edge here used to reach for --ink, which goes CREAM. Cream on the
   field is 2.74:1 and on the reading page's Olive band, which the theme
   re-points to #7dadd9, it is 2.00:1 — so the shadow, the mark, the link
   underline and the focus ring all faded out on a dark page while the words
   beside them stayed black. --on-field takes those same edges to 5.62:1 and
   7.71:1. The token lane named this repair in its own comment at
   tokens.css:597-609 rather than trying to solve it with a third ground: no
   single band colour satisfies a near-black type and a cream edge at once, so
   the edge is what had to move. */
.bg-dark {
  background: var(--field);
  color: var(--on-field);
  --shadow: var(--on-field);
  --logo-ring: var(--on-field);
}
.bg-dark h1, .bg-dark h2, .bg-dark h3, .bg-dark h4 { color: var(--on-field); }
.bg-dark .t-lead, .bg-dark .prose, .bg-dark .t-small, .bg-dark p { color: var(--on-field); }
.bg-dark .t-eyebrow { color: var(--on-field); }
/* Links on the field are ink with an ink underline. The accent never appears
   here: green on orange is 2.14:1. A button and the logo carry their own edge,
   so neither takes the underline. */
.bg-dark a:not(.btn):not(.c-logo):not(.btn-round) {
  color: var(--on-field);
  border-bottom: var(--stroke-display) solid var(--on-field);
}
/* The focus ring is drawn on the band too, so it takes the band's reversal
   rather than the page's: --focus is cream in dark, which is 2.74:1 on the
   field and 2.00:1 on the Olive band — both under the 3:1 a UI edge has to
   clear. --on-field is 5.62:1 and 7.71:1. A focus ring that is only visible
   in one theme is an accessibility regression, not a cosmetic one. */
.bg-dark :focus-visible { outline-color: var(--on-field); }

/* ON A READING PAGE THE LOUD BAND IS BLUE, NOT ORANGE, and the reference
   states the reason in its own stylesheet: the article's closing band is the
   one block that has to be the most confident thing on the page, and on the
   field it was also its least legible — ink on orange is 6.46:1, the lowest
   reading contrast anywhere on the article. On `--band-olive` it is 15.39:1.
   It also stops the page spending its two allowed bands on the same colour:
   the band and the footer now read as two things rather than as one band
   printed twice.

   Scoped by `:has(.measure h1)`, which is the reading-page signal this file
   already uses for the article h1 rung — measured across the whole build, 465
   pages put their h1 in `.measure` (450 answers, 11 toolshed, 3 benchmarks,
   1 essay) and every marketing hero uses `.c-hero_inner` or
   `.c-page-hero_inner` instead, so no marketing band is reached. Only the
   ground changes: ink type, ink links, ink shadow and the cancelled heading
   outdent all come from the rules above and all still hold, because
   `--on-field` is ink on either ground. A browser without `:has()` support
   simply keeps the orange band, which is the state this shipped in. */
body:has(.measure h1) .bg-dark { background: var(--band-olive); }

/* The canvas dot grid, dropped to the tint step. It was painted in
   `--border-strong`, which now resolves to the rule colour — a 1px BLACK dot
   every 24px, which is not a whisper behind content, it is a screen. */
.dotted {
  background-image: radial-gradient(var(--row-tint) 1px, transparent 1px);
  background-size: 24px 24px;
  background-position: -1px -1px;
}

/* ==========================================================================
   Buttons
   ========================================================================== */

/* THE BUTTON IS FLAT AND BRUTALIST NOW: a square face, a 1.5px frame and a
   hard 3px offset shadow with no blur. The gradient-plus-inset-highlight it
   replaces was doing exactly what its comment said — reading as a physical,
   moulded control — and that is the register this design is moving away from.
   A blurred drop shadow over a pale ground reads as dirt; an offset shadow in
   a named colour reads as a decision.

   `min-height`, not `height`. The old fixed 40px gave a label that wraps
   nowhere to go and rendered two lines inside a one-line box. Padding at
   space-3/space-4 over a 16px label at line-height 1 lands on 40px anyway, so
   nothing moves at the default size and a long label can now grow. */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  min-height: 40px;
  padding: var(--space-3) var(--space-4);
  border: var(--frame-border) solid transparent;
  border-radius: 0;
  font-family: var(--font-body);
  font-size: var(--text-ui);
  font-weight: var(--weight-medium);
  line-height: 1;
  white-space: nowrap;
  transition:
    background-color var(--dur-fast) var(--ease),
    box-shadow var(--dur-fast) var(--ease),
    transform var(--dur-fast) var(--ease),
    color var(--dur-fast) var(--ease);
}
/* --lg is a SIZE modifier and not a third style: padding only. */
.btn--lg { min-height: 48px; padding: var(--space-4) var(--space-6); }

/* PRESS, DON'T LIFT. The house physics is one direction: a control travels
   2px DOWN toward its own shadow on hover. Nothing on this site scales up,
   glows, or lifts off the page any more. */
.btn:hover { transform: translateY(var(--press)); }
.btn:active { transform: translateY(calc(var(--press) * 2)); }

/* THE FILLED PRIMARY TAKES AN INK LABEL, AND THAT IS NOT A STYLE CHOICE.
   White on #1BA5FF measures 2.67:1 and fails WCAG AA outright; ink on the same
   blue is 7.87:1. Do not put white back without darkening the ground first,
   which is a different blue and a different decision. The shadow is the deep
   navy rather than the accent, so the control's boundary is identifiable
   against the blue face. */
.btn--primary {
  background: var(--accent-2);
  color: var(--on-accent-2);
  border-color: var(--shadow-deep);
  box-shadow: var(--shadow-control) var(--shadow-deep);
}
.btn--primary:hover { background: var(--accent-2); }

/* The ink control. It keeps the accent for its shadow, because an ink shadow
   under an ink face is invisible. Paper on ink is 19.00:1. */
.btn--dark {
  background: var(--ink);
  color: var(--on-accent);
  border-color: var(--ink);
  box-shadow: var(--shadow-control) var(--accent);
}
.btn--dark:hover { background: var(--ink); }

/* The brutalist secondary: frame, label and offset shadow all in the one blue,
   over the white lift. White is a RATION in this system rather than a ground —
   three named surfaces may take it and this is one of them. */
.btn--secondary {
  --shadow: var(--btn-brutal);
  background: var(--lift);
  color: var(--btn-brutal);
  border-color: var(--btn-brutal);
  box-shadow: var(--shadow-control) var(--shadow);
}
.btn--secondary:hover { background: var(--lift); }

/* The ghost is the quietest member of the same family, not a fourth style:
   the page ground behind an ink hairline, no shadow, so it reads as an outline
   of the secondary rather than as a differently-coloured control. */
.btn--ghost { background: transparent; color: var(--ink); border-color: var(--rule); box-shadow: none; }
/* Only the small ghost takes the tight inset. `.btn--ghost` sits after
   `.btn--lg` at equal specificity, so its 10px was silently beating the large
   size's 24px on every `btn btn--ghost btn--lg` — the header's "Sign in" had
   10px of side padding next to two neighbours at 24px, 30 instances site-wide. */
.btn--ghost:not(.btn--lg) { padding-inline: 10px; }
.btn--ghost:hover { background: var(--row-tint); }

/* On the field band the blue frame goes to ink: blue on orange is 1.21:1 and
   the frame is the control's boundary. */
/* The face and its own type invert TOGETHER — --lift is white on paper and
   #201d17 in dark, --ink is black then cream, so the label reads at 21.00:1
   then 14.18:1 and the control's face still separates from the band it sits
   on (5.17:1 on the field, 7.08:1 on the Olive band). Only the offset shadow
   is drawn on the band itself, so only it takes the reversal. */
.bg-dark .btn--secondary {
  --shadow: var(--on-field);
  background: var(--lift); color: var(--ink); border-color: var(--ink);
}
/* The ghost has no face, so its frame IS on the band. */
.bg-dark .btn--ghost { color: var(--on-field); border-color: var(--on-field); }
.bg-dark .btn--ghost:hover { background: transparent; }

/* The circular arrow affordance. `--radius-pill` survives the move to a
   four-radius system and is spent on exactly this: a round CONTROL, not a
   rounded panel. Flat ink, no blurred plate, and it presses like everything
   else rather than scaling up under the cursor. */
.btn-round {
  display: grid; place-items: center;
  width: 40px; height: 40px; flex: 0 0 auto;
  border-radius: var(--radius-pill);
  background: var(--ink); color: var(--on-accent);
  border: var(--frame-border) solid var(--rule);
  transition: background-color var(--dur-fast) var(--ease), transform var(--dur-fast) var(--ease);
}
.btn-round svg { width: 16px; height: 16px; }
.btn-round--light { background: var(--lift); color: var(--ink); box-shadow: none; }
.btn-round:hover { transform: translateY(var(--press)); }

/* The standalone "Read more >" link: a 3px accent rule under a 700 label at
   the UI rung. It was the accent colour ITSELF at 600 with no rule — coloured
   type doing a link's job, which is the one thing the accent is not for: green
   on paper is 6.29:1 and passes, but it makes the accent mean two things. */
.link-arrow {
  display: inline-flex; align-items: baseline; gap: var(--space-2);
  font-size: var(--text-ui); line-height: 1.4;
  font-weight: var(--weight-bold); color: var(--ink);
  border-bottom: var(--frame-heavy) solid var(--accent);
}
.link-arrow:hover { color: var(--accent-deep); }
.bg-dark .link-arrow { color: var(--on-field); border-bottom-color: var(--on-field); }
.link-arrow svg { width: 15px; height: 15px; transition: transform var(--dur-base) var(--ease); }
.link-arrow:hover svg { transform: translateX(3px); }

/* THE CLUSTER PILL. Square, flat, caps — six saturated grounds, one per
   cluster, replacing nine tinted chips with an inset ring. It keeps the NAME
   "pill" because `assertPillStyles()` in site/lib/stories.mjs spells it that
   way and throws the build if a variant goes missing; the shape moved, the
   vocabulary did not. `--radius-pill` is spent on the one round control and
   nothing else.

   `min-height`, never `height`: a two-word cluster name at a raised text size
   has to be able to grow. The old fixed 26px is why the comment below it had
   to explain which box `align-items` was centring. */
.pill {
  display: inline-flex; align-items: center;
  min-height: 1.625rem;
  padding: var(--space-1) var(--space-2);
  line-height: 1.55;
  border-radius: 0;
  font-family: var(--font-body);
  font-size: var(--text-label);
  font-weight: var(--weight-semi);
  text-transform: uppercase;
  letter-spacing: var(--tracking-caps);
  background: var(--cluster-ground, var(--row-tint));
  color: var(--ink);
  box-shadow: none;
}

/* THE TONE→CLUSTER MAP IS READ OFF THIS REPO'S OWN TABLE, NOT THE
   REFERENCE'S, AND THE TWO DISAGREE ON HALF OF IT. site/lib/stories.mjs:93-100
   maps mint→assessment, cyan→screening, lilac→interviewing, yellow→policy,
   pink→teams, teal→pipeline. The reference's compatibility block maps
   cyan→assessment, mint→screening and orange→teams — copying it verbatim, as
   the obvious move, would have painted three of the six clusters the wrong
   colour with every gate green, because no gate reads a pill's colour.

   Two clusters reverse to paper type and they are the two whose grounds are
   dark: black on green is 3.02:1 and black on ink is 1.00:1, while paper on
   green is 6.29:1 and paper on ink is 19.00:1. Written down here so neither
   gets "fixed" back. */
/* NOT ONE OF THESE SIX TAKES A REVERSAL OF ITS OWN, AND THAT WAS MEASURED
   RATHER THAN ASSUMED. Every cluster ground is re-pointed by the theme
   (tokens.css:730-737), and each one is chosen for the type token this file
   already pairs it with — so the pill and its ink invert together and the two
   lines below are correct in both themes untouched. Computed on the dark
   rungs: assessment 4.52:1, screening 4.52:1, interviewing 4.50:1, teams
   4.50:1 with --ink; policy 8.37:1 and pipeline 15.43:1 with --on-accent.

   The four at 4.50-4.52 are the label rung sitting deliberately just over the
   bar, so DO NOT reach for a "safer" ink here: swapping --ink for the
   near-black on those four grounds measures 3.41 / 3.41 / 3.43 / 3.43 and
   fails, and swapping --on-accent for a near-white on policy's light green
   measures 1.84. Both were tried in this lane and both were wrong. */
.pill--mint   { --cluster-ground: var(--pill-assessment); }
.pill--cyan   { --cluster-ground: var(--pill-screening); }
.pill--lilac  { --cluster-ground: var(--pill-interviewing); }
.pill--yellow { --cluster-ground: var(--pill-policy); color: var(--on-accent); }
.pill--pink   { --cluster-ground: var(--pill-teams); }
.pill--teal   { --cluster-ground: var(--pill-pipeline); color: var(--on-accent); }

/* The three remaining tone names are NOT clusters — the renderers spend them
   on a decorative rotation across card grids and strips. They take the tint
   step rather than a seventh, eighth and ninth colour, because a colour that
   means nothing should not be able to be mistaken for one that does. */
.pill--orange, .pill--blue, .pill--coral { --cluster-ground: var(--row-tint); }

/* The three semantic tones — the one place colour is allowed to carry meaning
   on this site. Flat grounds from the palette, no inset rings. */
.pill--green { --cluster-ground: var(--accent); color: var(--on-accent); }
.pill--amber { --cluster-ground: var(--highlight); color: var(--ink); }
.pill--rust  { --cluster-ground: var(--field); color: var(--on-field); }

/* On the field band a pill is never its cluster colour — teams' orange on
   orange is literally invisible and pipeline's ink ground reads as a hole
   punched in the band. Inside a field band every pill takes ONE construction:
   paper ground, ink outline, ink text at 19.00:1. */
.bg-dark .pill, .c-footer .pill {
  --cluster-ground: var(--paper);
  color: var(--ink);
  box-shadow: inset 0 0 0 var(--frame-border) var(--rule);
}

/* ==========================================================================
   Header — full-width, sticky, with dropdown menus
   ========================================================================== */

.c-header {
  position: sticky;
  top: 0;
  z-index: var(--z-header);
  background: var(--paper);
  /* A RULE, NOT A SHADOW. The header is separated from the page by 1px of
     black and nothing else; the two stacked rgba drop shadows it carried were
     the last thing on the page still trying to float. Present from first
     paint, and nothing about it changes on scroll — no shrink, no shadow fade,
     no hide-on-down — which is most of why the chrome reads as part of the
     page rather than as a bar that reacts to you. */
  border-bottom: var(--rule-hair) solid var(--rule);
  box-shadow: none;
}

.c-header_inner {
  position: relative;   /* the mega-menu viewport is positioned against this */
  display: flex;
  align-items: center;
  gap: var(--space-6);
  /* min-height, never height: the row has to be able to grow at a raised text
     size rather than clipping its own contents. */
  min-height: var(--header-h);
  max-width: calc(var(--container-wide) + var(--gutter) * 2);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

/* The scrim behind an open menu. Shared by the desktop panel and the mobile
   sheet, and sitting *below* the header so the chrome stays crisp over it. */
.c-scrim {
  position: fixed;
  inset: 0;
  z-index: calc(var(--z-header) - 1);
  /* --ground-dark, not --ink. A scrim darkens toward black on BOTH themes;
     --ink is cream on the dark theme, so this veil would have lightened the
     page it is meant to push back. --ground-dark is #000000 in light (it
     aliases ink) and #000000 in dark, which is the one thing here that must
     not invert. */
  background: color-mix(in srgb, var(--ground-dark) 10%, transparent);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.15s ease, visibility 0.15s;
}
body[data-scrim="true"] .c-scrim { opacity: 1; visibility: visible; }

/* The wordmark is the display face, so it is uppercase at 700 like every
   other rung that spends it. Tracking goes to 0 from -0.04em: negative
   tracking closes up lowercase, and on caps at 24px it collides the counters. */
.c-logo {
  display: inline-flex; align-items: center; gap: var(--space-3);
  font-family: var(--font-display);
  /* 18px against a 32px mark, both measured off the reference. It was 24px
     against 34px — proportionate to itself, but a wordmark a third larger than
     the one it is meant to match is the most visible thing on the page to get
     wrong, and the header is the first surface anyone compares. */
  font-size: var(--text-body); font-weight: var(--weight-bold);
  text-transform: uppercase;
  letter-spacing: 0;
  color: var(--logo-ring);
}
.c-logo svg { width: 32px; height: 32px; flex: 0 0 auto; }

/* THE WORDMARK, MATCHED TO THE ALTERNATE FRONT PAGE (founder, 4 Sep: every page
   after the homepage should carry the homepage's logo). Measured before and
   after rather than eyeballed — the site header was rendering the word at 18px
   / weight 700 with `font-feature-settings: normal`, while the front page
   renders it at 16.8px / weight 500 with ss15 ON. The visible difference is
   ss15: it is Romie's "capital pairs, raised second letter" set, and in OLIVE
   it fires on LI and VE, lifting the I and the E. With it off the word is just
   five capitals, which is why the two headers did not look like the same brand.

   This rule is a deliberate copy of site/alt-front-v2/css/story.css's
   `.c-logo_word`, and the two must move together. Three details carried across
   verbatim, each of which breaks the mark if dropped:

     - font-feature-settings REPLACES the browser's default set rather than
       adding to it, so kern and calt are re-listed beside liga and ss15. Write
       only ss15 and you silently switch kerning off for the one word on the
       page set in the brand face.
     - weight 500, because Romie-Medium is a drawn cut and 400 would be
       synthesised.
     - NO letter-spacing, and that is the point rather than an omission. ss15 is
       PAIR-based, so tracking the letters apart works directly against the
       feature: the raised I reads as a stray mark beside the L instead of the
       second half of a pair. The pairs have to touch. `.c-logo` sets
       `letter-spacing: 0` above, which is why this rule does not have to.

   The mark's own SVG and the 32px sizing are untouched; only the word changed. */
.c-logo_word {
  font-family: "Romie", var(--font-serif);
  font-feature-settings: "liga" 1, "ss15" 1, "kern" 1, "calt" 1;
  font-weight: 500;
  font-size: 1.05rem;
}

.c-nav { display: flex; align-items: center; gap: 2px; margin-right: auto; }
/* Static, deliberately: the panel is one shared viewport spanning the header,
   so it must position against .c-header_inner rather than against whichever
   trigger happens to own it. */
.c-nav_item { position: static; }
.c-nav_link {
  /* `height` is fixed at 40px, so a label that wraps has nowhere to go and
     renders two lines inside a one-line box. Measured with six items in the
     bar: "Why Olive" wrapped at every width from 1205px down to the burger
     breakpoint. It already did so from 1101px down with five items — the sixth
     widened a 21px defect to 125px rather than introducing one. */
  white-space: nowrap;
  display: inline-flex; align-items: center; gap: var(--space-2);
  min-height: 40px; padding: 0 var(--space-3);
  border-radius: 0;
  /* THE NAV IS THE CAPS-LABEL DEVICE, not the display face at reading size.
     Two reasons, and the second is the load-bearing one. It is what the
     reference does — a nav item is a label, and labels in this system are
     Figtree 600 at 11px with 0.07em. And the reading floor moved 16px→18px
     when the tokens landed, so a nav set at `--text-body` grew with it and the
     1213px breakpoint below was measured against the OLD 16px. The label rung
     takes the bar back under that measurement with room to spare rather than
     silently overflowing it. */
  font-family: var(--font-body);
  font-size: var(--text-label);
  font-weight: var(--weight-semi);
  text-transform: uppercase;
  letter-spacing: var(--tracking-caps);
  color: var(--ink-muted);
  border-bottom: var(--frame-border) solid transparent;
  transition: color var(--dur-fast) var(--ease), border-bottom-color var(--dur-fast) var(--ease);
}
/* Hover reveals; it does not re-chrome. A tinted plate behind a label is a
   second surface the system has no ground for. */
.c-nav_link:hover { color: var(--ink); border-bottom-color: var(--accent); }
.c-nav_item[data-open="true"] > .c-nav_link { color: var(--ink); border-bottom-color: var(--accent); }
.c-nav_link svg { width: 12px; height: 12px; transition: transform var(--dur-fast) var(--ease); }
.c-nav_item[data-open="true"] > .c-nav_link svg { transform: rotate(180deg); }

.c-actions { display: flex; align-items: center; gap: var(--space-3); }

/* --- the mega-menu ------------------------------------------------------- */

/* One shared viewport, not one panel per trigger: the panel spans the header
   width and its content swaps in place. There is a deliberate 14px dead gap
   below the header — safe to cross, because hover never opens anything. */
.c-mega {
  position: absolute;
  top: 100%;
  /* Inside the page gutter, not against the viewport. The containing block is
     the header's padding box, so `inset-inline: 0` ran the panel edge to edge
     at every width below --container-wide — its 20px corners were sheared off
     at both screen edges and its first column sat left of the logo above it. */
  inset-inline: var(--gutter);
  margin-top: 14px;
  transform-origin: top;
  padding: var(--space-5) var(--space-6);
  /* The panel is a framed surface, so it takes the frame: paper face, 1.5px
     ink border, hard 4px offset shadow. The blurred plate it replaces was the
     only thing on the site still reading as a floating card. */
  background: var(--lift);
  border: var(--frame-border) solid var(--rule);
  border-radius: 0;
  box-shadow: var(--shadow-box) var(--shadow);
  /* A panel is a menu, not a page. Cap it and let it scroll rather than let
     one long column grow until it fills the viewport. */
  max-height: min(calc(100vh - 110px), 520px);
  overflow-y: auto;
  opacity: 0;
  visibility: hidden;
  transform: translateY(-6px);
  transition:
    opacity var(--dur-base) var(--ease),
    transform var(--dur-base) var(--ease),
    visibility var(--dur-base);
}
.c-nav_item[data-open="true"] .c-mega {
  opacity: 1; visibility: visible; transform: translateY(0);
}

/* 1px dividers between columns, never after the last one. */
.c-mega_grid {
  display: grid;
  grid-template-columns: repeat(var(--cols, 2), minmax(200px, 1fr));
  gap: var(--space-2) 0;
  align-items: start;
}
.c-mega_grid > * { padding-inline: var(--space-6); }
.c-mega_grid > *:first-child { padding-left: 0; }
.c-mega_grid > *:not(:last-child) { border-right: var(--rule-hair) solid var(--rule); }
.c-mega_col_title {
  font-family: var(--font-body);
  font-size: var(--text-label); font-weight: var(--weight-semi);
  letter-spacing: var(--tracking-caps); text-transform: uppercase;
  line-height: 1.55;
  color: var(--ink-muted);
  padding: 0 var(--space-3) var(--space-3);
  min-height: 1em;
}
.c-mega_link {
  display: block;
  padding: var(--space-2) var(--space-3);
  border-radius: 0;
  transition: background-color var(--dur-fast) var(--ease);
}
/* The tint step, not a second ground: `--surface-alt` resolves to paper now,
   so a hover painted with it would show nothing at all. */
.c-mega_link:hover { background: var(--row-tint); }
.c-mega_link b {
  display: block;
  /* The menu's link title is a card title, so it is the sans at 600 — the
     display face here would put a caps serif on forty nav entries. Both sizes
     were untokenised literals (0.9375rem / 0.8125rem); they take the meta and
     label rungs, which are the two nearest and are the rungs the rest of the
     chrome uses. */
  font-family: var(--font-body);
  font-size: var(--text-meta);
  line-height: 1.3;
  font-weight: var(--weight-semi);
  color: var(--ink);
}
.c-mega_link span {
  display: block;
  font-size: var(--text-label);
  line-height: 1.45;
  color: var(--ink-muted);
  margin-top: 2px;
}

/* A featured panel rail on the right of the widest menus. */
.c-mega--feature { display: flex; gap: var(--space-6); align-items: flex-start; }
.c-mega--feature .c-mega_grid { flex: 1 1 auto; }
.c-mega_feature {
  flex: 0 0 auto;
  margin-left: auto;
  width: 300px;
  padding: var(--space-5);
  border-radius: 0;
  background: var(--row-tint);
}
.c-mega_feature h4 { font-size: var(--text-body); margin-bottom: var(--space-2); }
.c-mega_feature p { font-size: var(--text-small); color: var(--ink-muted); margin-bottom: var(--space-4); }

/* 1104, not 1080. `white-space: nowrap` above stops the label wrapping but
   cannot create room: measured across 1081-1440 one pixel at a time, six items
   plus the action cluster overflow the header by 1px from 1213px down, and a
   horizontally scrolling page is a worse answer than the burger. Clean at
   1105px and above, which keeps the mega-menu on iPad Pro 11" landscape
   (1194px) and 1152x864. Both 1080 breakpoints move together — the pair swaps
   the bar for the burger, and moving one alone shows neither or both. */
@media (max-width: 1213px) {
  .c-nav, .c-actions .btn--secondary, .c-actions .btn--ghost { display: none; }
}

/* --- mobile -------------------------------------------------------------- */

.c-burger {
  /* Flex column, not grid. As a grid the four children became four auto rows
     that stretched to fill the 40px button, spacing the bars 15px apart — so
     the ±6px close transform left them 18px short of meeting and the X came
     out as a chevron. Flex packs them at their real height. */
  display: none;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  width: 40px; height: 40px;
  border-radius: var(--radius);
}
.c-burger:hover { background: var(--row-tint); }
/* The bars are <i>, not <span>, so the button's visually-hidden <span> label
   cannot be counted by nth-of-type. Selecting them as spans made the label the
   first child and shifted every index by one — the open state rendered as a
   slash over a dash instead of an X, and no attribute selector fixes it
   because nth-of-type counts the type, not the match. */
.c-burger i {
  display: block; width: 18px; height: 2px;
  background: var(--ink);
  transition: transform var(--dur-base) var(--ease), opacity var(--dur-fast);
}
.c-burger i + i { margin-top: 4px; }
body[data-menu-open="true"] .c-burger i:nth-of-type(1) { transform: translateY(6px) rotate(45deg); }
body[data-menu-open="true"] .c-burger i:nth-of-type(2) { opacity: 0; }
body[data-menu-open="true"] .c-burger i:nth-of-type(3) { transform: translateY(-6px) rotate(-45deg); }
@media (max-width: 1213px) { .c-burger { display: flex; } }   /* moves with the rule above */

.c-mobile {
  position: fixed; inset: 72px 0 0;
  z-index: var(--z-menu);
  background: var(--surface);
  overflow-y: auto;
  /* Contain rather than lock: a flick that reaches the end of the sheet must
     not chain into the page behind it. Locking the page would mean an
     overflow container on the root, which un-sticks the header. */
  overscroll-behavior: contain;
  padding: var(--space-5) var(--gutter) var(--space-8);
  opacity: 0; visibility: hidden;
  transition: opacity var(--dur-base) var(--ease), visibility var(--dur-base);
}
body[data-menu-open="true"] .c-mobile { opacity: 1; visibility: visible; }
.c-mobile_group { border-bottom: var(--rule-hair) solid var(--rule); }
.c-mobile_group > button,
.c-mobile_flat {
  display: flex; align-items: center; justify-content: space-between;
  width: 100%; padding: var(--space-4) 0;
  /* The sans, matching the desktop nav: a mobile nav item is the same label
     the bar carries, and the display face is caps-only, which would have set a
     whole sheet of navigation in a serif shout. */
  font-family: var(--font-body); font-size: var(--text-body); font-weight: var(--weight-semi);
  text-align: left;
}
/* An SVG with no intrinsic size inside a flex row stretches to fill it. The
   accordion chevron rendered at ~400px before this. */
.c-mobile_group > button svg,
.c-mobile_flat svg { width: 20px; height: 20px; flex: 0 0 auto; }
.c-mobile_group > button[aria-expanded="true"] svg { transform: rotate(180deg); }
.c-mobile_group > button svg { transition: transform var(--dur-base) var(--ease); }

.c-mobile_sub { display: none; padding-bottom: var(--space-4); }
.c-mobile_sub[data-open="true"] { display: block; }
.c-mobile_sub a { display: block; padding: var(--space-3) 0 var(--space-3) var(--space-4); color: var(--ink-muted); }
.c-mobile_cta { display: grid; gap: var(--space-3); margin-top: var(--space-6); }
.c-mobile_cta .btn { width: 100%; }

/* ==========================================================================
   Hero
   ========================================================================== */

.c-hero {
  position: relative;
  padding-top: clamp(3.5rem, 2rem + 5vw, 6rem);
  padding-bottom: 0;
  background: var(--surface);
  overflow: clip;
}
/* THE TWO RADIAL BLOOMS ARE GONE. A gradient wash is the one thing this
   language has no ground for: the page has a single paper ground, and a
   soft-focus lilac cloud behind a caps serif headline is the register the
   redesign is leaving. The headline carries the fold on type and space now.
   `--wash-lilac` and `--wash-peach` stay declared in tokens.css — they are
   quoted by name in DESIGN_SYSTEM.md, so removing them is the guide's edit and
   not this file's — but nothing here reads them any more. */
.c-hero::before,
.c-hero::after { content: none; }

.c-hero_inner { position: relative; z-index: 1; text-align: center; }
.c-hero_title { max-width: 18ch; margin-inline: auto; }
.c-hero_sub { max-width: 56ch; margin: var(--space-5) auto 0; }

/* Email capture then button — the signature conversion unit. */
.c-capture {
  display: flex; flex-wrap: wrap; gap: var(--space-3);
  justify-content: center;
  width: 100%;
  max-width: 460px;
  margin: 0 auto;
}
/* The chooser and the calculator take this field verbatim rather than growing
   one of their own. The focus ring is an accessibility floor (§8: always
   visible, `--focus-ring` at 4px), and a floor copied into three places is a
   floor that drifts in two of them. `flex` is inert in the grids those two
   devices use. */
.c-capture input,
.lawcard__q input,
.lawcard__q select,
.calc input {
  flex: 1 1 250px;
  min-height: 48px;
  padding: var(--space-3) var(--space-4);
  /* A real border, not a shadow standing in for one. `--shadow-hairline` was
     a 1px ring drawn as an inset box-shadow, which is invisible to a
     high-contrast mode and to a forced-colours stylesheet — the two places a
     form field most needs its edge. */
  border: var(--frame-border) solid var(--rule);
  border-radius: 0;
  background: var(--lift);
  box-shadow: none;
  font-family: var(--font-body);
  font-size: var(--text-ui);
  color: var(--ink);
  transition: box-shadow var(--dur-fast) var(--ease);
}
.c-capture input::placeholder,
.lawcard__q input::placeholder,
.calc input::placeholder { color: var(--ink-muted); }
.c-capture input:focus,
.lawcard__q input:focus,
.lawcard__q select:focus,
.calc input:focus {
  outline: var(--focus-width) solid var(--focus);
  outline-offset: var(--focus-offset);
  box-shadow: none;
}
.c-capture .btn { flex: 0 0 auto; }

/* ---- the contact form ----------------------------------------------------
   Deliberately built on the SAME field rules as .c-capture above rather than
   beside them: the focus ring is an accessibility floor, and a floor copied
   into a second place is a floor that drifts in one of them. Only the things
   a multi-field form needs that a one-field capture does not are written here.

   THE TWO OUTCOME REGIONS ARE HIDDEN BY DEFAULT AND REVEALED BY :target. That
   is the whole no-JavaScript path: the app answers a scriptless post with a
   303 to /contact#sent, the browser addresses the fragment, and these two
   rules do what a second page would otherwise have to. `:has()` is already
   used elsewhere in this codebase, so it is not a new dependency. */
.c-form_grid {
  display: grid; gap: var(--space-4);
  grid-template-columns: repeat(2, minmax(0, 1fr));
}
.c-form_field { display: flex; flex-direction: column; gap: var(--space-2); }
.c-form_field--wide { grid-column: 1 / -1; }
@media (max-width: 640px) { .c-form_grid { grid-template-columns: 1fr; } }

.c-form_label {
  font-family: var(--font-body); font-size: var(--text-ui);
  font-weight: 600; color: var(--ink);
}
/* The OPTIONAL field is the one that is marked. Marking the exception rather
   than the rule means no asterisk convention to explain, no legend, and the
   word lands inside the label's own accessible name with no ARIA at all. */
.c-form_opt { font-weight: 400; color: var(--ink-muted); }

.c-form input,
.c-form select,
.c-form textarea {
  width: 100%;
  min-height: 48px;
  padding: var(--space-3) var(--space-4);
  border: var(--frame-border) solid var(--rule);
  border-radius: 0;
  background: var(--lift);
  box-shadow: none;
  font-family: var(--font-body);
  font-size: var(--text-ui);
  color: var(--ink);
}
.c-form textarea { min-height: 9rem; resize: vertical; }
.c-form input:focus,
.c-form select:focus,
.c-form textarea:focus {
  outline: var(--focus-width) solid var(--focus);
  outline-offset: var(--focus-offset);
  box-shadow: none;
}
.c-form [aria-invalid="true"] { border-color: var(--accent-deep); }

.c-form_actions {
  display: flex; flex-wrap: wrap; align-items: center;
  gap: var(--space-3) var(--space-4);
  margin-top: var(--space-5);
}
.c-form_note { margin: 0; font-size: var(--text-ui); color: var(--ink-muted); }

.c-form_ok, .c-form_bad { display: none; margin-top: var(--space-5); }
.c-form_ok:target, .c-form_bad:target { display: block; }
.measure:has(#sent:target) .c-form,
.measure:has(#couldnt-send:target) .c-form { display: none; }
.c-form_ok[data-shown], .c-form_bad[data-shown] { display: block; }
.measure:has([data-contact-ok][data-shown]) .c-form,
.measure:has([data-contact-bad][data-shown]) .c-form { display: none; }

.c-hero_note { margin-top: var(--space-4); }
.c-hero_actions { display: flex; flex-wrap: wrap; gap: var(--space-3); justify-content: center; margin-top: var(--space-6); }

/* The product shot, floating in a rounded frame that runs off the fold. */
.c-frame {
  position: relative;
  margin-top: var(--space-8);
  /* The heavy frame — a MOUNT, not a control. It is the one surface that gets
     the 16px radius and the 3px border, and it deliberately carries no offset
     shadow and never presses: those two properties are how a mount is told
     apart from something you can click. */
  border-radius: var(--radius-heavy) var(--radius-heavy) 0 0;
  overflow: hidden;
  background: var(--lift);
  border: var(--frame-heavy) solid var(--rule);
  border-bottom: 0;
  box-shadow: none;
}
.c-frame--full { border-radius: var(--radius-heavy); border-bottom: var(--frame-heavy) solid var(--rule); }
.c-frame img, .c-frame > svg { width: 100%; display: block; }

/* ==========================================================================
   Page hero (inner pages)
   ========================================================================== */

.c-page-hero {
  position: relative;
  padding-top: clamp(2.5rem, 1.5rem + 4vw, 4.5rem);
  padding-bottom: clamp(2.5rem, 1.5rem + 4vw, 4.5rem);
  background: var(--surface);
  overflow: clip;
}
.c-page-hero::before {
  content: "";
  position: absolute; right: -260px; top: -160px;
  width: 900px; height: 560px;
  background: none;
  pointer-events: none;
}
.c-page-hero_inner { position: relative; z-index: 1; max-width: 42rem; }
.c-page-hero_inner > * + * { margin-top: var(--space-5); }
.c-page-hero_actions { display: flex; flex-wrap: wrap; gap: var(--space-3); }

/* ==========================================================================
   Cards
   ========================================================================== */

.c-cards {
  display: grid;
  gap: var(--space-5);
  grid-template-columns: repeat(auto-fill, minmax(min(300px, 100%), 1fr));
}
.c-cards--2 { grid-template-columns: repeat(auto-fit, minmax(min(420px, 100%), 1fr)); }

.c-card {
  display: flex;
  flex-direction: column;
  padding: var(--space-6);
  /* A card on paper with no frame is invisible when the page has one neutral,
     and a card here is interactive — so it takes the brutalist frame and it
     presses. The blurred plate it replaces was carrying the card's edge as a
     6% shadow, which on a white page reads as depth and on a warm paper ground
     reads as a smudge. */
  background: var(--lift);
  border: var(--frame-border) solid var(--rule);
  border-radius: 0;
  box-shadow: var(--shadow-control) var(--shadow);
  transition: transform var(--dur-fast) var(--ease);
}
/* PRESS, DON'T LIFT — the same physics as the button, in the same direction. */
a.c-card:hover { transform: translateY(var(--press)); }
.c-card_top { display: flex; align-items: flex-start; justify-content: space-between; gap: var(--space-4); margin-bottom: var(--space-5); }
.c-card h3 { font-size: var(--text-lead); line-height: 1.27; }
.c-card_body { margin-top: var(--space-3); color: var(--ink-muted); }
.c-card_foot { margin-top: auto; padding-top: var(--space-5); }
/* The loud card is the field band's card-sized sibling: orange ground, ink
   type at 6.46:1, ink shadow — never the muted ink, which is 2.77:1 there. */
.c-card--dark {
  background: var(--field); color: var(--on-field);
  --shadow: var(--ink);
  border-color: var(--rule);
}
.c-card--dark h3 { color: var(--on-field); }
.c-card--dark .c-card_body { color: var(--on-field); }

/* A media well inside a card — where a product shot or diagram sits. */
.c-card_media {
  /* Full-bleed to the card's top corners. The plate's wash is painted by the
     SVG itself, so no padding and no background here — a second frame around
     a framed illustration is the tell of a bolted-on image slot. */
  /* The negative margin now has to swallow the card's border as well as its
     padding, or the plate leaves a 1.5px paper seam at the top corners. */
  margin: calc((var(--space-6) + var(--frame-border)) * -1) calc((var(--space-6) + var(--frame-border)) * -1) var(--space-5);
  border-radius: 0;
  overflow: hidden;
}
.c-card_media svg, .c-card_media img { display: block; width: 100%; }

/* THE PLATE PALETTE, AND IT SWAPS WITH THE THEME.
   media.mjs emits each plate's family pair on the SVG as --w (the wash) and --k
   (its ink), because only the renderer knows which family a dimension belongs
   to. Only CSS knows the theme. So the renderer supplies both halves and this
   decides which one is ground and which one is mark.

   LIGHT is unchanged, exactly: ground = the wash, marks = its ink, sheets
   white, rules the same grey they always were. Nothing moves.

   DARK is the fix. Measured before it existed, every plate sat between 13.33:1
   and 15.73:1 against the --lift card it lives in — not a tint, a near-white
   slab on a dark page, on a site whose DEFAULT is dark. The rule taken is the
   alternate front page's own, from its six-dimension census: the pair swaps.
   The plate becomes a recessed well at --paper, its sheets take --lift, its
   rules take --hairline, and the family's WASH carries every mark. The hue
   moves onto the drawing instead of sitting behind it, and the plate reads as
   part of the card rather than a window cut in it.
   REJECTED, and measured rather than argued: tinting the well with the family
   wash at 12% gives six grounds between #353730 and #3b352d — six near-identical
   browns with the hue gone, and the white sheets fall to 1.08-1.11:1 against
   them. One uniform well at a token that already exists beats six hand-tuned
   alphas that all look the same. */
.c-card_media svg, .c-rowmedia svg {
  --plate-bg: var(--w);
  --plate-fg: var(--k);
  --plate-sheet: #ffffff;
  --plate-rule: #e4e6ec;
}
:root[data-theme="dark"] .c-card_media svg,
:root[data-theme="dark"] .c-rowmedia svg {
  --plate-bg: var(--paper);
  --plate-fg: var(--w);
  --plate-sheet: var(--lift);
  --plate-rule: var(--hairline);
}

/* A card with media puts its pill row under the plate, not over it. */
.c-card--media .c-card_top { margin-top: 0; }

/* ==========================================================================
   Carousel — horizontally scrolling card rail with round controls
   ========================================================================== */

.c-rail_head { display: flex; align-items: flex-end; justify-content: space-between; gap: var(--space-5); margin-bottom: var(--space-6); }
.c-rail_ctrls { display: flex; gap: var(--space-3); flex: 0 0 auto; }
.c-rail_ctrls button:disabled { opacity: 0.35; cursor: default; }
.c-rail_ctrls button:disabled:hover { transform: none; }

.c-rail_track {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: minmax(300px, 340px);
  gap: var(--space-5);
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  scroll-behavior: smooth;
  padding-bottom: var(--space-2);
  scrollbar-width: none;
}
.c-rail_track::-webkit-scrollbar { display: none; }
.c-rail_track > * { scroll-snap-align: start; }

/* ==========================================================================
   Feature rows
   ========================================================================== */

.c-split {
  display: grid;
  grid-template-columns: repeat(12, minmax(0, 1fr));
  gap: var(--space-6);
  align-items: center;
}
.c-split_text { grid-column: 1 / span 5; }
.c-split_media { grid-column: 7 / span 6; }
.c-split--flip .c-split_text { grid-column: 8 / span 5; order: 2; }
.c-split--flip .c-split_media { grid-column: 1 / span 6; order: 1; }
.c-split_text > * + * { margin-top: var(--space-4); }
.c-split_text .btn { margin-top: var(--space-5); }

@media (max-width: 900px) {
  .c-split, .c-split--flip { display: block; }
  .c-split_media { margin-top: var(--space-6); }
}

/* ==========================================================================
   Stats
   ========================================================================== */

.c-stats {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(220px, 100%), 1fr));
  /* One gap for peer card grids: --space-5 is what .c-cards and .c-plans
     already use, and a stats row at 48px next to a cards row at 24px reads as
     two different rhythms with nothing separating them semantically. */
  gap: var(--space-5);
  text-align: center;
}
.c-stat_label { margin-top: var(--space-3); color: var(--ink-muted); }

/* ==========================================================================
   Steps
   ========================================================================== */

.c-steps { counter-reset: s; display: grid; gap: var(--space-5); }
.c-steps--3 { grid-template-columns: repeat(auto-fit, minmax(min(280px, 100%), 1fr)); }
.c-step { counter-increment: s; }
.c-step_num {
  display: grid; place-items: center;
  width: 32px; height: 32px; margin-bottom: var(--space-4);
  /* Same line-box trap as `.pill`: `place-items: center` centers the LINE BOX,
     and an inherited 1.5 put the digit 0.63px low inside its disc (measured at
     8x — ink centre 132.5 against a disc centre of 127.5). */
  line-height: 1;
  border-radius: var(--radius-pill);
  /* Set by the rotation below; these are the fallbacks. Mono figures because
     it is a number in a sequence and a column of them should line up. */
  background: var(--row-tint); color: var(--ink);
  font-family: var(--font-mono);
  font-size: var(--text-meta); font-weight: var(--weight-semi);
  font-variant-numeric: tabular-nums;
}
.c-step_num::before { content: counter(s); }
.c-step h3 { font-size: var(--text-lead); line-height: 1.27; margin-bottom: var(--space-3); }
.c-step p { color: var(--ink-muted); }
.c-step_meta { margin-top: var(--space-3); }

/* ==========================================================================
   Accordion
   ========================================================================== */

/* THE FAQ ROW IS A ROW, not a hairline-separated list item. It takes the
   derived baby blue — `--accent-2` at 10% over paper, so the row, the
   disclosure circle and the filled button read as one family — and rows are
   separated by the gap between them rather than by a rule. Ink on it is
   17.31:1. */
.c-acc_item {
  border-bottom: 0;
  background: var(--faq-row);
  border-radius: var(--radius-card);
}
.c-acc_item + .c-acc_item { margin-top: var(--space-3); }
.c-acc_q {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--space-4); width: 100%;
  min-height: var(--space-8);
  padding: var(--space-4) var(--space-5);
  /* The question is a control's label, not a headline: the sans at 700 on the
     UI rung. It was the display face at 20px, which after the face swap would
     have set 2,343 questions in a caps serif. */
  font-family: var(--font-body);
  font-size: var(--text-ui);
  line-height: 1.4;
  font-weight: var(--weight-bold);
  letter-spacing: 0;
  text-align: left;
  color: var(--ink);
}
/* The disclosure circle: one non-wrapping glyph, and `--radius-pill`'s second
   and last consumer. The + rotates into an x. */
.c-acc_icon {
  flex: 0 0 auto; display: grid; place-items: center;
  width: 30px; height: 30px; border-radius: var(--radius-pill);
  background: var(--hl-fact); color: var(--on-accent-2);
  transition: transform var(--dur-base) var(--ease);
}
.c-acc_icon svg { width: 14px; height: 14px; }
.c-acc_item[data-open="true"] .c-acc_icon { transform: rotate(45deg); }
.c-acc_a { display: grid; grid-template-rows: 0fr; transition: grid-template-rows var(--dur-base) var(--ease); }
.c-acc_item[data-open="true"] .c-acc_a { grid-template-rows: 1fr; }
.c-acc_a > div { overflow: hidden; }
.c-acc_a > div > div { padding: 0 var(--space-5) var(--space-5); max-width: 72ch; color: var(--ink); }

/* ==========================================================================
   Table
   ========================================================================== */

/* A LADDER, NOT A CARD. The table is ruled horizontally in one colour at
   three widths — 3px closes the head and the table, 1px divides the rows — and
   there are no vertical rules and no banding. It used to sit on a white plate
   with a blurred edge, which framed the data instead of structuring it.
   The container scrolls itself; the page never does. */
.c-table-wrap {
  overflow-x: auto;
  overscroll-behavior-x: contain;
  background: none;
  border-radius: 0;
  box-shadow: none;
  margin-block: var(--space-6);
}
.c-table { width: 100%; border-collapse: collapse; min-width: 680px; }
.c-table th, .c-table td {
  padding: var(--space-3) var(--space-4);
  text-align: left; vertical-align: top;
  border-bottom: 0;
}
/* Cell text is never muted — it is the reading text of the table. */
.c-table td, .c-table th { color: var(--ink); font-size: var(--text-body); line-height: 1.56; }
/* A column head is a LABEL, so it takes the caps-label spec. */
.c-table thead th, .c-table th:not([scope="row"]) {
  font-family: var(--font-body);
  font-size: var(--text-label);
  font-weight: var(--weight-semi);
  text-transform: uppercase;
  letter-spacing: var(--tracking-caps);
  line-height: 1.55;
  color: var(--ink-muted);
  border-bottom: var(--rule-section) solid var(--rule);
}
.c-table tbody tr + tr td, .c-table tbody tr + tr th { border-top: var(--rule-hair) solid var(--rule); }
.c-table tbody tr:last-child td, .c-table tbody tr:last-child th { border-bottom: var(--rule-section) solid var(--rule); }
.c-table th[scope="row"] { color: var(--ink); font-weight: var(--weight-semi); font-size: var(--text-body); }
.c-table th:first-child, .c-table td:first-child { padding-inline-start: 0; }
.c-table th:last-child, .c-table td:last-child { padding-inline-end: 0; }

/* ==========================================================================
   Pricing
   ========================================================================== */

.c-plans { display: grid; gap: var(--space-5); grid-template-columns: repeat(auto-fit, minmax(min(280px, 100%), 1fr)); }
.c-plan {
  display: flex; flex-direction: column;
  padding: var(--space-6);
  background: var(--lift);
  border: var(--frame-border) solid var(--rule);
  border-radius: 0;
  box-shadow: var(--shadow-control) var(--shadow);
}
/* The featured plan is told apart by a HEAVIER frame and a deeper shadow, not
   by a second colour: a 2px blue ring around one card of three was the only
   place on the site where the accent identified a surface rather than an
   action. */
.c-plan--featured { border-width: var(--frame-heavy); box-shadow: var(--shadow-box) var(--shadow); }
/* A price is a figure, so it takes the data register — mono at 600 with
   tabular figures, which is also what makes a column of prices line up. */
.c-plan_price { font-family: var(--font-mono); font-size: var(--text-h3); font-weight: var(--weight-semi); font-variant-numeric: tabular-nums; letter-spacing: 0; margin: var(--space-4) 0 var(--space-2); }
.c-plan_list { margin: var(--space-5) 0 var(--space-6); display: grid; gap: var(--space-3); }
.c-plan_list li { position: relative; padding-left: 26px; color: var(--ink); line-height: 1.56; }
.c-plan_list li::before {
  content: ""; position: absolute; left: 0; top: 8px;
  width: 14px; height: 8px;
  border-left: var(--stroke-display) solid var(--accent); border-bottom: var(--stroke-display) solid var(--accent);
  transform: rotate(-45deg);
}
.c-plan .btn { margin-top: auto; }

/* ==========================================================================
   Quote
   ========================================================================== */

/* THE PULL QUOTE IS THE SANS, AND THAT IS A CONSEQUENCE OF THE CAPS RULING.
   The display face is set in caps wherever it is used, and a pull quote is a
   SENTENCE — a whole sentence in caps is shouting, and the reference is
   explicit that its display face is never a sentence. So the quote keeps the
   h3 rung and the display size, and takes the reading face at 600. */
.c-quote { max-width: 32ch; font-family: var(--font-body); font-size: var(--text-h3); font-weight: var(--weight-semi); letter-spacing: 0; line-height: 1.29; color: var(--ink); }
.c-quote_attr { margin-top: var(--space-5); color: var(--ink-muted); font-size: var(--text-meta); }
/* On the field band the attribution is full ink: the muted step is 2.77:1 on
   orange and fails at every size. */
.bg-dark .c-quote_attr { color: var(--on-field); }

/* ==========================================================================
   Editorial list
   ========================================================================== */

.c-list { border-top: var(--rule-hair) solid var(--rule); }
.c-list_item { border-bottom: var(--rule-hair) solid var(--rule); }
.c-list_link {
  /* minmax(0, 1fr), not 1fr: a grid track's default min-width is auto, so a
     row of unwrapping meta pills refused to shrink and pushed the arrow past
     the viewport edge on mobile. */
  display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: var(--space-6);
  align-items: center; padding: var(--space-5) 0;
}
/* THE ROW NO LONGER MOVES. It used to take `padding-inline: var(--space-3)` on
   hover, transitioned, which is not a highlight but a RELAYOUT: the padding is
   on the grid container, so every hover reflowed the row's two tracks and the
   title, the body line and the meta pills all slid 12px right and back. The
   founder, with the D1/D2/D3 dimension list on screen: "Across the app, we use
   a list where each row slightly shifts right with a hover. Just draw an
   underline with the hover and remove the shift on ALL pages that use this
   list." One rule served every one of them — `.c-list_link` is the row in
   sections.mjs's `list`, in laws.mjs's jurisdiction shelf and in toolshed.mjs's
   document shelf — so the shift is gone everywhere in one edit.

   WHAT GETS UNDERLINED IS THE TITLE AND ONLY THE TITLE. The whole row is one
   anchor, but it carries a name, a body line and a row of meta pills; a rule
   under all three would be three rules and would read as noise rather than as
   a link. The title is the row's name and the thing being followed.

   TEXT-DECORATION, NOT A BORDER, WHICH IS A DEPARTURE FROM site.css:355. The
   prose link's comment rejects text-decoration because a plain underline
   "rides the descenders" and a border sits at a fixed distance. That was true
   before `text-underline-offset`, which pins the distance exactly as a border
   does while `text-decoration-skip-ink` still breaks the rule around a
   descender. Both, rather than one or the other.

   The colour is --accent, which is what every other hover underline on this
   site draws in (.byline, .c-nav_link, .related), and the thickness is
   --stroke-display, the token whose own comment names it "link underlines". No
   new colour is introduced and none needs one: --accent #00682e measures
   6.29:1 on --paper #f9f9f9, and #7dadd9 measures 7.71:1 on the dark ground
   (tokens.css:682) — a rule under type owes 3:1. The title's own ink is
   untouched; the underline is added to it and is never a recolour.

   The underline is always PRESENT and merely transparent at rest, so the only
   thing hover changes is one colour. Nothing about text-decoration reflows,
   but declaring it once also means the transition has two ends. */
.c-list_title {
  text-decoration-line: underline;
  text-decoration-color: transparent;
  text-decoration-thickness: var(--stroke-display);
  text-underline-offset: 0.18em;
  transition: text-decoration-color var(--dur-fast) var(--ease);
}
.c-list_link:hover .c-list_title,
.c-list_link:focus-visible .c-list_title { text-decoration-color: var(--accent); }

@media (prefers-reduced-motion: reduce) {
  .c-list_title { transition: none; }
}
/* THE RELATED ROW'S PILL CARRIES A COLOUR THAT LIES, AND THIS IS THE FIX CSS
   CAN REACH. site/lib/sections.mjs:546 picks the row's tone from the item's
   INDEX — `pill--${wash(n === 0 ? i : i + 3)}` — not from its cluster, so on
   the answer page a row labelled "Policy" renders `.pill--mint` and the next
   row labelled "Screening" renders `.pill--lilac`. The label and the colour
   already disagreed; while every tone was a pastel nobody could see it, and
   binding the tones to real cluster grounds would have made a wrong colour
   confident.

   The reference does not have this problem because it dropped the pill from
   the related row entirely, for a caps label over the headline. That is the
   shape reached here: inside a related row the pill loses its ground and
   becomes the caps label it always was, so the row states its cluster in words
   — which are correct — and states nothing in colour, which was not. */
.c-list_meta { display: flex; flex-wrap: wrap; gap: var(--space-2) var(--space-4); margin-bottom: var(--space-2); }
.c-list_meta .pill {
  --cluster-ground: transparent;
  color: var(--ink-muted);
  padding: 0;
  min-height: 0;
  box-shadow: none;
}
.c-list_title { display: block; font-family: var(--font-body); font-size: var(--text-lead); font-weight: var(--weight-semi); line-height: 1.27; letter-spacing: 0; color: var(--ink); }
.c-list_body { display: block; margin-top: var(--space-2); color: var(--ink-muted); font-size: var(--text-meta); line-height: 1.43; max-width: 72ch; }

/* ==========================================================================
   Footer — dark, six columns
   ========================================================================== */

/* THE FOOTER IS THE PAGE'S SECOND AND LAST LOUD BAND, and it is the field:
   orange ground, ink type at 6.46:1, ink rules. It was a near-black slab —
   the one surface still carrying the old dark register, and the thing a
   reader's eye lands on at the end of every page. Everything inside takes
   `--on-field`: never the muted ink, which is 2.77:1 here. */
/* Same reversal as `.bg-dark`, and for the same reason: the footer's ground
   is --field and its type is --on-field, which is near-black in both themes,
   so every EDGE on it takes --on-field too (6.46:1 light, 5.62:1 dark) rather
   than --ink, which goes cream and reads 2.74:1 here. */
.c-footer {
  background: var(--field);
  color: var(--on-field);
  --shadow: var(--on-field);
  --logo-ring: var(--on-field);
  padding-block: var(--section-pad) var(--space-6);
}
.c-footer_cols {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: var(--space-7) var(--space-5);
  padding-bottom: var(--space-8);
}
/* The column head is the caps-label device in its MONO register — the same
   size, tracking and caps as every other label, with the face saying "this
   names a group" rather than "this is a heading". It is the reference's own
   footer treatment. */
.c-footer_col h3 {
  font-family: var(--font-mono);
  font-size: var(--text-meta); font-weight: var(--weight-medium);
  text-transform: uppercase; letter-spacing: var(--tracking-caps);
  color: var(--on-field);
  margin-bottom: var(--space-3);
}
.c-footer_col li + li { margin-top: var(--space-2); }
/* 14px, matching the reference's footer measured at 1440. The footer is the
   one place on the page where a list of thirty-odd links has to read as a
   directory rather than as prose, and at the 18px reading floor it reads as
   prose. --text-meta is the token that already means "smaller than reading". */
.c-footer_col a { font-size: var(--text-meta); color: var(--on-field); border-bottom: var(--rule-hair) solid transparent; transition: border-bottom-color var(--dur-fast) var(--ease); }
.c-footer_col a:hover { color: var(--on-field); border-bottom-color: var(--on-field); }
.c-footer_base {
  display: flex; flex-wrap: wrap; gap: var(--space-4) var(--space-6);
  align-items: center; justify-content: space-between;
  padding-top: var(--space-5);
  border-top: var(--rule-hair) solid var(--on-field);
  color: var(--on-field);
  font-size: var(--text-meta);
}

/* --------------------------------------------------------------------------
   THE THEME CONTROL IS A FIXED CHIP, BOTTOM RIGHT, ON EVERY PAGE.

   It is emitted inside `.c-footer_base` by site/build.mjs, and that file's
   comment argues at length for the footer row and against the alternate front
   page's floating chip, on the grounds that the bottom right corner is already
   the sticky conversion bar's. The founder overruled it: the switch is to sit
   where the alternate page puts it, on every page. build.mjs is not this
   lane's file and its markup does not need to change — a fixed chip is a
   position, and a position is CSS. So the relocation lives here.

   THE THREE THINGS THAT CHANGE, AND WHY EACH ONE HAD TO.

   1. POSITION. `bottom: 20px; right: 20px; z-index: 95` — the alternate page's
      own numbers (alt-front/src/parts/layout.css:193-207), literal rather than
      tokenised, because they are a copy of a specific control and not a new
      point on the space scale. 95 is under --z-header (100) there and here, so
      the chip never covers the sticky header or the mobile sheet (--z-menu).

   2. THE GROUND AND THE EDGE. The footer control borrowed its two colours from
      the band it stood on: `currentColor` for the edge and the --on-field /
      --field pair, reversed, for the active rung. Off the band that is not a
      near miss, it is invisible — in the dark theme --on-field is #16150F and
      so is --paper, so the edge, the inactive label and the chip's own ground
      would all have been the same colour as the page behind them.
      The fix keeps build.mjs's contract rather than fighting its selectors:
      that file says the control's contrast IS its band's, so the two custom
      properties it reads are re-pointed here to the page's pair. Nothing in
      the appended stylesheet has to change, and it cannot drift, because it
      still names the same two variables.
      Measured, both themes, the reversed active rung:
        light  --paper #f9f9f9 type on --ink #000000 ground  = 19.00:1
        dark   --paper #16150F type on --ink #EEECE1 ground  = 15.44:1
      And the resting label, --ink-muted on the wash:
        light  #4b4b4b on #f9f9f9   =  8.15:1   (tokens.css:117)
        dark   #adaca2 on #16150F   =  8.01:1   (tokens.css:635)
      The chip floats, so the worst ground under it is the footer's own orange
      band, which the wash lets through at 10%. Composited and measured there:
        light  #4b4b4b on #fce1da   =  7.27:1
        dark   #adaca2 on #2d1b0e   =  7.21:1
      The edge, --rule, is 16.94:1 and 13.88:1 on those same two composites.
      Every figure above the 4.5:1 the label owes and the 3:1 the edge does.

   3. THE SLIDING BAR. `.c-sticky` is fixed at bottom 0, full width, at
      --z-header, and shows only past the first screen and only from 1024px up.
      Measured in a real browser at 1440 and at 1280 it is 92px tall, so a chip
      at z-index 95 sitting 20px off the bottom is entirely behind it. It rides
      above the bar while the bar is up and drops back when it retracts. The
      lift is media-queried to 1024px because `data-show` stays "true" below
      that width — the bar is hidden with `display: none`, not with the
      attribute — and an unguarded `:has()` would have lifted the chip 92px off
      the bottom of a phone with no bar to clear. `:has()` is already load
      bearing in this file (site.css:589, :2743).
      92px is a measurement, not an arithmetic: the bar's height is its
      content's. Re-measure it if the bar's copy or its button changes.
   -------------------------------------------------------------------------- */
/* THE FOOTER RESERVES THE BAND THE CHIP FLOATS IN. A fixed chip covers
   whatever is at the bottom right of the viewport, and at the bottom of the
   page that is the footer's last row — measured before this rule, at 1440 the
   chip sat squarely on the mail address in `.c-footer_base`, which is a link.
   Everywhere else on the page the chip covering body copy is the design (it is
   what the alternate front page does); covering a LINK is not. The footer's
   closing padding grows from --space-6 to the chip's own band, so the last row
   ends above it with the same 20px the chip keeps from the two edges. */
.c-footer {
  --chip-inset: 20px;
  /* Measured at 35px in both themes; 2.5rem is that plus the headroom a reader
     who has raised their text size needs, since the chip is sized in rem. */
  --chip-h: 2.5rem;
  padding-bottom: calc(var(--chip-inset) * 2 + var(--chip-h));
}

.c-footer .c-theme {
  --chip-wash: color-mix(in srgb, var(--paper) 90%, transparent);
  /* Measured at 1440 and 1280: `.c-sticky` is 92px (89px inner + its 3px top rule). */
  --sticky-h: 92px;

  position: fixed;
  right: var(--chip-inset);
  bottom: var(--chip-inset);
  z-index: 95;

  /* Re-pointed for the page, not the band. See (2) above. */
  --field: var(--paper);
  --on-field: var(--ink);

  transition: bottom var(--dur-base) var(--ease-in-out);
}

.c-footer .c-theme_btn {
  background: var(--chip-wash);
  -webkit-backdrop-filter: blur(6px);
  backdrop-filter: blur(6px);
  border: var(--rule-hair) solid var(--rule);
  color: var(--ink-muted);
  /* The caps-label spec, which is what the alternate page's 11px tracked type
     is in this system's own vocabulary. */
  font-size: var(--text-label);
  letter-spacing: var(--tracking-caps);
}

@media (min-width: 1024px) {
  body:has(.c-sticky[data-show="true"]) .c-footer .c-theme {
    bottom: calc(var(--chip-inset) + var(--sticky-h));
  }
}

@media (prefers-reduced-motion: reduce) {
  .c-footer .c-theme { transition: none; }
}

/* ==========================================================================
   Reveal — our engine, the reference's values
   ========================================================================== */

html.js [data-reveal] {
  opacity: 0;
  transform: translateY(var(--reveal-y));
  transition:
    opacity var(--reveal-dur) var(--ease) var(--reveal-delay, 0s),
    transform var(--reveal-dur) var(--ease) var(--reveal-delay, 0s);
  will-change: transform, opacity;
}
html.js [data-reveal].is-inview { opacity: 1; transform: none; will-change: auto; }

@media (prefers-reduced-motion: reduce) {
  html.js [data-reveal] { opacity: 1; transform: none; transition: none; }
}

/* ==========================================================================
   Art
   ========================================================================== */

c-art { display: block; width: 100%; }
c-art img { display: block; width: 100%; height: auto; border-radius: var(--radius-md); }
c-rig { display: block; width: 100%; }
c-rig svg { width: 100%; height: auto; overflow: visible; }

/* ==========================================================================
   Sticky conversion bar
   ==========================================================================
   The last surface before the reader leaves. It slides up once they are past
   the first screen and retracts at the real footer, so it never sits on top
   of the page's own closing call to action. Desktop only — on a phone it
   would take a fifth of the viewport to say something the page already says. */

.c-sticky {
  position: fixed;
  inset: auto 0 0 0;
  z-index: var(--z-header);
  background: var(--paper);
  border-top: var(--rule-section) solid var(--rule);
  box-shadow: none;
  transform: translateY(100%);
  transition: transform var(--dur-base) var(--ease-in-out);
}
.c-sticky[data-show="true"] { transform: translateY(0); }

.c-sticky_inner {
  /* Same arithmetic as `.container`: --container is the CONTENT width and the
     gutters sit outside it. Using the bare token made the bar one whole gutter
     narrower than every section above it, and it is a fixed bar, so the
     misalignment was permanently on screen. */
  max-width: calc(var(--container) + var(--gutter) * 2);
  margin-inline: auto;
  padding: var(--space-4) var(--gutter);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-6);
}
.c-sticky_text b {
  display: block;
  font-family: var(--font-body);
  font-size: var(--text-ui);
  font-weight: var(--weight-semi);
  letter-spacing: 0;
}
.c-sticky_text span { color: var(--ink-muted); font-size: var(--text-meta); }
.c-sticky_act { display: flex; align-items: center; gap: var(--space-3); flex-shrink: 0; }

.c-sticky_close {
  width: 32px; height: 32px;
  display: grid; place-items: center;
  border-radius: var(--radius-pill);
  color: var(--ink-muted);
  transition: background var(--dur-fast) var(--ease-in-out);
}
.c-sticky_close:hover { background: var(--row-tint); color: var(--ink); }
.c-sticky_close svg { width: 16px; height: 16px; }

@media (max-width: 1023px) { .c-sticky { display: none; } }

/* --------------------------------------------------------------------------
   Pill tones — semantic aliases.
   The card grids speak in outcomes (ok / warn / bad); the palette speaks in
   colors. Keeping both names means content never has to know a hex value.

   These three are the one place on the site where colour is allowed to MEAN
   something, so they are the one place it must not be the only thing carrying
   the meaning — every one of them is a spelled-out word, and the ground is a
   flat palette colour rather than a 10% tint of an untokenised hex. Eight of
   the eleven hexes this block used to hold were in no token and in no
   document; the grounds now come from the palette and the ink from the ramp.
   -------------------------------------------------------------------------- */

.pill--ok   { --cluster-ground: var(--accent);    color: var(--on-accent); }
.pill--warn { --cluster-ground: var(--highlight); color: var(--ink); }
.pill--bad  { --cluster-ground: var(--field);     color: var(--on-field); }

/* On the field band all three take the band's one pill construction — paper
   ground, ink outline, ink text — because two of the three grounds ARE the
   band's own colours and would vanish into it. */
.c-card--dark .pill--ok,
.c-card--dark .pill--warn,
.c-card--dark .pill--bad,
.bg-dark .pill--ok,
.bg-dark .pill--warn,
.bg-dark .pill--bad { --cluster-ground: var(--paper); color: var(--ink); }

/* Three-up card grid. Two-up and the default four-up are already defined; the
   three-up is the dimension detail page's pass / fail / never-scored row. */
.c-cards--3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
@media (max-width: 1023px) { .c-cards--3 { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 719px)  { .c-cards--3 { grid-template-columns: 1fr; } }

/* ==========================================================================
   Email capture — microcopy wrapper
   ==========================================================================
   Four lines in a fixed order: a tag that pre-answers "what happens next",
   the field, a helper that justifies asking for a work address, and a
   standalone trust line. The reference keeps all four as separate strings
   rather than one blob, which is what lets the trust line be reused beside
   CTAs that have no form. */

.c-capture_wrap {
  display: flex; flex-direction: column; align-items: center;
  gap: var(--space-3);
  margin-top: var(--space-7);
}

.c-capture_tag {
  font-size: var(--text-small);
  color: var(--ink-muted);
  margin-bottom: var(--space-1);
}
.c-capture_help {
  font-size: var(--text-small);
  color: var(--ink-muted);
  max-width: 46ch;
  text-align: center;
}
.c-capture_trust {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: var(--text-small);
  font-weight: var(--weight-medium);
  color: var(--ink-muted);
}
.c-capture_trust svg { width: 15px; height: 15px; color: var(--accent); }

.bg-dark .c-capture_tag,
.bg-dark .c-capture_help,
.bg-dark .c-capture_trust { color: var(--on-field); }
.bg-dark .c-capture_trust svg { color: var(--on-field); }

/* An invalid submit marks the field rather than throwing a dialog. */
.c-capture[data-error="true"] input { border-color: var(--rust); border-width: var(--frame-heavy); box-shadow: none; }


/* ==========================================================================
   Button states — measured
   ==========================================================================
   THIS BLOCK USED TO REPAINT THE BUTTONS ON HOVER, AND THAT IS WHY IT IS
   EMPTY NOW. It sat 1,200 lines after the button definitions and restated
   three colours — a Miro blue for the primary's hover and active, and one
   warm grey for both quiet buttons — so any repaint of the button up there was
   silently undone the moment a cursor touched it. In this language a control
   does not change colour under the cursor at all: it TRAVELS 2px toward its
   own offset shadow, which is declared once with the button and needs no
   second block. The nav's hover plate is gone for the same reason — the label
   reveals an accent rule instead of growing a background.

   ==== RETIRED VALUES — a debt marker, not a palette =======================
   These three hexes are quoted in DESIGN_SYSTEM.md and live nowhere else in
   the five files tools/check-design-system.mjs reads — verified by grep across
   tokens.css, site.css, sections.mjs, media.mjs and art.mjs. They are kept as
   text ONLY so that gate keeps passing while the guide still describes the
   miro palette; nothing consumes them and nothing may.

     #314cd9  was .btn--primary:hover   -> travel, not a repaint
     #2a41b6  was .btn--primary:active  -> travel, not a repaint
     #f4f4f1  was the shared quiet-button and nav hover wash

   Delete these three lines in the same commit that rewrites the guide's
   colour tables, and re-run the gate — it should go green on its own rather
   than by re-adding a string. This is the same debt marker the token lane
   left at site/assets/css/tokens.css, in the same session, for the same
   reason. */

/* ==========================================================================
   The wash rotation
   ==========================================================================
   THE ROTATION IS THE PALETTE NOW, NOT A PASTEL FAMILY. It used to be nine
   sticky-note tints, each with the ink from its own family — a colour strategy
   that works when the page has no other colour, and reads as noise beside six
   saturated cluster grounds. The nine class NAMES survive because 2,448 built
   elements carry them and the renderers pick them by index; what each one
   resolves to has moved.

   Six of the nine ARE the clusters, and the mapping is read off this repo's
   own table at site/lib/stories.mjs:93-100 so that a `.wash-cyan` card and a
   `.pill--cyan` pill on the same page cannot disagree about what cyan means.
   `--wi` is the ink that ground takes: two of the six reverse to paper,
   because black on green is 3.02:1 and black on ink is 1.00:1.

   The remaining three — orange, blue, coral — are NOT clusters. They are the
   tail of an eight-name rotation that the renderers reach on a long row, and
   they take the tint step rather than three more colours, because a colour
   that means nothing should not look like one that does. */

/* --wi reads the SAME six grounds as the pill map, so it inherits the same
   verdict: the theme re-points every ground onto the type token named here,
   and none of the six needs a reversal of its own. The ratios are the pill
   map's ratios — see the note there before changing an ink on this side, or
   the two halves of one decision will stop agreeing. */
.wash-mint   { --w: var(--pill-assessment);   --wi: var(--ink); }
.wash-cyan   { --w: var(--pill-screening);    --wi: var(--ink); }
.wash-lilac  { --w: var(--pill-interviewing); --wi: var(--ink); }
.wash-yellow { --w: var(--pill-policy);       --wi: var(--on-accent); }
.wash-pink   { --w: var(--pill-teams);        --wi: var(--ink); }
.wash-teal   { --w: var(--pill-pipeline);     --wi: var(--on-accent); }
.wash-orange { --w: var(--row-tint); --wi: var(--ink); }
.wash-blue   { --w: var(--row-tint); --wi: var(--ink); }
.wash-coral  { --w: var(--row-tint); --wi: var(--ink); }

/* The stat card is the framed device, not a tinted one. A measured claim on
   its own paper face behind an ink frame reads as a receipt; the same number
   on a pastel plate reads as a badge, and this corpus does not do badges. The
   number is the colour it needs. */
.c-stat {
  padding: var(--space-6);
  border-radius: 0;
  background: var(--lift);
  border: var(--frame-border) solid var(--rule);
  box-shadow: var(--shadow-control) var(--shadow);
}
.c-stat .t-stat { color: var(--ink); }
/* An opacity is not a colour. `--ink` at 0.72 over paper is an unnamed grey
   that no token holds and no contrast figure covers; the ramp has a step for
   exactly this and it is 8.15:1. */
.c-stat .c-stat_label { color: var(--ink-muted); opacity: 1; }

/* A washed card marks its family with a band across its top edge. It keeps
   doing that; the band is a cluster colour now rather than a tint, and it
   takes the rule ladder's own width instead of an untokenised 6px. */
.c-card--washed { position: relative; overflow: hidden; }
.c-card--washed::before {
  content: "";
  position: absolute; inset: 0 0 auto 0; height: var(--rule-band);
  background: var(--w);
}

/* The step number disc. */
.c-step_num { background: var(--w, var(--row-tint)); color: var(--wi, var(--ink)); }

/* The wide plate in an alternating feature row — framed like every other
   set-apart surface, rather than floating on a blur. */
.c-rowmedia {
  border-radius: 0;
  overflow: hidden;
  border: var(--frame-border) solid var(--rule);
  box-shadow: var(--shadow-control) var(--shadow);
}
.c-rowmedia svg { display: block; width: 100%; }

/* THE REAL FIGURE IN A WIDE PLATE. `.c-rowmedia` is `overflow: hidden` and
   carries its own frame and offset shadow, which is right for an SVG schematic
   drawn to the plate's exact viewBox and wrong for a `.figure`: the figure
   brings a second frame of its own, and chart.mjs sizes its SVG to a 480px
   minimum so the row labels and the value column stay legible. Measured before
   this rule existed: the value labels ("1.4% to 8.1%") were cut mid-character
   on the right at 1400px, and at 390px the 480px chart sat clipped inside a
   350px plate.

   Three corrections, each for one of those. The inner frame goes, because the
   plate already is the frame. The chart scrolls in x rather than being cut, so
   a narrow reader can reach the whole series instead of seeing a cropped one —
   and the scroll is on the CHART only, so the provenance foot below it still
   wraps normally. And the plate stops hiding what leaves it. */
.c-rowmedia--figure { overflow: visible; }
.c-rowmedia--figure .figure {
  width: 100%; max-width: none;
  /* Padding goes with the frame. Measured: the figure's own padding left the
     chart box at 502px inside a 584px plate while the chart needs 544px, so the
     value column ("1.4% to 8.1%") scrolled out of view on a DESKTOP screen —
     the frame was paying for itself twice and the data lost. */
  border: 0; box-shadow: none; border-radius: 0; margin: 0; background: none;
  padding: 0;
}
.c-rowmedia--figure .figure__chart {
  /* The 32px side padding this carries on a corpus page is the frame's inner
     margin, and the frame is gone here — so it was 64px of the plate spent on
     nothing while the chart scrolled for want of 42. Measured: chart box 502px
     against a 544px chart inside a 584px plate. */
  padding-left: 0; padding-right: 0;
  /* Still scrollable, for the narrow case only: the chart holds a 480px floor
     so six month labels and a value column stay legible, and below about 480px
     of plate there is no honest way to show all of it at once. Scrolling shows
     a cropped chart the reader can complete; scaling to fit would put the row
     labels under 8px. */
  overflow-x: auto;
}
.c-rowmedia--figure .figure__chart svg { min-width: 480px; }
.c-rowmedia--figure .figure__foot { padding-left: 0; padding-right: 0; }
/* The product walkthrough in a wide plate. It inherits the frame and the offset
   shadow from .c-rowmedia above, so nothing is restated here — the video only
   has to fill the plate and keep its own 16:9, which `aspect-ratio` does
   without the padding-top trick and without a wrapper. `display:block` is the
   one that matters: an inline video leaves a baseline gap under itself inside
   an overflow:hidden frame, which reads as a 4px seam along the bottom edge. */
.c-rowmedia--video { line-height: 0; }
.c-rowvideo { display: block; width: 100%; height: auto; aspect-ratio: 16 / 9; background: var(--ink); }
.c-rowmedia + .c-card, .c-card--under { margin-top: var(--space-5); }

/* A band painted in the rotation. Whatever cluster ground it lands on, its
   type is that ground's own ink — the `opacity: 0.78` the attribution carried
   was a fourth neutral nobody named. */
.bg-wash { background: var(--w); color: var(--wi, var(--ink)); }
.bg-wash .c-quote { color: var(--wi, var(--ink)); }
.bg-wash .c-quote_attr { color: var(--wi, var(--ink)); opacity: 1; }
.bg-wash .c-card { background: var(--surface); }

/* THE DISCLOSURE CIRCLE IS ONE COLOUR, NOT NINE. 2,343 accordion rows carry a
   wash class, so the disc used to cycle through the whole family down a single
   FAQ list — eight differently-coloured plus signs answering eight questions
   that are all the same kind of thing. The reference fills every one of them
   with the same blue: ink on it is 7.87:1. */
.c-acc_item .c-acc_icon { background: var(--hl-fact); color: var(--on-accent-2); }

.c-plan::before {
  content: "";
  display: block;
  height: var(--rule-section);
  width: 56px;
  border-radius: 0;
  background: var(--w, var(--rule));
  margin-bottom: var(--space-5);
}
.c-list_meta .pill { margin-right: 0; }

/* THE ARTICLE'S FAQ IS ONE COLUMN IN THE READING MEASURE, not a marketing
   split. The renderer sends it through the shared `split` section, so the
   heading is stranded in an empty five-column well while the questions sit in
   a six-column well beside it — on a reading page whose every other block runs
   the 680px measure. `#questions` is emitted on 450 pages and every one of
   them is an answer (measured across the whole build), so this is scoped to
   the article and reaches no marketing split. */
#questions .c-split {
  display: block;
  max-width: var(--measure);
}
#questions .c-split_text > * + * { margin-top: var(--space-2); }
#questions .c-split_media { margin-top: var(--space-5); }
/* One heading, not two. The renderer emits an eyebrow AND an invented
   "Questions this raises" h2 above the same rows; the reference has a single
   block title. The h2's TEXT is the renderer's to fix — what is reachable here
   is not letting the pair read as two ranks of heading, so the eyebrow drops
   to the label it is and the h2 takes the block-title rung. */
#questions .c-split_text h2 {
  font-family: var(--font-body);
  font-weight: var(--weight-semi);
  font-size: var(--text-h2);
  line-height: var(--leading-heading);
  text-transform: none;
  letter-spacing: 0;
}

/* The billing period rides at a fraction of the number's size — the price is
   the thing being read, and "/mo" set at 40px competes with it. */
.c-plan_price span {
  font-size: 0.42em;
  font-weight: var(--weight-medium);
  letter-spacing: 0;
  color: var(--ink-muted);
}


/* --------------------------------------------------------------------------
   The mark on the field band
   --------------------------------------------------------------------------
   THIS RULE WAS WRITTEN FOR A NEAR-BLACK FOOTER AND THE FOOTER HAS BEEN
   ORANGE SINCE THE PORT. It asked for a white disc and a --ground-dark face,
   which was the right inversion when the band was #1c1c1e and is two defects
   now that the band is --field. Measured on what actually shipped: the face
   took --ground-dark and the disc took the band's --logo-ring, which are
   BOTH near-black, so the central character was black on black — 1.00:1, an
   invisible glyph in a visible disc — and the wordmark took --ink-on-dark,
   i.e. white on #ff5200 at 3.25:1, the lowest-contrast type anywhere on the
   page. Dark mode makes the second one worse (cream on the field, 2.74:1) and
   leaves the first exactly as broken.

   The band already knows how to reverse and the mark now uses that answer:
   the disc and the wordmark take --on-field with everything else on the band
   (6.46:1 light, 5.62:1 dark) and the character is KNOCKED OUT in the band's
   own ground, so it reads as a hole in the disc rather than as a second
   colour — #ff5200 on the near-black disc is 6.46:1 light and 5.62:1 dark.
   Same mark read the other way round, which is what the old comment intended,
   against a band it no longer described. */

.bg-dark .c-logo, .c-footer .c-logo { --logo-face: var(--field); color: var(--on-field); }

/* ==========================================================================
   Essays
   ==========================================================================
   Posts are pages of this site, so they keep the chrome and the palette. What
   changes is the measure and the rhythm: a reading column, generous leading,
   and sticky canvas scenes that hold still while their step cards move. */

.e-progress {
  position: fixed; inset: 0 0 auto 0; height: 2px;
  z-index: calc(var(--z-header) + 1);
  background: transparent; pointer-events: none;
}
.e-progress > i {
  display: block; height: 100%; width: 100%;
  background: var(--blue);
  transform: scaleX(0); transform-origin: 0 50%;
}

.e-head { padding-block: var(--space-9) var(--space-8); background: var(--surface); }
.e-head h1 {
  font-size: var(--text-display);
  line-height: var(--leading-display);
  letter-spacing: var(--tracking-display);
  margin-bottom: var(--space-5);
}
.e-meta { display: flex; flex-wrap: wrap; gap: var(--space-4); margin-bottom: var(--space-5); }
.e-meta a { color: var(--ink); border-bottom: var(--rule-hair) solid var(--accent); }
.e-byline { margin-top: var(--space-5); color: var(--ink-muted); font-size: var(--text-small); }

.e-body { background: var(--surface); padding-bottom: var(--space-9); }

/* The essay header and the receipts panel ride the same reading column as the
   prose. They are wrapped in `.container > .measure`, and `.measure` has no
   `margin-inline: auto` — it is deliberately left-aligned, which is what a
   section intro wants and exactly wrong here: it parked the title, byline and
   the whole receipts panel 300px left of the article's own body text at 1280. */
.e-post > .container,
.e-head > .container { max-width: none; padding-inline: 0; }
.e-post > .container > .measure,
.e-head > .container > .measure {
  max-width: var(--measure);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

/* The reading column. 680px is the measure the rest of the site uses; an
   essay has no reason to be wider than the page that links to it. */
.e-prose, .e-h, .e-h3, .e-pull, .e-caveat, .e-note, .e-figure {
  max-width: var(--measure);
  margin-inline: auto;
  padding-inline: var(--gutter);
}
/* The essay's reading spec is THE reading spec — the same rung and the same
   leading as `.prose` and as the body default. It carried its own 1.125rem and
   1.65 as literals, which happen to equal `--text-body` and to miss
   `--leading-body` by 0.02: a second copy of the reading floor that already
   disagreed with the first by a hair nobody would ever spot. */
.e-prose p {
  font-size: var(--text-body);
  line-height: var(--leading-body);
  color: var(--ink);
  margin-bottom: var(--space-5);
}
.e-prose ul, .e-prose ol { margin: 0 0 1.15em 1.2em; display: grid; gap: 0.5em; }
.e-prose li { font-size: var(--text-body); line-height: 1.56; }

/* The essay's in-body headings are the sans, like every other heading inside
   a reading column — the serif is for the page's own title. */
.e-h {
  font-family: var(--font-body); font-weight: var(--weight-semi);
  text-transform: none;
  font-size: var(--text-h2);
  line-height: var(--leading-heading);
  letter-spacing: 0;
  margin: var(--space-9) auto var(--space-5);
}
.e-h3 {
  font-family: var(--font-body); font-weight: var(--weight-semi);
  text-transform: none; letter-spacing: 0;
  font-size: var(--text-h3); margin: var(--space-7) auto var(--space-4);
}

/* The pull quote is a SENTENCE, so it is the sans — same call as `.c-quote`,
   and for the same reason: the display face is set in caps wherever it is
   used, and a paragraph in caps is shouting. */
.e-pull {
  position: relative;
  font-family: var(--font-body);
  font-weight: var(--weight-semi);
  font-size: clamp(1.5rem, 1.1rem + 1.8vw, 2rem);
  line-height: 1.29;
  letter-spacing: 0;
  margin-block: var(--space-8);
  /* The rule lives inside the gutter rather than on the element's edge. A
     border-left paints OUTSIDE the padding box, and `padding-left` here was
     overriding the shared `padding-inline: var(--gutter)` above — so below
     719px the blue rule sat on x=0, flush to the screen, while the body text
     beside it kept its 20px. */
  padding-left: calc(var(--gutter) + var(--space-5));
  border-left: 0;
}
.e-pull::before {
  content: "";
  position: absolute;
  left: var(--gutter); top: 0; bottom: 0;
  /* The blockquote's left bar — 5px, and it has its own token because it is a
     WIDTH and not a radius. It was an untokenised 3px. */
  width: var(--bar-accent); background: var(--highlight);
}

/* An honesty beat. Visually quieter than the prose around it, because it is
   an aside — but never hidden, because hiding it defeats the point.
   `.limits` is the same beat off the essay page — a benchmark's limits and
   refuses, an asset's disclaimer — and it rides this selector for the width
   arithmetic below, which was measured twice and is not worth having twice. */
.e-caveat, .limits {
  /* `--surface-alt` resolves to paper, so this panel had no ground at all and
     only its 1px shadow ring said it was there. The tint step is the banding
     ground this system has, and a note block is exactly what it is for. */
  background: var(--row-tint);
  border-radius: var(--radius-card);
  /* A card has to stay inside the gutter, and `max-width` alone does not do it:
     at 375px the 680px cap never binds, so this ran edge to edge with its 20px
     corners sheared off by the viewport — while its own 32px padding indented
     the text 12px FURTHER in than the prose. Two errors in opposite directions
     on one element. */
  max-width: calc(var(--measure) - var(--gutter) * 2);
  width: calc(100% - var(--gutter) * 2);
  margin-inline: auto;
  padding: var(--space-4) var(--space-5);
  margin-block: var(--space-7);
  box-shadow: none;
}
.e-caveat .t-eyebrow, .limits .t-eyebrow { margin-bottom: var(--space-3); }
/* Ink, not the muted step: a caveat is quieter than the prose around it by
   being SMALLER and set apart, not by being harder to read. */
.e-caveat p, .limits p { color: var(--ink); font-size: var(--text-meta); line-height: 1.5; }

.e-note { color: var(--ink-muted); font-size: var(--text-meta); margin-block: var(--space-7); font-style: italic; }
.e-figure { margin-block: var(--space-7); }
.e-figure figcaption { margin-top: var(--space-3); font-size: var(--text-small); color: var(--ink-muted); }

/* --- scenes -------------------------------------------------------------- */

.e-scene { position: relative; height: var(--h, 300vh); margin-block: var(--space-8); }
.e-scene_sticky {
  position: sticky; top: 72px;
  height: calc(100vh - 72px);
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  padding: var(--space-5) var(--gutter) var(--space-6);
}
/* The canvas takes the room it needs and no more: at full height the chart
   floated in the middle of a screen of nothing, and its own caption fell off
   the bottom edge. */
.e-scene_sticky canvas { width: 100%; max-width: 880px; flex: 0 1 auto; height: min(62vh, 560px); }
.e-scene_caption {
  margin-top: var(--space-4); font-size: var(--text-small); color: var(--ink-muted);
  text-align: center; max-width: 60ch;
}

/* Step cards ride over the sticky canvas, one active at a time. They sit to
   one side rather than centered so they never cover what they describe. */
.e-scene_steps {
  position: absolute; inset: 0;
  display: grid;
  padding-inline: var(--gutter);
  pointer-events: none;
}
.e-scene_steps .step {
  grid-area: 1 / 1;
  align-self: start;
  justify-self: end;
  width: min(380px, 100%);
  margin-top: calc(100vh - 72px - 260px);
  position: sticky; top: calc(100vh - 300px);
  background: var(--lift);
  border: var(--frame-border) solid var(--rule);
  border-radius: 0;
  box-shadow: var(--shadow-control) var(--shadow);
  padding: var(--space-5);
  opacity: 0;
  transform: translateY(12px);
  transition: opacity var(--dur-base) var(--ease), transform var(--dur-base) var(--ease);
}
.e-scene_steps .step.is-active { opacity: 1; transform: none; }
.e-scene_steps .step h3 { font-size: var(--text-ui); margin-bottom: var(--space-2); }
.e-scene_steps .step p { font-size: var(--text-meta); line-height: 1.43; color: var(--ink-muted); }

@media (max-width: 767px) {
  .e-scene_sticky { height: calc(100vh - 72px); padding-bottom: 220px; }
  .e-scene_steps .step { justify-self: stretch; width: 100%; top: auto; bottom: var(--space-5); align-self: end; margin-top: 0; }
}

/* --- receipts ------------------------------------------------------------ */

/* The receipts panel is the FAQ row's big sibling: the same derived baby blue
   ground and the same card radius, because both are a native <details> a
   reader opens. It was `--surface-alt`, which resolves to paper. */
.e-receipts {
  margin-block: var(--space-9) var(--space-8);
  border-radius: var(--radius-card);
  background: var(--faq-row);
  box-shadow: none;
  overflow: hidden;
}
.e-receipts > summary {
  display: flex; align-items: center; justify-content: space-between; gap: var(--space-4);
  padding: var(--space-4) var(--space-5);
  cursor: pointer;
  font-family: var(--font-body);
  font-size: var(--text-ui);
  font-weight: var(--weight-bold);
  list-style: none;
}
.e-receipts > summary::-webkit-details-marker { display: none; }
.e-receipts_count {
  display: inline-grid; place-items: center; min-width: 30px; height: 30px;
  padding-inline: var(--space-2); border-radius: var(--radius-pill);
  /* The same blue disc as the FAQ's disclosure circle, and INK on it rather
     than white: white on this blue is 2.67:1 and fails AA outright. */
  background: var(--hl-fact); color: var(--on-accent-2);
  font-family: var(--font-mono); font-size: var(--text-meta); font-weight: var(--weight-semi);
  font-variant-numeric: tabular-nums;
}
.e-receipts .c-table-wrap { padding: 0 var(--space-6) var(--space-6); }
.e-receipts code { font-family: var(--font-mono); font-size: 0.875em; }

@media (prefers-reduced-motion: reduce) {
  .e-scene { height: auto !important; }
  .e-scene_sticky { position: static; height: auto; min-height: 60vh; }
  .e-scene_steps { position: static; display: grid; gap: var(--space-4); margin-top: var(--space-5); }
  .e-scene_steps .step { position: static; opacity: 1; transform: none; margin-top: 0; width: 100%; justify-self: stretch; }
}

/* ==========================================================================
   Press row — four publications, set as type

   We have no license to the logos and will not redraw a trademarked wordmark
   from memory, so the differentiation is typographic: two serif mastheads and
   two sans, each with its own weight, case and tracking. Four labels in one
   family would read as a list; these read as a press bar.

   No boxes. The whole point of a credibility row is that it is quiet, and a
   card around each quote would be a border doing work the type already does.
   ========================================================================== */

.c-press {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: var(--space-7) var(--space-6);
}

.c-press_item {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
  margin: 0;
}

/* The masthead. `cite` is italic by default, which is exactly wrong here. */
/* THE TWO GEORGIA STACKS ARE GONE, AND THEY WERE THE LAST SERIF ON THE SITE
   THAT WAS NOT THE SITE'S SERIF. Two of these four asked for
   `ui-serif, Georgia, "Times New Roman", serif` — which is now, exactly,
   `--font-display`'s own fallback stack. Before this port the site shipped no
   serif at all, so Georgia was the only way to get one; it ships Romie now,
   and leaving these two on the fallback would print the fallback beside the
   real face on the same page.

   The differentiation survives, because it never depended on Georgia — it
   depends on there being four treatments. Three faces give four: the display
   serif at two tracking values, the reading sans, and the mono. Both serif
   marks are caps at 700, per the standing ruling that the display face is only
   ever set that way, and 0.1em against 0 is a wider gap at 16px than the old
   0.1em-against--0.015em was. */
.c-press_mark {
  font-style: normal;
  color: var(--ink);
  font-size: var(--text-ui);
  line-height: 1.05;
}
.c-press_mark--vanity-fair {
  font-family: var(--font-display);
  font-weight: var(--weight-bold);
  text-transform: uppercase;
  letter-spacing: 0.1em;
}
.c-press_mark--the-times {
  font-family: var(--font-display);
  font-weight: var(--weight-bold);
  text-transform: uppercase;
  letter-spacing: 0;
}
.c-press_mark--fast-company {
  font-family: var(--font-body);
  font-weight: var(--weight-bold);
  text-transform: uppercase;
  letter-spacing: var(--tracking-caps);
}
.c-press_mark--semafor {
  font-family: var(--font-mono);
  font-weight: var(--weight-medium);
  letter-spacing: 0.03em;
}

.c-press_quote {
  margin: 0;
  color: var(--ink);
  font-size: var(--text-body);
  line-height: 1.5;
  text-wrap: pretty;
}
.c-press_quote p { margin: 0; }

.c-press_by {
  margin-top: auto;
  font-size: var(--text-small);
  line-height: 1.45;
  color: var(--ink-muted);
}
.c-press_speaker { display: block; }

/* The byline is the link, so its name is short and specific; the stretched
   pseudo-element makes the whole slot the hit area without nesting the quote
   inside an anchor. */
.c-press_link { color: var(--ink-muted); }
.c-press_link::after { content: ""; position: absolute; inset: 0; }
.c-press_item:hover .c-press_link { color: var(--ink); border-bottom: var(--rule-hair) solid var(--accent); }

@media (max-width: 1023px) {
  .c-press { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* Below 640px four stacked pull quotes are a long scroll for a bar that is
   meant to be skimmed, so it becomes a swipeable track. The next slot peeks,
   which is the only affordance it needs — there are four items and dead
   arrow controls would be worse than none. */
@media (max-width: 639px) {
  .c-press {
    grid-template-columns: none;
    grid-auto-flow: column;
    grid-auto-columns: 78%;
    gap: var(--space-6);
    overflow-x: auto;
    scroll-snap-type: x proximity;
    padding-block: var(--space-2) var(--space-3);
    scrollbar-width: none;
  }
  .c-press::-webkit-scrollbar { display: none; }
  .c-press > * { scroll-snap-align: start; }
}

/* ==========================================================================
   Takeaways — the chart figure, the shelf, the chooser, the calculator
   ==========================================================================
   Four devices for the pages a reader is meant to carry away rather than
   visit: a benchmark figure, a catalogue of forwardable documents, a
   two-question router into the compliance pages, and a client-side
   calculator.

   None of them adds a token and none of them adds a type rung. Everything
   below is the existing scale, the existing hairline and the existing
   surfaces, recomposed. Three of the four ride a selector declared elsewhere
   rather than restating a measured value: the axis label on `.t-eyebrow`
   (Typography), the two fields on `.c-capture input` (Hero), and `.limits`
   on `.e-caveat` (Essays).

   Nothing here animates. There is no transition, no transform and no reveal
   in this section, so the reduced-motion blocks above have nothing to add to
   it — the frame is furniture rather than an affordance, and a calculator
   whose arithmetic is local and instant has no state to move through. */

/* --- the chart figure ---------------------------------------------------- */

/* The heavy frame, and the boundary is the whole device. A crop of this box
   alone has to resolve back to the cells it drew, because the screenshot in
   somebody's deck two quarters from now is the artifact, not the page. That
   is the one job §9's "no border doing work type could carry" does not reach:
   type cannot draw a crop edge.

   The old ring reasoned about which of two greys survives JPEG re-encoding
   worst. That question is gone with the greys: the boundary is a 3px BLACK
   border now, the heaviest width in the rule ladder and the one edge no
   re-encode can eat. This is the heavy frame — a mount rather than a control —
   so it carries no offset shadow and does not press. */
.figure {
  max-width: calc(var(--measure) - var(--gutter) * 2);
  width: calc(100% - var(--gutter) * 2);
  margin-inline: auto;
  margin-block: var(--space-7);
  background: var(--lift);
  border: var(--frame-heavy) solid var(--rule);
  border-radius: var(--radius-heavy);
  box-shadow: none;
  overflow: hidden;
}

/* viewBox only, no width and no height attributes — the same contract the
   media plates ship under. The 480px floor is arithmetic rather than taste.
   The frame's chart well renders about 536px wide at 1440 and about 271px at
   375, and an SVG that scales down to 271 renders a 14px value label at 6px.
   A floor under the desktop width and over the phone one therefore scrolls
   the well on a phone and nowhere else, which is the mechanism `.c-table`
   already uses. The label shrinks on a phone and stays legible on the second
   surface: the table below carries the same cells at reading size. */
.figure__chart {
  padding: var(--space-6) var(--space-6) var(--space-5);
  overflow-x: auto;
}
.figure__chart svg { display: block; width: 100%; min-width: 480px; height: auto; }

/* The provenance foot: n, the period and the measurement date on line one,
   the canonical URL and the edition id on line two. Both lines take
   `.t-eyebrow`, so the URL has to opt back out of the uppercase — a path is
   case-sensitive on a static host, and an uppercased one is a 404 printed in
   the frame built to make a screenshot resolve. It ships inside <code>, which
   is also what stops a long URL widening the frame. */
.figure__foot {
  display: grid;
  gap: var(--space-1);
  padding: var(--space-4) var(--space-6) var(--space-5);
  border-top: var(--rule-hair) solid var(--rule);
  font-variant-numeric: tabular-nums;
}
.figure__foot code,
.figure__foot a {
  font-family: inherit;
  font-size: inherit;
  color: inherit;
  text-transform: none;
  letter-spacing: 0;
  overflow-wrap: anywhere;
}

/* The same cells as a real table, under the chart. Two surfaces, one dataset:
   the picture is what gets screenshotted, the table is what an answer engine
   reads and what a screen reader gets.

   The frame is already the card, so this drops `.c-table-wrap`'s ground and
   its 680px floor. Inside a 536px well that floor would scroll a five-column
   table on a 1440 desktop, which is the opposite of what a floor is for. */
.figure__table { overflow-x: auto; border-top: var(--rule-section) solid var(--rule); }
.figure__table table { width: 100%; min-width: 420px; border-collapse: collapse; }
.figure__table th, .figure__table td {
  padding: var(--space-3) var(--space-5);
  text-align: left;
  vertical-align: top;
  border-bottom: var(--rule-hair) solid var(--rule);
  font-size: var(--text-meta);
  /* Every cell, not only the numeric ones: a column that mixes "withheld"
     with 8.12 still has to keep its digits in a line. */
  font-variant-numeric: tabular-nums;
}
.figure__table thead th {
  font-family: var(--font-body);
  font-size: var(--text-label);
  font-weight: var(--weight-semi);
  text-transform: uppercase;
  letter-spacing: var(--tracking-caps);
  color: var(--ink-muted);
  border-bottom-width: var(--rule-section);
}
.figure__table td { color: var(--ink); }
.figure__table th[scope="row"] {
  font-size: inherit;
  font-weight: var(--weight-medium);
  color: var(--ink);
}
.figure__table tr:last-child td, .figure__table tr:last-child th { border-bottom: 0; }

/* The copyable citation, and it sits OUTSIDE the frame on purpose: it ends in
   an "Accessed __" slot the reader fills in, and the frame's whole promise is
   that a crop of it resolves with nothing filled in. A static build cannot
   know when a stranger loaded the page, so the slot is the honest form and it
   must not be inside the thing being screenshotted. Emitted as the frame's
   next sibling. */
.figure__cite {
  max-width: calc(var(--measure) - var(--gutter) * 2);
  width: calc(100% - var(--gutter) * 2);
  margin-inline: auto;
  margin-block: var(--space-3) var(--space-7);
  font-size: var(--text-small);
  line-height: 1.45;
  color: var(--ink-muted);
  overflow-wrap: anywhere;
}
/* The frame's bottom margin is the gap to whatever comes next; when the cite
   line is what comes next, that gap belongs under the cite instead. Same
   idiom as the adjacent-ground collapse in Layout. */
.figure + .figure__cite { margin-top: calc(var(--space-3) - var(--space-7)); }

/* --- inside the chart ---------------------------------------------------- */

/* Colour differentiates here and never means. Every series is labelled in
   place, a two-instrument figure is one bar with a rule at each end, and the
   tone comes off the data contract's own ink / accent through `data-tone` —
   the attribute idiom `.c-capture[data-error]` already uses. Take the colour
   away and the figure loses nothing, which is the test. */
.chart__bar { fill: var(--ink); }
.chart__bar[data-tone="accent"] { fill: var(--accent); }

/* The bracket ends of a two-instrument bar. A bracket reads as a range, and a
   range is what a floor and a ceiling are; a midpoint would be a claim the
   data does not carry. */
.chart__rule {
  fill: none;
  stroke: var(--ink);
  stroke-width: 2;
  stroke-linecap: butt;
}
.chart__rule[data-tone="accent"] { stroke: var(--accent); }

/* In-chart value labels sit at the small rung with tabular figures, so a
   column of them lines up and a bar's number does not shift as it changes. */
.chart__val {
  /* A value label is a FIGURE, so it takes the data register — the mono face
     is where tabular figures actually line up, because it is drawn for them. */
  font-family: var(--font-mono);
  font-size: var(--text-meta);
  font-weight: var(--weight-semi);
  font-variant-numeric: tabular-nums;
  fill: var(--ink);
}

/* The eyebrow rung, in the one property SVG reads. The rest of it — the size,
   the 0.07em tracking and the uppercase — is on the shared selector in
   Typography, which is why that selector names `.chart__axis` beside
   `.t-eyebrow` rather than restating the device here. */
.chart__axis { fill: var(--ink-muted); }

/* --- the shelf ----------------------------------------------------------- */

/* The toolshed catalogue: the editorial list's hairline rhythm under one
   eyebrow per kind, not a new list. Rows take `.c-list_link`, `.c-list_meta`,
   `.c-list_title` and `.c-list_body` unchanged.

   Grouping is carried by the eyebrow and the space around it, because the
   eyebrow in this system has no colour band behind it and is not getting one:
   a band per kind would be seven leads competing with the page's own, and a
   seventh pill colour is banned. The kind is always spelled in words. */
.shelf { border-top: var(--rule-hair) solid var(--rule); }
.shelf__row { border-bottom: var(--rule-hair) solid var(--rule); }
.shelf__kind { margin-top: var(--space-7); margin-bottom: var(--space-3); }
.shelf__kind:first-child { margin-top: 0; }

/* --- the compliance chooser ---------------------------------------------- */

/* Two questions — where you hire from, and where the work will be done —
   because either one alone gets the answer wrong, in opposite directions.

   It returns pages and never a verdict, so its result has no device of its
   own: it is a `.shelf` of jurisdiction links. That is the line that keeps the
   card on the right side of the law, and it is a rendering rule as much as a
   legal one, because the moment a result region can hold a sentence somebody
   will write a conclusion into it. With scripting off the card is a plain
   link to the index; nothing here hides a fallback. */
.lawcard {
  max-width: calc(var(--measure) - var(--gutter) * 2);
  width: calc(100% - var(--gutter) * 2);
  margin-inline: auto;
  margin-block: var(--space-7);
  padding: var(--space-6);
  background: var(--lift);
  border: var(--frame-border) solid var(--rule);
  border-radius: 0;
  box-shadow: var(--shadow-box) var(--shadow);
}
.lawcard > .t-eyebrow { margin-bottom: var(--space-3); }
.lawcard__q {
  display: grid;
  gap: var(--space-2);
  margin-top: var(--space-5);
  max-width: 32rem;
}
.lawcard__q label { font-size: var(--text-small); color: var(--ink-muted); }

/* --- the calculator ------------------------------------------------------ */

/* Client-side, and structurally incapable of taking a person: the smallest
   unit of input is a stage or a group, so there is no name field, no
   per-candidate row and no score input. Nothing is stored and nothing is
   sent, so there is no saved state to style — and no spinner and no progress
   bar either, because local arithmetic does not wait. Reset is a reload.

   A quiet panel rather than a washed band. The reason survives the repaint
   and gets simpler: colour arrives as a set of peers cycling a family, and a
   calculator arrives alone, so one tinted thing on an otherwise neutral page
   would read as an error rather than as a system.

   Its ground moves from `--surface-alt` to the tint step, and that is not a
   preference — `--surface-alt` resolves to paper now, so the panel would have
   had no ground at all and its 1px shadow ring would have been the only thing
   saying a panel was there. */
.calc {
  max-width: calc(var(--measure) - var(--gutter) * 2);
  width: calc(100% - var(--gutter) * 2);
  margin-inline: auto;
  margin-block: var(--space-7);
  padding: var(--space-6);
  background: var(--row-tint);
  border: var(--frame-border) solid var(--rule);
  border-radius: var(--radius-card);
  box-shadow: none;
}
.calc > .t-eyebrow { margin-bottom: var(--space-3); }
.calc label {
  display: grid;
  gap: var(--space-2);
  margin-top: var(--space-5);
  max-width: 22rem;
  font-size: var(--text-small);
  color: var(--ink-muted);
}

/* The output. It prints a number and a sentence, and never a verdict word —
   no pass, no fail, no compliant, no safe — because a verdict is a conclusion
   about the reader's own situation and this is arithmetic. Below the stated
   floor it prints the floor in place of the ratio.

   It is a region of its own for one reason that is not visual: it carries
   `aria-live="polite"`, so a value that changes under a keyboard is
   announced. The rule above it used to reach for the heavier of two greys;
   there is one rule colour now and the ladder carries that job instead — 3px
   is what closes a section, and input and result must not read as one list.

   The number takes the MONO face, not the display serif. It is a figure, and
   a figure set in a caps serif would be shouting arithmetic; the mono is also
   where `tabular-nums` actually buys anything, since it is the face drawn for
   it. */
.calc__out {
  margin-top: var(--space-6);
  padding-top: var(--space-5);
  border-top: var(--rule-section) solid var(--rule);
  font-family: var(--font-mono);
  font-size: var(--text-h3);
  font-weight: var(--weight-semi);
  letter-spacing: 0;
  font-variant-numeric: tabular-nums;
  color: var(--ink);
}
.calc__out p {
  margin-top: var(--space-3);
  font-family: var(--font-body);
  font-size: var(--text-meta);
  font-weight: var(--weight-regular);
  letter-spacing: 0;
  line-height: 1.43;
  color: var(--ink-muted);
}

/* --- limits and refuses -------------------------------------------------- */

/* `.limits` is `.e-caveat` off the essay page, and the treatment is declared
   once, with it, in Essays. What is here is the part the essay caveat never
   needed: two lists rather than a paragraph. What the figure cannot tell you,
   and the claims its data does not support.

   Both are required and neither is ever empty. Absence is never silence, so a
   file with nothing in either list is a file that failed its gate, not a page
   that renders an empty <ul>. */
.limits ul { margin: var(--space-3) 0 0 1.2em; display: grid; gap: var(--space-2); }
.limits li { color: var(--ink); font-size: var(--text-body); line-height: 1.56; }
.limits ul + .t-eyebrow { margin-top: var(--space-5); }

/* --------------------------------------------------------------------------
   The embed snippet, and its copy control.

   `.snippet__code` is the control. The button is progressive enhancement and
   ships `hidden`, so a page whose script never ran shows selectable text and
   no dead button rather than a button that does nothing. `pre` wraps rather
   than scrolls: the snippet is nine short lines and a horizontal scrollbar
   inside a measure column is worse than a wrap.
   -------------------------------------------------------------------------- */

.snippet {
  margin-top: var(--space-6);
}

.snippet__head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-4);
}

.snippet__code {
  margin-top: var(--space-3);
  padding: var(--space-4);
  /* `--surface-alt` resolves to paper now, so a code block painted with it had
     no ground; the tint step is the banding ground this system actually has.
     The 10px radius was a rung in no token — it sat between two of the six
     radii the old scale carried and matched neither; the four-radius system
     has a card radius and this is a card. */
  background: var(--row-tint);
  border: var(--frame-border) solid var(--rule);
  border-radius: var(--radius-card);
  /* The site ships a real mono now. This was the one place asking for the
     platform's default, which meant a snippet rendered in SF Mono here and in
     Consolas on Windows while every other figure on the page was Plex. */
  font-family: var(--font-mono);
  font-size: var(--text-meta);
  line-height: var(--leading-body);
  color: var(--ink);
  white-space: pre-wrap;
  word-break: break-word;
  overflow-wrap: anywhere;
}

/* ==========================================================================
   THE CORPUS ARTICLE
   ==========================================================================

   site/lib/stories.mjs already emits the editorial structure: article__kicker,
   article--railed, article__rail, the two reading columns, capsule, toc, faq,
   refs__*, ai__*, block, related. Verified against the reference DOM on seven
   components (kicker, TOC head/list/foot, rail-related, the AI band, the head
   column, and the whole body column) - the skeletons are identical. What was
   missing was this file: every one of those class names had ZERO rules here,
   so a correct DOM rendered as unstyled defaults and the rail fell underneath
   the article at full container width.

   MEASURED BEFORE (Chromium, 1440x1000, both servers, same selectors):
     .article--railed  reference GRID 680px 475px gap 64px   here display:block
     .article__rail    reference x=854.5 w=475               here x=120 w=1200

   ONE CORRECTION TO THE BRIEF, AND IT IS THE LOAD-BEARING ONE. The delta table
   handed to this lane reads "paragraph 20px/27.2, measure 616px" against our
   18px/30.06 at 680px, and it is an instrument artifact - the probe compared
   two DIFFERENT components. Dumping every paragraph on both pages with its
   ancestry settles it: the reference's 616px/20px/27.2 paragraph is
   `capsule < article__col--head`, and 680 - 2 * var(--space-6) = 616 is exactly
   the capsule's own horizontal padding. The reference's BODY prose measures
   680px at 18px/30.06, which is what we already render, and its width
   histogram is {425:2, 616:2, 632:5, 680:17} - seventeen paragraphs at 680.
   So --measure does not move and .prose does not go to 20px; the capsule grows
   a box, which is what was actually missing. Narrowing the measure to 616 would
   have shipped every article 64px inside the reference. */

/* The kicker is the measure pinned LEFT, flush with the columns beneath it, so
   one left edge runs from the breadcrumb to the last reference. */
.article { padding-block: var(--space-7) var(--space-9); }
.article__kicker { max-width: var(--measure); margin-inline: 0; }

.breadcrumb { color: var(--ink-muted); font-size: var(--text-meta); margin-bottom: var(--space-4); }
.breadcrumb a { border-bottom: var(--frame-border) solid var(--accent); }
.breadcrumb a:hover { color: var(--accent-deep); }
/* The separator is chrome, which is the one thing --ink-faint is licensed for:
   never reading text, never a sentence. */
.breadcrumb > span { margin-inline: var(--space-2); color: var(--ink-faint); }

/* ---------------------------------------------------------- the rail grid

   STACKED IS THE DEFAULT AND IT IS NOT A FALLBACK. Below the breakpoint the
   wrapper is a flex column and the rail is `display: contents`, so the rail's
   three blocks become siblings of the two reading columns and `order` can
   split them around the article. The renderer emits the rail FIRST in the DOM
   for exactly this reason, so nothing here may give .article__rail a box of
   its own at narrow widths without breaking that. */
.article--railed { display: flex; flex-direction: column; }
.article__rail { display: contents; }
/* Stacked, each rail block is capped at the measure so none runs wider than
   the article it sits with. In the rail they are capped at --rail instead. */
.article__rail > * { max-width: var(--measure); }
.article__col--head  { order: 1; }
.article__rail .toc  { order: 2; }
.article__col--body  { order: 3; }
.rail-related        { order: 4; }
.article__rail .ai   { order: 5; }

/* A hash lands under the sticky header. The headings carry this already; the
   TOC links at the SECTION, so the section needs it too. */
.article section[id] { scroll-margin-top: var(--rail-top); }

/* THE BREAKPOINT IS DERIVED, NOT CHOSEN: it is the width at which the measure
   survives intact with the rail at its floor.
     --measure 680 + --gutter-rail 64 + --rail-min 300 + 2 * --gutter 40 = 1124px
   1124px = 70.25rem. Above it the rail flexes from --rail-min up to --rail as
   room appears; below it the rail stacks above the article, which is where a
   phone puts it. A reading column is not the thing that gives way to a
   navigation aid. */
@media (min-width: 70.25rem) {
  /* The article is the ONLY page wider than --container, and the width is
     computed rather than typed so it tracks --rail and --gutter-rail if either
     moves. Measured on the reference: .article w=1299 at x=70.5. */
  .article.container {
    max-width: calc(var(--measure) + var(--gutter-rail) + var(--rail) + 2 * var(--gutter));
  }
  /* ONE declaration, two consumers. The site header is 1200 on every page, so
     without this it sits ~49px inside the article's left edge and ~50px inside
     the rail's right edge - a header that is NEARLY aligned, which is the
     hardest kind of misalignment to see. The reference hangs this off an
     `is-article` class on <body>; layout.mjs is another lane's file and emits
     no such hook, so the condition is read off the article itself. `:has()` is
     already load-bearing in this file (the reading-page band swap above) and
     degrades the same way: a browser without it leaves the header at 1200,
     which is exactly today's state and not a broken one. */
  body:has(.article--railed) .c-header_inner {
    max-width: calc(var(--measure) + var(--gutter-rail) + var(--rail) + 2 * var(--gutter));
  }
  .article__rail { display: block; grid-column: 2; grid-row: 1 / -1; }
  .article__rail > * { max-width: var(--rail); }
  .article--railed {
    display: grid;
    /* The measure is a FIXED track, not minmax(0, ...). A grid hands free space
       to flexible tracks equally, so two of them shrink TOGETHER and the
       reading column gives way with the rail. Fixing the first track makes the
       rail the only thing that flexes, which is the whole design. */
    grid-template-columns: var(--measure) minmax(var(--rail-min), var(--rail));
    grid-template-rows: max-content 1fr;
    column-gap: var(--gutter-rail);
    align-items: start;
  }
  /* TWO ROWS in one column, and the rail spans both. Row 1 is the headline,
     byline and capsule; row 2 is everything under them. The rail spanning
     1 / -1 is what gives the sticky contents card a containing block TALLER
     than itself - in a single-row grid its area is its own height and sticky
     never engages at all. */
  .article__col--head { grid-column: 1; grid-row: 1; }
  .article__col--body { grid-column: 1; grid-row: 2; }
  /* z-index, because a sticky element without one is painted in source order
     and the reading column scrolls THROUGH it. It sits under the site header
     and over everything else. */
  .article__rail .toc {
    position: sticky; top: var(--rail-top); z-index: 20;
    margin-top: var(--space-4);
  }
  .rail-related { margin-top: var(--space-6); }
}

/* ------------------------------------------------------------- the capsule

   The answer block an answer engine quotes whole, and the component the
   brief's "616px paragraph" was actually pointing at. SQUARE: the left edge is
   an inset shadow rather than a border so the bar is drawn one way on one
   surface, and its width names itself (--bar-accent) instead of borrowing the
   card radius it no longer shares a value with. */
.capsule {
  background: var(--surface);
  border-radius: 0;
  box-shadow: inset var(--bar-accent) 0 0 0 var(--accent);
  padding: var(--space-5) var(--space-6);
}
.capsule p { font-weight: var(--weight-regular); font-size: var(--text-lead); line-height: 1.36; color: var(--ink); }
.capsule .capsule__opinion { margin-top: var(--space-4); }
/* The label is honesty, not decoration: rendered inside the answer block, an
   unmarked opinion paragraph reads as paragraph two of the answer - a stated
   position wearing a fact's clothes. */
.capsule__take {
  display: block; font-family: var(--font-mono); font-size: var(--text-label);
  font-weight: var(--weight-semi); letter-spacing: 0.14em; text-transform: uppercase;
  color: var(--ink-muted); margin-bottom: var(--space-2);
}
.capsule__act {
  display: flex; flex-wrap: wrap; align-items: center;
  gap: var(--space-4); margin-top: var(--space-5);
}
/* The byline sits between the headline and the answer. */
.article__col > .byline { margin-bottom: var(--space-5); }

/* --------------------------------------------------------- set-apart blocks

   `.block__title` takes the h2 TYPE WITHOUT the outdented left rule. The rule
   marks a reading section of the argument and doubles as its divider; Common
   questions and References are appended blocks, not sections, so they get the
   rank and not the bar. `.measure > h2` above cannot reach these - they sit
   inside `section.block`, one level further down. */
.block { margin-top: var(--space-8); }
.block__title {
  font-family: var(--font-body);
  font-weight: var(--weight-semi);
  font-size: var(--text-h2);
  line-height: 1.19;
  text-transform: none;
  letter-spacing: 0;
  color: var(--ink);
  margin-bottom: var(--space-5);
  scroll-margin-top: var(--rail-top);
}

/* The contextual link at the foot of the last reading section. */
.sect-cta {
  margin-top: var(--space-5);
  padding-top: var(--space-4);
  border-top: var(--rule-hair) solid var(--rule);
}

/* ------------------------------------------------------------------ the FAQ

   A native <details>/<summary> with the marker removed - no JavaScript, and
   `min-height` never `height`, because a question wraps. The row ground is
   --faq-row, derived from the same --accent-2 the disclosure circle is filled
   with, so the two read as one family. */
.faq { display: flex; flex-direction: column; row-gap: var(--space-3); }
.faq details { background: var(--faq-row); border-radius: var(--radius-card); }
.faq summary {
  cursor: pointer; list-style: none;
  display: flex; gap: var(--space-4); align-items: center; justify-content: space-between;
  min-height: var(--space-8);
  padding: var(--space-4) var(--space-5);
  font-weight: var(--weight-bold); font-size: var(--text-ui); line-height: 1.4; color: var(--ink);
}
.faq summary::-webkit-details-marker { display: none; }
/* The disclosure circle. The + rotates into an x; both are a control's
   affordance rather than rendered text. Ink on --accent-2 is 7.87:1; WHITE on
   it is 2.67:1 and fails AA, which is why the glyph is ink. */
.faq summary::after {
  content: "+";
  flex: 0 0 auto;
  display: inline-flex; align-items: center; justify-content: center;
  width: 30px; height: 30px;
  background: var(--accent-2); color: var(--on-accent-2);
  border-radius: var(--radius-pill);
  font-family: var(--font-mono); font-weight: var(--weight-semi); line-height: 1;
  transition: transform var(--dur-base) var(--ease);
}
.faq details[open] summary::after { transform: rotate(45deg); }
.faq__a { padding: 0 var(--space-5) var(--space-5); color: var(--ink); }
.faq__a p { font-size: var(--text-body); line-height: 1.67; }
.faq__a a { color: var(--ink); font-weight: var(--weight-bold); border-bottom: var(--stroke-display) solid var(--accent); }
.faq__a a:hover { color: var(--accent-deep); }

/* ------------------------------------------------------------- the Olive band

   The article's one editorial band, at the TOP of the reading column. It runs
   the column's full width at radius 0, which is a band and not a card. It is
   BLUE rather than the footer's orange: black on orange is 6.46:1, the lowest
   reading contrast available here, and this is the one block that has to be
   the most confident thing on the page. Ink on --band-olive is 15.39:1. */
/* `color` ON THE BAND, not only on the title. The title and the eyebrow each
   named --on-field and everything else in the block — the paragraph, any link,
   any small print — inherited the ARTICLE's ink instead, which is the page's
   and not the band's. On paper that was invisible as a bug, because --ink and
   --on-field are both black there. In dark they part: --ink is the cream and
   the band is #7dadd9, so the one block "that has to be the most confident
   thing on the page" rendered its sentences at 2.00:1 while its heading sat at
   7.71:1 beside them. Declared once here, so the two ranks cannot disagree and
   the per-element colours below are the band's own reversal rather than three
   copies of it. The button's offset shadow is drawn on the band too. */
.article__col .cta-band {
  background: var(--band-olive);
  color: var(--on-field);
  border-radius: 0;
  padding: var(--space-6);
  margin-top: var(--space-7);
}
.article__col .cta-band .block__title { margin-top: 0; color: var(--on-field); }
.article__col .cta-band p { margin-bottom: var(--space-5); font-size: var(--text-ui); }
.article__col .cta-band .btn--secondary { --shadow: var(--on-field); }

/* ---------------------------------------------------------------- the notes

   No heading, no card, no tint: a hairline and small print under the
   references. The SENTENCES are load-bearing; the block around them was not.
   Meta inside it is --ink-muted (8.15:1), never --ink-faint (4.67:1). */
.note {
  background: var(--row-tint); border-radius: var(--radius-card);
  padding: var(--space-4) var(--space-5);
  font-size: var(--text-meta); line-height: 1.5; color: var(--ink);
}
.note--quiet {
  background: none; border-radius: 0; padding: var(--space-4) 0 0;
  margin-top: var(--space-6);
  border-top: var(--rule-hair) solid var(--rule);
  color: var(--ink-muted);
}
.note p { font-size: var(--text-meta); line-height: 1.5; }

/* ------------------------------------------------------------ the references

   The check mark renders per citation; the footer line renders only when every
   one is verified, so a partial claim is never made. */
.refs { display: flex; flex-direction: column; row-gap: var(--space-4); list-style: none; margin: 0; padding: 0; }
.refs li { display: flex; gap: var(--space-4); font-size: var(--text-meta); line-height: 1.5; scroll-margin-top: var(--rail-top); }
.refs li::before { content: none; }
.refs__n { font-family: var(--font-mono); font-weight: var(--weight-semi); color: var(--accent); flex: 0 0 auto; font-variant-numeric: tabular-nums; }
.refs__title { font-weight: var(--weight-semi); }
.refs a { word-break: break-word; color: var(--ink); border-bottom: var(--rule-hair) solid var(--accent); }
.refs a:hover { color: var(--accent-deep); }
.refs__meta { color: var(--ink-muted); }
.refs__note { display: block; margin-top: var(--space-1); color: var(--ink-muted); font-size: var(--text-meta); }
.check { color: var(--accent); font-weight: var(--weight-bold); }
.refs__foot {
  margin-top: var(--space-6); padding-top: var(--space-4);
  border-top: var(--rule-hair) solid var(--rule);
  /* No line-height. The reference declares none here, so the foot inherits the
     reading leading (1.67) and sets 23.38px at 14px; a 1.5 typed in by hand
     measured 21px and was the only thing separating this block from the
     reference on a page that carries one. The rows above DO take 1.5, which is
     the reference's own split and not an inconsistency. */
  font-size: var(--text-meta); color: var(--ink-muted);
}

/* ------------------------------------------------------- the contents card

   A framed nav with an eyebrow-and-chevron header row, underlined link rows
   and a hairline footer. Sticky in the rail, stacked above the article below
   the breakpoint. The toggle ships `hidden` in the markup and is revealed by
   script, so a reader without JavaScript gets the open list and no dead
   control - which is the state both sites render today. */
.toc {
  margin-top: var(--space-7);
  background: var(--lift);
  border: var(--frame-border) solid var(--rule);
  box-shadow: var(--shadow-box) var(--ink);
  border-radius: 0;
  padding: var(--space-4) var(--space-5) var(--space-3);
}
.toc__head { display: flex; align-items: center; justify-content: space-between; gap: var(--space-3); }
.toc__label {
  font-family: var(--font-body);
  font-weight: var(--weight-semi); font-size: var(--text-label); line-height: 1.55;
  text-transform: uppercase; letter-spacing: var(--tracking-caps); color: var(--ink-muted);
}
.toc__toggle {
  display: inline-flex; align-items: center; justify-content: center;
  width: 24px; height: 24px; padding: 0;
  border: 0; background: transparent; color: var(--ink); cursor: pointer;
}
.toc__chevron { transition: transform var(--dur-base) var(--ease); }
.toc__toggle[aria-expanded="true"] .toc__chevron { transform: rotate(180deg); }
.toc__list { margin-top: var(--space-2); display: flex; flex-direction: column; list-style: none; padding: 0; }
/* `hidden` must beat the flex above, or the toggle toggles nothing. */
.toc__list[hidden] { display: none; }
.toc__list li { margin: 0; padding: 0; }
.toc__list li::before { content: none; }
.toc__foot {
  margin-top: var(--space-2); padding-top: var(--space-2);
  border-top: var(--rule-hair) solid var(--rule);
}
.toc__foot a { font-size: var(--text-meta); font-weight: var(--weight-semi); }
.toc a {
  display: block;
  padding: var(--space-2) var(--space-4);
  font-family: var(--font-body);
  font-weight: var(--weight-regular); font-size: var(--text-ui);
  color: var(--ink); text-decoration: underline; text-underline-offset: 3px;
  border-bottom: 0;
}
.toc a:hover { background: var(--accent-wash); color: var(--ink); }

/* ------------------------------------------------------------------ related

   Three rows: a caps cluster label with a highlighter band under it, then the
   headline. No pill, no card chrome, no hairline - three rows of
   label-over-headline separate themselves, and a rule between them in a 300px
   rail is one divider too many.

   The colour is read off `data-cluster` on the <li>, which the renderer emits.
   That matters: the map phase measured the OLD related rail deriving its pill
   tone from the row's INDEX, so a row labelled "Policy" rendered mint and the
   next "Screening" row rendered lilac. Binding the colour to the cluster
   attribute is what makes the label and its colour agree. */
.rail-related { margin-top: var(--space-6); }
.rail-related__label { margin-bottom: var(--space-4); }
.related { display: flex; flex-direction: column; row-gap: var(--space-5); list-style: none; margin: 0; padding: 0; }
.related li { display: flex; flex-direction: column; align-items: start; row-gap: var(--space-2); }
.related li::before { content: none; }
.related a {
  font-family: var(--font-body); font-weight: var(--weight-semi);
  font-size: var(--text-body); line-height: 1.29; color: var(--ink);
  border-bottom: var(--stroke-display) solid transparent;
}
.related a:hover { border-bottom-color: var(--accent); }

/* The caps-label device, standalone. `.t-eyebrow` above is the same device on
   the marketing shell; the corpus renderer emits `.eyebrow`. */
.eyebrow {
  font-family: var(--font-body);
  font-size: var(--text-label);
  font-weight: var(--weight-semi);
  letter-spacing: var(--tracking-caps);
  text-transform: uppercase;
  line-height: 1.55;
  color: var(--ink-muted);
}
/* THE MARKER IS THE LABEL'S OWN LINE BOX GROWN BY HALF A --rule-band, not a
   slab drawn across it. An earlier version of this device in the reference WAS
   a fixed band, and at nearly the letter height it sliced every letterform
   into three - tops on paper, middles on colour, feet on paper - and read as a
   clipping bug. Nothing here is a fixed height. */
.eyebrow--strike {
  position: relative;
  isolation: isolate;
  align-self: start;
  display: inline-flex; align-items: center;
  padding-block: calc(var(--rule-band) / 2);
  padding-inline: var(--space-3);
  color: var(--ink);
}
/* THE FALLBACK GROUND WAS THE RULE COLOUR, WHICH IS THE INK. `.eyebrow--strike`
   sets `color: var(--ink)`, so a marker rendered with no `[data-cluster]`
   ancestor drew ink type on an ink slab: 1.00:1, a solid black bar with the
   label inside it, in BOTH themes. It reads as a deliberate blackout rather
   than as a missing attribute, which is why nothing has reported it. The tint
   step is the banding ground this system has and is the one neutral that
   carries --ink at reading contrast on either theme — 15.95:1 on paper,
   12.80:1 on the dark ground. The six real clusters never reach this value. */
.eyebrow--strike::before {
  content: ""; position: absolute; z-index: -1;
  inset: 0;
  background: var(--cluster-ground, var(--row-tint));
  border-radius: 0;
}
/* Read off THIS repo's cluster table, the same one the pill map above uses.
   Two clusters reverse to paper type and they are the two whose grounds are
   dark: black on green is 3.02:1 and black on ink is 1.00:1, against paper on
   green at 6.29:1 and paper on ink at 19.00:1.

   6.29, not the 5.79 both of these said until 2026-08-27: that figure is
   #ffe4e4 on #00682e, the paper from two grounds ago. The number was carried
   forward through two ground changes without being recomputed, which is the
   exact failure this comment exists to prevent. */
[data-cluster="assessment"]   { --cluster-ground: var(--pill-assessment); }
[data-cluster="screening"]    { --cluster-ground: var(--pill-screening); }
[data-cluster="interviewing"] { --cluster-ground: var(--pill-interviewing); }
[data-cluster="policy"]       { --cluster-ground: var(--pill-policy); }
[data-cluster="teams"]        { --cluster-ground: var(--pill-teams); }
[data-cluster="pipeline"]     { --cluster-ground: var(--pill-pipeline); }
[data-cluster="policy"]   .eyebrow--strike,
[data-cluster="pipeline"] .eyebrow--strike { color: var(--on-accent); }

/* ------------------------------------------------------------- take it away

   The <pre> is the one element permitted a max-height with overflow: auto - it
   is a copy target, so it scrolls rather than clips. */
.ai {
  margin-top: var(--space-6);
  background: var(--lift);
  border: var(--frame-border) solid var(--rule);
  /* Ink, not the accent. The contents card above it draws an ink shadow, and
     two framed cards in one rail drawing two different shadows is the kind of
     drift nobody decides and everybody inherits. */
  box-shadow: var(--shadow-box) var(--ink);
  padding: 0;
}
/* Near-black rather than deep green. Paper on ink is 19.00:1. */
.ai__band { background: var(--ink); color: var(--paper); padding: var(--space-5); }
.ai__eyebrow {
  font-family: var(--font-body);
  font-weight: var(--weight-semi); font-size: var(--text-label); line-height: 1.55;
  text-transform: uppercase; letter-spacing: 0.08em; color: var(--paper);
}
.ai__lead { margin-top: var(--space-2); font-size: var(--text-ui); line-height: 1.55; color: var(--paper); }
.ai__act {
  display: flex; flex-wrap: wrap; align-items: center;
  gap: var(--space-4); margin-top: var(--space-4);
}
/* THE ONE ROUND CONTROL, plus the FAQ disclosure circle. Everything else on
   this site is squared; these two are what --radius-pill is still spent on.
   WHITE rather than paper: on the ink band both are legible, and white is the
   one that reads as a control rather than as a hole cut in the band. Ink on it
   is 21.00:1. */
.ai__continue {
  display: inline-block; border-radius: var(--radius-pill);
  background: var(--lift); color: var(--ink);
  font-family: var(--font-body);
  font-weight: var(--weight-semi); font-size: var(--text-ui); text-decoration: none;
  padding: var(--space-3) var(--space-5);
  border-bottom: 0;
}
.ai__continue:hover { background: var(--accent-wash); color: var(--ink); }
.ai__copy {
  border: 0; background: transparent; padding: 0; cursor: pointer;
  font-family: var(--font-body); font-size: var(--text-meta); color: var(--paper);
  text-decoration: underline; text-underline-offset: 3px;
}
.ai pre {
  margin-top: var(--space-4); padding: var(--space-4);
  background: var(--paper); border-radius: var(--radius-card);
  font-family: var(--font-mono); font-size: var(--text-meta); line-height: 1.6;
  white-space: pre-wrap; word-break: break-word;
  max-height: 320px; overflow: auto; color: var(--ink);
}
.ai pre[hidden] { display: none; }
.ai__micro { margin-top: var(--space-4); font-size: var(--text-meta); line-height: 1.5; color: var(--paper); }

/* The band's eyebrow is kept on founder instruction; the reference's band
   carries a bare title. It takes the band's own reversal, the same as the
   title beside it: --ink-muted is 6.61:1 on --band-olive against ink's
   15.39:1 (both computed this session, not transcribed), and a label the
   reader is meant to read is not chrome. */
.article__col .cta-band .eyebrow { color: var(--on-field); margin-bottom: var(--space-3); }

/* THE TABLE'S COLUMN MODEL, article-scoped. `.c-table` is shared with seven
   marketing pages, so the floor goes on the corpus rather than on the rule.
   The reference floors every CELL at 8.5rem instead of flooring the TABLE at
   680px: five 8.5rem columns is 42.5rem, exactly the reading measure, so a
   five-column table of sentences fills the column at 1440 and the wrapper
   scrolls below that rather than crushing the text. Measured on a three-column
   corpus table: the reference distributes 227.4/227.4, the table-floor model
   gave 219.8/235.6. In rem, so the columns track a raised text size.
   The line-height belongs on the table element too, not only on the cells: the
   reference reads 28.08px there against 30.06 inherited. */
.article .c-table { line-height: 1.56; }
.article .c-table th, .article .c-table td { min-width: 8.5rem; }

/* THE ROW HEADER KEEPS ITS SEMANTICS AND LOSES ITS WEIGHT. The renderer emits
   the first body cell as th[scope="row"] where the reference emits a plain td,
   which is the better markup and worth keeping: a screen reader announces the
   row's subject with every cell. But `.c-table th[scope="row"]` also paints it
   semibold, so 62 corpus tables read with a bold first column the reference
   does not have. Measured on one row: reference 400/400/400 at 227.4/230.4/
   222.2, ours 600/400/400 at 228.8/229.7/221.5 - the column widths differed
   only because the heavier face is wider, so matching the weight closes the
   geometry too. Scoped to the article: the seven marketing tables keep the
   emphasis they were designed with. */
.article .c-table th[scope="row"] { font-weight: var(--weight-regular); }

/* ------------------------------------------------- the article's rhythm

   THE SPACING MODEL DIFFERS FROM THE MARKETING PROSE AND HAD TO. The shared
   `.prose` above spaces with adjacent siblings (`.prose > * + *` and
   `.prose p + p`), which works on a flat marketing column. The corpus renderer
   wraps every reading section in a <section>, so `.prose > * + *` only ever
   reaches the sections themselves and nothing inside one is spaced except a
   paragraph that directly follows another paragraph.

   MEASURED, and it is the difference a reader sees before any of the type:
                          reference   before this block
     .prose h2 margin       48/24px        0/24px
     paragraph to list      24px           0px
     Olive band to prose    48px           0px
     section to section     48px           18px

   The reference spaces with margin-BOTTOM on the block plus explicit heading
   margins, so it does not care how the blocks are nested. That model is ported
   here rather than patching the sibling selectors, because the sibling model
   cannot express "space after a list" at all. Adjacent margins collapse, so
   `p + p` above and `p { margin-bottom }` here agree on 24px rather than
   stacking to 48.

   Scoped to `.article`: the 51 marketing pages that use `.prose` keep the model
   they were built and measured with. */
.article .prose p { margin-bottom: var(--space-5); }
.article .prose p:last-child { margin-bottom: 0; }
.article .prose ul, .article .prose ol {
  margin-bottom: var(--space-5);
  display: flex; flex-direction: column; row-gap: var(--space-4);
}
/* The em-dash marker is a flex item here, not an absolute ::before, so the
   list item needs no left padding to make room for it. */
.article .prose ul li, .article .prose ol li {
  display: flex; gap: var(--space-3);
  padding-left: 0; line-height: 1.56;
}
.article .prose ul li + li, .article .prose ol li + li { margin-top: 0; }
.article .prose ul li::before, .article .prose ol li::before {
  content: "\2014";
  position: static; color: var(--ink-muted);
  flex: 0 0 auto; width: auto; height: auto; background: none;
}
.article .prose h2 { margin-top: var(--space-7); }
.article .prose h3 { margin-top: var(--space-6); }
.article .prose h4 { margin-top: var(--space-5); }
.article .block__title { margin-top: var(--space-7); }
.article .cta-band .block__title { margin-top: 0; }

/* The separator carries the spacing, and without it the byline reads as one
   run: "JACOB JUSTICE & OLIVE|AUGUST 2026". The rule above it paints the
   colour and never set a margin. Measured: 8px each side on the reference. */
.byline > span[aria-hidden] { margin-inline: var(--space-2); }

/* ===========================================================================
   THE ARTICLE CTA BAND, IN DARK — two failures that shipped on 450 pages
   ===========================================================================
   `.article__col .cta-band` takes its ground from --band-olive, and in dark
   that token and --accent-2 resolve to the SAME hex, #7dadd9. So the primary
   call to action on the largest page type in the build was a 1.00:1 shape:
   a blue button on a blue band, held together by a frame that was itself only
   1.29:1 before the token fix above. And the focus ring, --focus, is cream in
   dark: #EEECE1 on #7dadd9 is 2.00:1, so keyboard focus was invisible there.

   The band itself is NOT repainted. Its type is --on-field, near-black, and it
   is correct at 7.71:1 on that blue — repainting the ground would drag every
   rung on the band with it. What changes is only the two things that were
   competing with the ground: the button takes the page's own near-black face
   with cream on it (7.71:1 against the band, 15.43:1 for its own label), and
   the ring drops to near-black for the same 7.71:1.

   Scoped to dark because in light the pair is #1ba5ff on #c2e1f6 (1.96:1) held
   by a 5.76:1 frame — thin, but a different problem and not this one. */
:root[data-theme="dark"] .article__col .cta-band .btn--primary {
  background: var(--paper);
  color: var(--ink);
  border-color: var(--ink);
}
.article__col .cta-band :focus-visible { outline-color: var(--on-field); }


/* ===========================================================================
   THE HEADER MUST FIT THE NARROWEST PHONE
   ===========================================================================
   Measured at 320x844 in a real isMobile context, on BOTH surfaces (:4173 the
   live site and :4175 the alternate front page, byte-identical numbers, so this
   is an olive.is defect and not something the alt page introduced):

     documentElement  scrollWidth 360  ·  clientWidth 320
     .c-header        320 wide, correct
     .c-logo          98 wide, ends at 118
     .c-actions       210 wide, ends at 360   <- overflows its own parent by 40

   `.c-actions` is `display:flex` with no shrink, so the primary CTA keeps its
   full padding at every width. 98 + 210 does not fit a ~280px content box, the
   document gains 40px of horizontal scroll, and THE WHOLE PAGE PANS SIDEWAYS —
   which also grows the initial containing block, so every `position: fixed`
   box (the transit layer, the progress bar, the theme toggle) silently becomes
   360 wide too. One unshrinkable button moved five other things.

   The CTA is made smaller rather than hidden, because nothing was found that
   reliably re-offers it: the burger drawer's markup carries no account link
   this build could show, and removing the page's primary action on the
   narrowest screens to fix a layout bug is the wrong trade.

   Scoped to 380px so nothing at 390 or above moves. */
@media (max-width: 380px) {
  .c-actions { gap: var(--space-2); }
  .c-actions .btn {
    padding-inline: var(--space-3);
    font-size: var(--text-label);
    white-space: nowrap;
  }
}

/* ==========================================================================
   Theme control — markup and comments in site/build.mjs, appended to this
   stylesheet by the same file.
   ========================================================================== */

.c-theme { flex: 0 0 100%; }

.c-theme_btn {
  display: inline-flex; align-items: baseline; gap: 0.35rem;
  margin: 0; padding: var(--space-2);
  background: none; color: inherit;
  border: var(--rule-hair) solid currentColor;
  font-family: var(--font-body);
  font-size: var(--text-meta);
  font-weight: 600; line-height: 1;
  letter-spacing: 0.08em; text-transform: uppercase;
  cursor: pointer;
}
/* currentColor, NOT var(--focus), and that is a measurement rather than a
   preference. --focus is the ink of the page body, and the footer is not the
   page body: it is a --field band. In the dark theme --focus resolves to the
   cream #EEECE1, which against the footer's #ff5200 ground computes 2.74:1 —
   under the 3:1 a focus indicator owes. currentColor here is --on-field, the
   footer's own type colour, which is 6.46:1 on that ground in light and
   7.37:1 on #DB9644 in dark, and it cannot drift from the footer because it
   IS the footer's value. (The same 2.74:1 applies to every other focusable
   thing in this band — the footer links and the mail address. That belongs to
   tokens.css and is reported, not fixed here.) */
.c-theme_btn:focus-visible {
  outline: var(--focus-width) solid currentColor;
  outline-offset: var(--focus-offset);
}
.c-theme_slash { font-style: normal; }

/* The transparent border is what keeps the box identical in both states. */
.c-theme_opt {
  padding: 2px 6px;
  border: var(--rule-hair) solid transparent;
  transition: background-color var(--dur-fast) var(--ease),
              color var(--dur-fast) var(--ease);
}
:root[data-theme="light"] .c-theme_opt[data-opt="light"],
:root[data-theme="dark"]  .c-theme_opt[data-opt="dark"] {
  background: var(--on-field);
  color: var(--field);
}

/* Named rather than borrowed: the site ships no visually-hidden utility.
   text-transform: none is not tidiness: the button uppercases its contents,
   and Chrome's accessible-name computation reads the RENDERED text, so this
   span was computing as "DARK THEME" — which some screen readers spell out a
   letter at a time. Measured through CDP before and after. */
.c-theme_sr {
  position: absolute; width: 1px; height: 1px;
  text-transform: none; letter-spacing: normal;
  margin: -1px; padding: 0; overflow: hidden;
  clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap;
}

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