/* ==========================================================================
   Tokens — olive.is, both themes.

   DESIGN_SYSTEM.md is canonical (§4 colour, §5 grounds and edges, §3 type);
   this file ships it. Change a value here and in the guide in the same
   commit, or the guide becomes decoration — tools/check-design-system.mjs
   holds the two together.

   The system in one paragraph: one paper ground, and hierarchy from type and
   1px ink rules. One blue hue in three steps (action, shade, tint) fills every
   button, the closing band and the footer. Green marks links and casts the
   hard 3px offsets, in both themes. Yellow is every caution and highlight.
   One red pair means a failed outcome and nothing else. Corners are square,
   shadows have no blur, and nothing in the orange range ships.

   ---------------------------------------------------------------- read this
   THE DERIVATION LAW. The three neutral steps are not literals chosen by eye:
   each is the ink at a stated alpha over the paper, rounded half up. If the
   paper moves, all three move by computation.

   THE ALIASING TRAP. A var() inside a custom property is substituted on the
   element that DECLARES it, and the resolved string is what inherits. So an
   alias declared on :root resolves once, on :root. That is right for a role
   name nothing below :root re-points, and wrong for anything a band re-points
   — which is why the shadow geometries below carry NO colour. The colour is
   appended where the shadow is drawn: box-shadow: var(--shadow-control)
   var(--shadow). Do not fold it back in.

   NO prefers-color-scheme, on purpose. `(prefers-color-scheme: light)` also
   matches a browser stating no preference. The site boots dark
   (site/build.mjs, THEME_BOOT) and `data-theme` is the only switch; bare
   :root is the light palette, which is what a page paints if the boot dies.
   ========================================================================== */

:root {
  /* --- Grounds -------------------------------------------------------------
     ONE page ground. Two regions are separated by a rule, never by a second
     grey. Ink on the paper is 19.95:1. */
  --paper:         #f9f9f9;
  /* --ground is a LITERAL equal to --paper, not var(--paper): the share-card
     test (site/lib/og.test.mjs) reads this token's value with a regex, and an
     alias reads to it as null. Move the two together by hand. */
  --ground:        #f9f9f9;
  --surface:       var(--paper);   /* legacy name for the paper               */
  --surface-alt:   var(--paper);   /* legacy name; the quiet band is retired  */

  /* A thing sitting OVER the ground — cards, plans, fields, the mega menu.
     White on the paper is 1.05:1 and has no edge of its own, so a lifted
     surface always carries a 1px ink frame. Ink on it is 21.00:1. */
  --lift:          #ffffff;

  /* The neutral fill — ink 8% over the paper. Carries ink at 16.67:1. */
  --row-tint:      #e5e5e5;

  /* The scrim's source only (10% under an open mega menu). No section paints
     it. */
  --ground-dark:   var(--ink);

  /* --- Ink -----------------------------------------------------------------
     THE DERIVED RAMP, ink over paper, rounded half up:
       paper = rgb(249,249,249)   ink = rgb(0,0,0)
       --ink-muted  70%  ->  74.70 x3   ->  75  = #4b4b4b   8.29:1
       --ink-faint  55%  -> 112.05 x3   -> 112  = #707070   4.70:1
       --row-tint    8%  -> 229.08 x3   -> 229  = #e5e5e5
     --ink-faint is CHROME ONLY and never sets a sentence. */
  --ink:           #000000;
  --ink-heading:   var(--ink);
  --ink-muted:     #4b4b4b;
  --ink-faint:     #707070;

  /* One colour at three widths; every rule, frame and focus ring is the ink. */
  --rule:          var(--ink);
  --border:        var(--ink);
  --border-strong: var(--ink);   /* also the chart zero baseline (chart.mjs) */
  --focus:         var(--ink);
  --focus-ring:    var(--ink);
  --focus-width:   2px;
  --focus-offset:  3px;

  /* --- The one blue (§4.2) -------------------------------------------------
     One hue, 204deg, three steps and no fourth.
       action  hsl(204 100% 55%)  button faces, the FAQ and accordion discs,
                                  the fact highlighter — black on it 7.87:1
       shade   hsl(204  94% 13%)  button frames and offsets, the footer —
                                  paper on it 14.59:1
       tint    hsl(204  74% 86%)  the closing band, the reading band, FAQ
                                  rows — black on it 15.39:1
     Nothing white sits on the action blue (2.67:1), and blue never sets type
     on the paper or the lift; the secondary button's label is --blue-label. */
  --blue-action:    #1ba5ff;
  --on-blue-action: #000000;
  --blue-shade:     #022740;
  --on-blue-shade:  #f9f9f9;
  --blue-tint:      #c2e1f6;
  --blue-label:     var(--blue-shade);   /* 15.36:1 on the lift              */

  /* One role name stays an alias: the closing band's, which the footer
     re-points to its own pair. The other blue aliases retired once nothing
     read them (DESIGN_SYSTEM.md §15). */
  --field:         var(--blue-tint);     /* the closing band                 */
  /* Type on the tint is the ink in both themes, so a band's type and its
     edges are one colour. The footer re-points --field and --on-field to the
     shade pair in site.css rather than repainting these. */
  --on-field:      var(--ink);

  /* --- Green — the accent, in both themes ----------------------------------
     The 2px link rule, the 3px offset under cards, the nav hover rule, the
     ok fill, the Policy pill. On paper 6.61:1, on the lift 6.95:1. A green
     ground takes paper type — black on green is 3.02:1 and fails. */
  --accent:        #00682e;
  --accent-deep:   #004e23;   /* hover for anything green-ruled — 9.44:1    */
  --on-accent:     var(--paper);
  --shadow:        var(--accent);   /* the shadow COLOUR; geometry is below */

  /* --- Yellow — every caution and highlight --------------------------------
     Ink on it 16.65:1. Selection stays yellow, not green, so a selection
     never hides a link's green rule. */
  --highlight:     #ffe566;   /* the caution fill, the pull-quote bar         */
  --accent-wash:   #ffe566;   /* ::selection; contents-link hover             */
  --hl-key:        #ffe566;   /* the same in both themes; type #16150f in dark */
  --hl-term:       #c9a2ff;   /* the same in both themes; no consumer          */
  --marker:        #ff81d0;   /* ink 9.30:1                                    */

  /* --- Cluster pills — six, and no seventh ---------------------------------
     Policy and Pipeline take --on-accent; the rest take the ink. */
  --pill-assessment:   #ffe566;
  --pill-screening:    #1ba5ff;
  --pill-interviewing: #ff81d0;
  --pill-policy:       #00682e;
  --pill-teams:        #c9a2ff;
  --pill-pipeline:     var(--ink);

  /* --- Status — the only colours that mean something ----------------------
     Each always travels with its word. The red pair is the only red in the
     palette and means a failed or closed outcome, never decoration. */
  --wash-red:      #ffc6c6;  --ink-red:     #600000;   /* 9.45:1 */
  --ok:            var(--accent);
  --ok-fill:       var(--accent);
  --on-ok:         var(--on-accent);
  --warn:          #746019;           /* as text on the paper, 5.82:1          */
  --warn-fill:     var(--highlight);
  --on-warn:       var(--ink);
  --bad:           var(--ink-red);
  --bad-fill:      var(--wash-red);
  --on-bad:        var(--ink-red);

  /* --- The six dimension tones (§4.4) --------------------------------------
     Identity, never rank. A wash never ships without its ink; the pairs are
     declared two to a line so they cannot be read apart. */
  --wash-mint:   #d1f7ea;  --ink-mint:   #067429;   /* D1 */
  --wash-lilac2: #e5e2ff;  --ink-lilac:  #4f45d6;   /* D2 */
  --wash-cyan:   #ccf4ff;  --ink-cyan:   #003e57;   /* D3 */
  --wash-yellow: #fff8d6;  --ink-yellow: #746019;   /* D4 */
  --wash-pink:   #fde0f0;  --ink-pink:   #a13aab;   /* D5 */
  --wash-teal:   #c3faf5;  --ink-teal:   #0e4343;   /* D6 */

  /* The vignette plates' sheet and text bars (§4.5). The plate is a light
     island, so the dark block restates both at the same values. */
  --plate-sheet:   #ffffff;
  --plate-rule:    #e4e6ec;

  /* The notation palette (--jm-*, the iPhone app's Jefferson marks) lives
     in notation.css beside this file: it is the app's palette, outside this
     system on purpose, and has its own file so the design gate can name it
     (DESIGN_SYSTEM.md §2.9). */

  /* --- Rules and edges -----------------------------------------------------
     --rule-band is the eyebrow marker ONLY, and never a border: it is
     padding-block: calc(var(--rule-band) / 2) on the label's own line box. */
  --rule-hair:      1px;
  --rule-section:   3px;
  --rule-band:      8px;
  --frame-border:   1px;   /* declared as the pixels render it              */
  --frame-heavy:    3px;
  --stroke-display: 2px;   /* link rules                                    */
  --bar-accent:     5px;   /* a WIDTH, not a radius: the capsule's bar       */
  --press:          2px;   /* hover travel — a press, never a lift           */

  /* Hard offsets, zero blur, GEOMETRY ONLY (see the aliasing trap above). */
  --shadow-control: 3px 3px 0 0;
  --shadow-box:     4px 4px 0 0;

  /* --- Radii ---------------------------------------------------------------
     Square is the default. 5px only on the tinted panels; 16px only on the
     report mock's browser frame; a full circle only on round controls and
     markers. px, not rem: a border must not scale with text. */
  --radius-control: 0;
  --radius-card:    0;
  --radius-field:   0;
  --radius-tint:    5px;
  --radius-heavy:   16px;
  --radius-pill:    999px;

  /* --- Type ----------------------------------------------------------------
     Romie is display only (capitals, 700; 500 for the wordmark and the
     italic emphasis). Figtree carries every reading and UI rung. IBM Plex
     Mono is the data register. Georgia is a real fallback: if Romie fails,
     the page reads as a serif editorial page, not a broken one. The
     @font-face rules live in site.css. */
  --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 is zero on display: Romie is always capitals, and negative
     tracking cramps the counters of capitals. The one letterspaced device is
     the caps label, at 0.07em. */
  --tracking-display: 0;
  --tracking-tight:   0;
  --tracking-caps:    0.07em;

  /* THE SEVEN RUNGS. Static rem; a literal rem or px in a font-size is a
     defect. Add a rung only by the owner's ruling. */
  --text-label:  0.6875rem;  /* 11px — caps labels only                      */
  --text-meta:   0.875rem;   /* 14px                                          */
  --text-ui:     1rem;       /* 16px — buttons                                */
  --text-body:   1.125rem;   /* 18px — the reading floor, and the default     */
  --text-lead:   1.25rem;    /* 20px                                          */
  --text-h3:     1.5rem;     /* 24px                                          */
  --text-h2:     2rem;       /* 32px                                          */

  /* The wordmark's own size, not an eighth rung: the one word OLIVE beside
     the mark (§3.2's Wordmark row), 16.8px. */
  --text-wordmark: 1.05rem;

  /* Legacy rung names. Two changed meaning at the 2026-08-27 port:
     --text-h4 was a 24px clamp, --text-eyebrow was 13px. */
  --text-h4:      var(--text-lead);
  --text-small:   var(--text-meta);
  --text-eyebrow: var(--text-label);

  /* The only fluid sizes in the system. They saturate near 790-800px. */
  --text-display:    clamp(2.6rem,  1.4rem + 5.2vw, 4rem);   /* -> 64px */
  --text-article-h1: clamp(2.25rem, 1.5rem + 3vw,   3rem);   /* -> 48px */
  --text-stat:       clamp(2.5rem,  1.4rem + 4.8vw, 4rem);   /* -> 64px */

  /* 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 -------------------------------------------------------------- */
  --measure:        42.5rem;   /* 680px at a 100% root                       */
  --container:      1200px;
  --container-wide: 1440px;
  --gutter:         clamp(1.25rem, 4vw, 2.5rem);
  --header-h:       72px;      /* a MIN-height, never a height               */
  --rail:           475px;
  --rail-min:       300px;
  --gutter-rail:    var(--space-8);
  --rail-top:       calc(var(--header-h) + var(--space-5));

  /* Space — rem, so it follows a reader who raises the text size. Identical
     to the px scale at a 100% root, so a change here needs a 200%-text
     check, not 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-pad:    clamp(3.5rem, 2rem + 5vw, 6rem);
  --section-pad-lg: clamp(5rem, 3rem + 7vw, 8.5rem);

  /* --- Motion -------------------------------------------------------------- */
  --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 the disc and the wordmark. They swap in dark so the
     mark inverts rather than vanishing. */
  --logo-face: #ffffff;
  --logo-ring: var(--ink);
}

/* ===========================================================================
   DARK — the default on the site.

   The ground is the cream ink taken to near-black in the same hue, which is
   why the dark page reads as the same printed object, not an inversion of it.

   Only literals are restated here. An alias declared on :root (--surface,
   --rule, --field, --shadow, --ok ...) follows with nothing
   written, because this block re-points its source on that same element.
   Four dark rungs (the Assessment, Screening, Interviewing and Teams pills)
   pass AA by 0.00-0.05: each is the lightest legal member of its hue by
   design, not slack to round off.
   ======================================================================== */
:root[data-theme="dark"] {
  color-scheme: dark;

  /* --- Grounds ------------------------------------------------------------ */
  --paper:         #16150f;
  --ground:        #16150f;   /* a literal, for the same reason as :root's  */
  --lift:          #201d17;   /* ink on it 14.18:1                          */
  --row-tint:      #272620;   /* ink 8% over the ground — ink on it 12.80:1  */
  --ground-dark:   #000000;

  /* --- Ink -----------------------------------------------------------------
     The ramp, recomputed by the same law with the cream as ink:
       ground = rgb(22,21,15)   ink = rgb(238,236,225)
       --ink-muted  70%  -> 173.20, 171.50, 162.00 -> #adaca2   8.01:1
       --ink-faint  55%  -> 140.80, 139.25, 130.50 -> #8d8b83   5.36:1
       --row-tint    8%  ->  39.28,  38.20,  31.80 -> #272620
     (130.50 rounds UP, to 131.) */
  --ink:           #eeece1;   /* on the ground 15.43:1                      */
  --ink-muted:     #adaca2;
  --ink-faint:     #8d8b83;

  /* --- The one blue --------------------------------------------------------
     The same hue, 204deg.
       action  hsl(204 55% 67%)  #16150f on it 8.19:1
       tint    hsl(204 35% 41%)  the dark-rung rule — the hue at the rung's
                                 saturation, lightness lowered until cream
                                 clears 4.5:1 (4.55:1); 3.39:1 off the paper
       shade   hsl(204 40% 25%)  halfway in contrast between the paper and
                                 the tint (1.84:1 from each), so the footer
                                 reads as its own band under the closing band;
                                 cream on it 8.39:1
     On the tint the shade is only 1.84:1, so a frame or offset standing on a
     dark tint band takes the paper instead (site.css). */
  --blue-action:    #7db4d9;
  --on-blue-action: #16150f;
  --blue-shade:     #27465b;
  --on-blue-shade:  #eeece1;
  --blue-tint:      #446f8c;
  --blue-label:     var(--blue-action);   /* 7.52:1 on the lift            */

  /* --- Green -----------------------------------------------------------------
     --accent-deep is the line colour lightened 35% toward the cream: on a
     dark ground the more emphatic step is the lighter one.
       149 + 0.35 x (238 - 149) = 180.15 -> 180, and 204, 151  = #b4cc97 */
  --accent:        #95bb6f;   /* on the ground 8.37:1, on the lift 7.70:1   */
  /* The offset under a card is NOT the accent here (owner, 2026-09-24): the
     dark accent is a text colour, and under a card on the dark ground it read
     as a pale band behind every card rather than an edge. A deep green keeps
     green under cards and reads as an edge, the way the pink's dark value is
     a deeper pink. The light theme keeps --shadow on --accent. */
  --shadow:        #2f5d36;
  --accent-deep:   #b4cc97;   /* 10.47:1                                    */
  --on-accent:     #16150f;

  /* --- Yellow — the olive-gold rung. Cream on it 4.52:1; 3.41:1 off the
     ground, so it still reads as a mark. --hl-key and --hl-term keep their
     light values in both themes; their type is #16150f here, never the ink. */
  --highlight:     #7d6923;
  --accent-wash:   #7d6923;
  --marker:        #a24f7a;   /* cream on it 4.50:1                         */

  /* --- Cluster pills -------------------------------------------------------
     Four go dark and carry cream; Policy goes LIGHT and carries #16150f. */
  --pill-assessment:   #7d6923;   /* cream 4.52:1                           */
  --pill-screening:    #446f8c;   /* the tint — cream 4.55:1                */
  --pill-interviewing: #a24f7a;   /* cream 4.50:1                           */
  --pill-policy:       #95bb6f;   /* #16150f 8.37:1                         */
  --pill-teams:        #8053be;   /* cream 4.55:1; 3.40:1 off the ground    */

  /* --- Status ----------------------------------------------------------------- */
  --wash-red:      #3d1414;  --ink-red:     #f2a3a3;   /* 8.06:1 */
  --warn:          #e8d68a;   /* as text on the ground 12.56:1              */

  /* --- The six dimension tones, dark pairs (§4.4) ----------------------------
     For a tone that sits directly on the dark page, such as a chart series.
     An illustration that carries its own light sheet (the vignettes, the
     report mock) keeps the light pairs in both themes and does not read these. */
  --wash-mint:   #10352a;  --ink-mint:   #8ee7c4;   /* D1  9.19:1 */
  --wash-lilac2: #272263;  --ink-lilac:  #b9b2ff;   /* D2  7.27:1 */
  --wash-cyan:   #0b3345;  --ink-cyan:   #8ad6f2;   /* D3  8.25:1 */
  --wash-yellow: #332c10;  --ink-yellow: #e8d68a;   /* D4  9.56:1 */
  --wash-pink:   #3d1230;  --ink-pink:   #f0a8dc;   /* D5  8.54:1 */
  --wash-teal:   #0e3b38;  --ink-teal:   #86e6de;   /* D6  8.46:1 */

  /* The plates' sheet and bars do not invert (§4.5): the light values. */
  --plate-sheet:   #ffffff;
  --plate-rule:    #e4e6ec;

  /* --- The mark ------------------------------------------------------------- */
  --logo-face:     #16150f;
}

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

/* The app's Jefferson palette (DESIGN_SYSTEM.md §2.9). */
/* ==========================================================================
   The notation palette — the Olive iPhone app's, verbatim
   ==========================================================================
   Moved out of tokens.css on 2026-09-24. These are the app's colours, not
   this system's: the restyle's design gate refuses two of their oranges and
   two of their blues (checks 9 and 10), and asks every literal colour in
   tokens.css to be restated for dark (check 8), which a pill that carries its
   own ground does not need. So they have their own file, which
   tools/check-design-system.mjs reads like any stylesheet and passes, by
   name, on checks 9 and 10 only (DESIGN_SYSTEM.md §2.9). site.css imports
   it right after tokens.css. A post's look may re-point one of them on its
   own article (kit.css: the light pitch arrow).
   ========================================================================== */

:root {
  /* --- The notation palette — the Olive iPhone app's, verbatim ------------
     Jefferson marks quoted on a research post (site/lib/jefferson.mjs, the
     second post onward) are painted in the app's own token colours —
     olive-ios-app Colors.swift, "Jefferson Token Palette", drawn by
     JeffersonMarkup.swift — so a mark on this site is the mark a person sees
     on the phone. Every mark is a PILL, the token colour behind --jm-on-pill
     type, except pitch, which is a coloured GLYPH with no ground so the arrows
     never out-shout the pills. A pill carries its own ground, so the same
     values hold in both themes and no dark block restates them.

     Computed, --jm-on-pill on each ground:
       pause 4.28  emphasis 3.51  quiet 6.01  faster 4.27  loud 7.43
       stretch 5.01  stretch-long 6.31  breath 5.41  cutoff 5.02
     THREE SIT UNDER 4.5:1 — pause, emphasis and faster. They are the app's
     values carried across, not tuned here; DESIGN_SYSTEM.md 2.9 records them
     as inherited, and moving them is a call about the app, not this page.
     The pitch glyph sits on the page ground itself: 4.54:1 on paper and
     3.82:1 on the dark ground, which clears the 3:1 floor for a graphical
     mark and not the 4.5:1 one for text. Recorded, not fixed, for the same
     reason. */
  --jm-on-pill:      #f5efe0;
  --jm-pause:        #a8567a;
  --jm-emphasis:     #9b7a2e;
  --jm-quiet:        #3e5a8c;
  --jm-faster:       #5a7a3e;
  --jm-loud:         #3e4a7a;
  --jm-stretch:      #8e5a2e;
  --jm-stretch-long: #8e3e3a;
  --jm-breath:       #2e6a6a;
  --jm-cutoff:       #3e6e6a;
  --jm-pitch:        #8a5dbe;
}

/* One post's scoped look (DESIGN_SYSTEM.md §2.10): every rule in it hangs off
   .e-post--kit or .e-progress--kit, so no other page is touched. */
/* ==========================================================================
   The kit look — one post's colours and scroll, scoped to that post
   ==========================================================================
   Founder, 2026-09-24, on "The Human Signal": the approved page built with the
   storyteller kit (olive-eval/prosody_reasoning/nm55/paper/final/piece.html)
   had better colours and better scrolling than its port to this site. Keep
   the site's fonts, its words and its page ground in both themes; take every
   other colour, and the scroll mechanics, from the kit. DESIGN_SYSTEM.md
   §2.10 is the record; this file is the whole implementation.

   SCOPE. Everything here hangs off `.e-post--kit` (the article) or
   `.e-progress--kit` (its reading bar), which a post gets by setting
   `look: 'kit'`. No other page carries either class, so nothing here reaches
   the site's chrome, the first research post, or any other page.

   WHERE THE VALUES COME FROM. Read, not eyeballed, from the kit's stylesheet
   (.claude/skills/storyteller/kit/css.css, its two :root blocks) and the
   paper's extra.css (the light pitch arrow). Where the kit writes a
   color-mix() against its own paper (#fdf5ef light, #120b33 dark), the
   result is written here as the hex it computes to in OKLab, because a canvas
   reads these through getPropertyValue and must be handed a colour, not an
   expression: muted is the ink at 64%, faint at 40%, the selection the accent
   at 40%. The alphas are the kit's own (hairline 0.12, card wash 0.9 / 0.86,
   grid 0.08 / 0.09, axis 0.32) and so are the chart alphas (the kit's hexA:
   plain words 0.75, a tie 0.42, the ghost cells 0.24, the dumbbell's
   connector 0.45).

   WHY IT IS ITS OWN FILE. Four of these colours are outside the site's
   system on purpose — the kit's orange (#c85a0a / #dc7411) and its teal
   (#0287a5 / #29a3c1), which the site's no-orange and one-blue rules would
   refuse. tools/check-design-system.mjs reads this file like every other
   stylesheet and exempts it, by name, from exactly those two checks. A
   stray orange anywhere else still fails.

   THE SITE'S TOKENS ARE RE-POINTED ON THE ARTICLE, NOT ON :root. A token
   such as --rule is `var(--ink)` on :root and is resolved there, so changing
   --ink lower down would leave --rule black: every derived token the post
   reads is restated here for that reason (the aliasing trap tokens.css
   describes). Light first; the dark block restates only the kit palette,
   because every re-point below reads it through var() on the same element.
   ========================================================================== */

.e-post--kit,
.e-progress--kit {
  /* ---- the kit palette, light (css.css :root[data-theme="light"]) ---- */
  --kit-ink:       #26123f;
  --kit-muted:     #6b5d7c;   /* ink 64% over the kit paper, OKLab — 5.73:1 on the site's paper */
  --kit-faint:     #9f93a6;   /* ink 40% */
  --kit-accent:    #d6207f;   /* the magenta — 4.56:1 on the paper */
  --kit-accent-2:  #0287a5;   /* the teal — 3.98:1, the kit's own value (see §2.10) */
  --kit-hairline:  rgba(38, 18, 63, 0.12);
  --kit-wash:      rgba(253, 245, 239, 0.9);   /* the kit's --paper-wash: cards, the log */
  --kit-human:     #6d4fe0;   /* the kit's series-human: the ghost cells, Sonnet 5's dots */
  --kit-machine:   #c85a0a;   /* the kit's series-machine: the finding, Opus 5.5 */
  --kit-grid:      rgba(38, 18, 63, 0.08);
  --kit-axis:      rgba(38, 18, 63, 0.32);
  --kit-selection: #f6aac1;   /* the accent 40% over the kit paper */
  --kit-pitch:     #6f43a8;   /* extra.css: the app's #8a5dbe is dark-only; this keeps 4.5:1 on a light page */

  /* ---- the site's tokens, re-pointed at it ---- */
  --ink:           var(--kit-ink);
  --ink-heading:   var(--kit-ink);
  --ink-muted:     var(--kit-muted);
  --ink-faint:     var(--kit-faint);
  --rule:          var(--kit-hairline);
  --border:        var(--kit-hairline);
  --border-strong: var(--kit-axis);
  --focus:         var(--kit-accent);
  --focus-ring:    var(--kit-accent);
  --accent:        var(--kit-accent);
  --accent-deep:   var(--kit-accent);
  --accent-wash:   var(--kit-selection);
  --shadow:        transparent;   /* the kit draws no offset under a card */
  --lift:          var(--kit-wash);
  --row-tint:      var(--kit-hairline);
  --jm-pitch:      var(--kit-pitch);

  /* ---- the chart roles kinds.js reads (engine.js theme()) ---- */
  --chart-voice:  var(--kit-machine);           /* words + voice won, matched */
  --chart-plain:  rgba(2, 135, 165, 0.75);      /* plain words: the teal at 0.75 */
  --chart-tie:    rgba(200, 90, 10, 0.42);      /* a tie: the orange at 0.42 */
  --chart-ghost:  rgba(109, 79, 224, 0.24);     /* the unlit cells: the violet at 0.24 */
  --chart-title:  var(--kit-accent-2);          /* a panel's title */
  --chart-a:      var(--kit-human);             /* the first series (Sonnet 5) */
  --chart-b:      var(--kit-machine);           /* the second (Opus 5.5) */
  --chart-b-text: var(--kit-accent);            /* the second series' figures */
  --chart-link:   rgba(200, 90, 10, 0.45);      /* the connector between them */
  --chart-grid:   var(--kit-grid);
  --chart-rule:   var(--kit-hairline);
  --chart-track:  rgba(109, 79, 224, 0.14);     /* under each dumbbell row: the violet at 0.14 */

  /* ---- the kit's motion (css.css, extra.css) ---- */
  --kit-ease:       ease;
  --kit-card-dur:   0.5s;    /* a step card fades and rises */
  --kit-card-rise:  14px;
  --kit-layer-dur:  0.45s;   /* a stage layer fades and rises */
  --kit-layer-rise: 8px;
  --kit-rule-dur:   0.4s;    /* a column's accent rule changes colour */
  /* The kit pulls its step column up by half a screen (-50svh), so each card
     crosses the middle of the stage halfway through its own step. Here the
     stage starts under the site's sticky header, which moves the stage's
     middle down by half the header; adding 1.5 headers keeps the card on it. */
  --kit-steps-offset: calc(var(--header-h) * 1.5 - 50svh);
}

/* Text the post does not colour itself inherits from <body>, whose colour was
   resolved from :root's --ink; restating it here makes that text the kit's
   ink too (a reply, a transcript, a list item). */
.e-post--kit { color: var(--kit-ink); }

:root[data-theme="dark"] .e-post--kit,
:root[data-theme="dark"] .e-progress--kit {
  /* ---- the kit palette, dark (css.css :root) ---- */
  --kit-ink:       #f6efff;
  --kit-muted:     #9995b1;   /* 6.35:1 on the site's dark paper */
  --kit-faint:     #615e80;
  --kit-accent:    #ff3d9e;   /* 5.59:1 */
  --kit-accent-2:  #29a3c1;   /* 6.19:1 */
  --kit-hairline:  rgba(246, 239, 255, 0.12);
  --kit-wash:      rgba(18, 11, 51, 0.86);
  --kit-human:     #9274f0;
  --kit-machine:   #dc7411;
  --kit-grid:      rgba(246, 239, 255, 0.09);
  --kit-axis:      rgba(246, 239, 255, 0.32);
  --kit-selection: #68265d;
  --kit-pitch:     #8a5dbe;

  --chart-plain:  rgba(41, 163, 193, 0.75);
  --chart-tie:    rgba(220, 116, 17, 0.42);
  --chart-ghost:  rgba(146, 116, 240, 0.24);
  --chart-link:   rgba(220, 116, 17, 0.45);
  --chart-track:  rgba(146, 116, 240, 0.14);
}

@media (prefers-reduced-motion: reduce) {
  .e-post--kit { --kit-card-dur: 0.01s; --kit-layer-dur: 0.01s; --kit-rule-dur: 0.01s; --kit-card-rise: 0px; --kit-layer-rise: 0px; }
}

/* ==========================================================================
   Colour, element by element, where a re-pointed token is not enough
   ========================================================================== */

/* The reading bar: the kit's two accents, teal into magenta. */
/* Doubled class: site.css's `.e-progress > i` comes later at the same weight. */
.e-progress.e-progress--kit > i { background: linear-gradient(90deg, var(--kit-accent-2), var(--kit-accent)); }

/* Step cards: the kit's wash behind a hairline, the accent down the left
   edge, no offset; the body in the ink and the kicker in the accent. The
   backdrop blur is the kit's, the recipe the theme chip already uses. */
.e-post--kit .e-scene_steps .step,
.e-post--kit .e-turn_steps .step {
  border-left: var(--stroke-display) solid var(--kit-accent);
  box-shadow: none;
  -webkit-backdrop-filter: blur(6px);
  backdrop-filter: blur(6px);
}
.e-post--kit .e-scene_steps .step p,
.e-post--kit .e-turn_steps .step p { color: var(--kit-ink); }
.e-post--kit .step p.e-step_kicker,
.e-post--kit .step p.e-step_figure { color: var(--kit-accent); }
.e-post--kit .step p.e-step_fine,
.e-post--kit .e-turn_leg { color: var(--kit-muted); }

/* The post's own head: the eyebrow line in the accent, as the kit's masthead. */
.e-post--kit .e-meta,
.e-post--kit .e-meta a { color: var(--kit-accent); }
.e-post--kit .t-lead { color: var(--kit-muted); }

/* The test-turn stage (extra.css .cs-*): the plain column ruled in the axis
   grey and the voice column in the teal; the verdict lights the favoured
   column in the magenta (the base rule, which reads --accent). Sonnet 5's
   replies are ruled and labelled in the teal, Opus 5.5's in the orange. */
.e-post--kit .e-turn_col { border-top-color: var(--kit-axis); }
.e-post--kit .e-turn_col--voice { border-top-color: var(--kit-accent-2); }
.e-post--kit .e-turn_label { color: var(--kit-muted); }
.e-post--kit .e-turn_col--voice .e-turn_label { color: var(--kit-accent-2); }
.e-post--kit .e-turn_reply[data-model="1"] { border-left-color: var(--kit-accent-2); }
.e-post--kit .e-turn_reply[data-model="2"] { border-left-color: var(--kit-machine); }
.e-post--kit .e-turn_reply[data-model="1"] .e-turn_who { color: var(--kit-accent-2); }
.e-post--kit .e-turn_reply[data-model="2"] .e-turn_who { color: var(--kit-machine); }
.e-post--kit .e-turn_chip.is-win {
  border-color: var(--kit-accent);
  background: color-mix(in oklab, var(--kit-accent) 12%, transparent);
}

/* Tables: the head ruled in the axis grey, the rows in the hairline. */
.e-post--kit .c-table thead th,
.e-post--kit .c-table th:not([scope="row"]) { border-bottom-color: var(--kit-axis); }

/* The receipts: on the page rather than on a blue panel, under a hairline,
   as the kit's receipts sit; the count and the method labels are the kit's
   quiet caps (its .aff), the count in the accent. */
.e-post--kit .e-receipts { background: transparent; border-top: var(--rule-hair) solid var(--kit-hairline); }
.e-post--kit .e-receipts_count {
  background: transparent;
  color: var(--kit-accent);
  box-shadow: inset 0 0 0 var(--frame-border) var(--kit-hairline);
}
.e-post--kit .e-receipts .c-table thead th { color: var(--kit-muted); }
.e-post--kit .pill,
.e-post--kit .e-receipts .pill {
  --cluster-ground: transparent;
  color: var(--kit-muted);
  box-shadow: none;
}

/* The reader's guess: the field on the page, ruled in the axis grey. */
.e-post--kit .e-guess_field input { background: transparent; border-color: var(--kit-axis); }
.e-post--kit .e-guess_field input:focus { border-color: var(--kit-accent); }

/* ==========================================================================
   Scroll, the kit's way
   ==========================================================================
   A scene is (steps + 1) x 100vh tall (the post sets `h`), its visual sticks
   under the header for the height of the screen, and its step cards are NOT
   pinned: each one sits in a 100svh row of its own, the column pulled up by
   half a screen, so a card rides up with the page and is centred on the stage
   halfway through its step. A card fades in over 0.5s from 14px below when
   its step starts, and out when the next one does.

   Scenes are gated on motion being welcome; the test turns are gated on
   `data-live` (blocks.js), which is never set under reduced motion. So a
   reader who asked for less motion, or has no script, gets the finished
   page the base rules draw. */

@media (prefers-reduced-motion: no-preference) {
  .e-post--kit .e-scene_sticky {
    height: calc(100svh - var(--header-h));
    padding-bottom: var(--space-6);
  }
  .e-post--kit .e-scene_steps {
    position: relative; inset: auto; z-index: 1;
    display: grid; grid-auto-rows: 100svh;
    margin-top: var(--kit-steps-offset);
  }
  .e-post--kit .e-scene_steps .step {
    grid-area: auto;
    position: relative; top: auto; margin-top: 0;
    align-self: center;
    transform: translateY(var(--kit-card-rise));
    transition: opacity var(--kit-card-dur) var(--kit-ease), transform var(--kit-card-dur) var(--kit-ease);
  }
  .e-post--kit .e-scene_steps .step.is-active { transform: none; }
}

.e-post--kit .e-turn[data-live] .e-turn_sticky {
  top: var(--header-h);
  height: calc(100svh - var(--header-h));
}
.e-post--kit .e-turn[data-live] .e-turn_steps {
  position: relative; inset: auto; z-index: 1;
  grid-auto-rows: 100svh;
  margin-top: var(--kit-steps-offset);
}
.e-post--kit .e-turn[data-live] .e-turn_steps .step {
  grid-area: auto;
  position: relative; top: auto; margin-top: 0;
  align-self: center;
  transform: translateY(var(--kit-card-rise));
  transition: opacity var(--kit-card-dur) var(--kit-ease), transform var(--kit-card-dur) var(--kit-ease);
}
.e-post--kit .e-turn[data-live] .e-turn_steps .step.is-active { opacity: 1; transform: none; }

/* A layer of the stage fades and rises the kit's 8px over 0.45s; the one it
   replaces leaves in the site's base duration, so the two barely overlap.
   The favoured column's rule turns over 0.4s. */
.e-post--kit .e-turn[data-live] .e-turn_reply,
.e-post--kit .e-turn[data-live] .e-turn_verdict { transform: translateY(var(--kit-layer-rise)); }
.e-post--kit .e-turn[data-live] .e-turn_stage[data-stage="1"] .e-turn_reply[data-model="1"],
.e-post--kit .e-turn[data-live] .e-turn_stage[data-stage="2"] .e-turn_reply[data-model="2"],
.e-post--kit .e-turn[data-live] .e-turn_stage[data-stage="3"] .e-turn_verdict {
  transform: none;
  transition: opacity var(--kit-layer-dur) var(--kit-ease), transform var(--kit-layer-dur) var(--kit-ease), visibility 0s;
}
.e-post--kit .e-turn[data-live] .e-turn_col { transition: border-color var(--kit-rule-dur) var(--kit-ease); }
/* On a phone the inputs take turns in the cell too (site.css says why); they
   come back with the kit's fade and rise. */
@media (max-width: 899px) {
  .e-post--kit .e-turn[data-live] .e-turn_stage:not([data-stage="0"]):not([data-stage="all"]) .e-turn_input { transform: translateY(var(--kit-layer-rise)); }
  .e-post--kit .e-turn[data-live] .e-turn_input {
    transition: opacity var(--kit-layer-dur) var(--kit-ease), transform var(--kit-layer-dur) var(--kit-ease), visibility 0s;
  }
}

/* Wide: the cards keep the reserved column on the left, and a chart moves
   right to leave it clear, as the stage of a test turn already does. */
@media (min-width: 900px) and (prefers-reduced-motion: no-preference) {
  .e-post--kit .e-scene_sticky { padding-left: calc(max(0px, (100% - 72rem) / 2 - var(--gutter)) + var(--gutter) + 22rem + var(--space-7)); }
  .e-post--kit .e-scene_steps {
    max-width: calc(72rem + var(--gutter) * 2); margin-inline: auto;
  }
  .e-post--kit .e-scene_steps .step { justify-self: start; width: min(22rem, 100%); }
}

/* Phones: a scene's card crosses the middle of the chart, and a test turn's
   card rides the foot of its row, 3svh up, over the stage (extra.css). */
@media (max-width: 899px) and (prefers-reduced-motion: no-preference) {
  .e-post--kit .e-scene_sticky { padding-bottom: var(--space-5); }
  .e-post--kit .e-scene_steps .step { justify-self: stretch; width: auto; bottom: auto; }
}
@media (max-width: 899px) {
  .e-post--kit .e-turn[data-live] .e-turn_steps { display: grid; }
  .e-post--kit .e-turn[data-live] .e-turn_steps .step {
    position: relative; left: auto; right: auto; top: auto;
    align-self: end; margin-bottom: 3svh;
    justify-self: stretch; width: auto;
    opacity: 0;
  }
}


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

/* --- The editorial faces -------------------------------------------------
   Thirteen rules over thirteen files. 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.

   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; }
/* The italic is ONE variable file per script, not four static cuts: Google's
   Figtree italic, weights 300 to 900 on the wght axis, split latin and
   latin-extended (the two unicode-ranges below are Google's own, so a page
   fetches the -ext file only when it draws a character from it). The range on
   font-weight is what makes every weight a drawn italic: `.t-em` in a Figtree
   heading asks for 500, `.prose em` and `.e-note` for 400, a bold <em> for 700,
   and each is an instance of the one file rather than a slanted roman. Declare
   a single weight here instead and every other weight goes back to being faked.
   DESIGN_SYSTEM.md §3.1 names the pair, and the design gate's check 11 reads
   the range, so a single weight here fails it. */
@font-face { font-family: Figtree; src: url(../fonts/Figtree-Italic.woff2)     format("woff2"); font-weight: 300 900; font-style: italic; font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; }
@font-face { font-family: Figtree; src: url(../fonts/Figtree-Italic-ext.woff2) format("woff2"); font-weight: 300 900; font-style: italic; font-display: swap;
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF; }

/* 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%; }
/* Every code, kbd and samp takes the mono (§7.13). Left to the user agent they
   fell back to the browser's own monospace — 150 elements on /how-it-works,
   /sample-report, a benchmark and the calculator. The size is the Code rung
   (§3.2, 14px), a token and not an `em`: a relative size took code off the
   scale (15.75px in body text, 12.25px in a 14px caption) and compounded inside
   a pre that sets its own size, shrinking the tools pages' copyable credit
   lines from 14px to 12.25px. Inside a pre the code takes the pre's size, so
   the block sets it once. */
code, kbd, samp { font-family: var(--font-mono); font-size: var(--text-meta); }
pre code, pre kbd, pre samp { font-size: inherit; }
pre { font-family: var(--font-mono); }
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.95:1. */
  outline: var(--focus-width) solid var(--focus);
  outline-offset: var(--focus-offset);
}

/* Yellow, not the accent: a green selection behind green link rules erases
   the one device that marks a link (§4.3). Ink on it is 16.65:1 on #ffe566
   and 4.52:1 on the dark theme's #7d6923 — the GROUND moves with the theme and
   the type rides it, so this pair needs no reversal of its own. */
::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.

   Inside a Figtree heading (an h3, a section h2 in sentence case, the essay's
   pull quote) the same span is Figtree 500 italic (§3.4), drawn from the
   variable italic declared with the other Figtree rules. */
.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.70: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 byline's separator and a trail's take the faint ink, the chrome step
   (§4.3). The renderers used to write them an inline `opacity:.45`, an
   unnamed grey this rule had to override with !important; none does now. */
.byline > span[aria-hidden], nav.t-eyebrow > span[aria-hidden] {
  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.95: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 closing 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 CLOSING BAND. `.bg-dark` is a legacy slot name: it paints the blue
   tint (§7.16), the first of the page's two loud bands — the footer is the
   second, in the shade. Its type is --on-field, which is the ink in both
   themes: black on the light tint at 15.39:1, cream on the dark tint at
   4.55:1, and never the muted ink, which is 2.36:1 on the dark tint. Every
   edge drawn on the band — the shadow, the mark, a link's rule, the focus
   ring — takes --on-field too, so the band's type and its edges are one
   colour. */
.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 band are ink with an ink rule (§7.5): the green is 2.47:1 on
   the dark tint. A button and the logo carry their own edge, so neither takes
   the rule. */
.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 type:
   15.39:1 on the light tint and 4.55:1 on the dark one (§12). */
.bg-dark :focus-visible { outline-color: var(--on-field); }


/* 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 1px 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 the action blue is 2.67:1 and fails WCAG AA outright; the ink is
   7.87:1 in light and the dark paper 8.19:1 in dark (DESIGN_SYSTEM.md §7.4).
   The frame and the offset are the shade, so the control's boundary reads
   against its own blue face (5.76:1 / 4.45:1). */
.btn--primary {
  background: var(--blue-action);
  color: var(--on-blue-action);
  border-color: var(--blue-shade);
  box-shadow: var(--shadow-control) var(--blue-shade);
}
.btn--primary:hover { background: var(--blue-action); }

/* THE SECONDARY'S LABEL AND FRAME ARE --blue-label, ITS OFFSET THE SHADE
   (§7.4, decided 2026-09-23). The label used to be the action blue on the
   lift: 2.67:1 on white, a WCAG AA failure on every inner page. --blue-label is
   the shade in light (15.36:1 on white) and the action in dark (7.52:1 on the
   dark lift) — the one blue that sets type in this system. */
.btn--secondary {
  --shadow: var(--blue-shade);
  background: var(--lift);
  color: var(--blue-label);
  border-color: var(--blue-label);
  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 A TINT BAND — the closing band (`.bg-dark`) and the reading band inside
   the article column (`.cta-band`) — every button keeps its own face and
   label (§7.4). In light nothing else changes either: the navy frame and
   offset read at 11.26:1 on the pale tint. In dark the shade is 1.84:1
   against the dark tint, so a frame or offset that would be the shade takes
   the dark paper, 3.39:1. The primary's face stays the action blue (2.41:1
   against the band, its label 8.19:1); the secondary keeps its --blue-label
   frame and label on the lift. Declared on the button, not the band: a
   shadow colour resolved on :root would not follow a re-point here (the
   aliasing trap tokens.css describes). */
:root[data-theme="dark"] .bg-dark .btn--primary,
:root[data-theme="dark"] .cta-band .btn--primary {
  border-color: var(--paper);
  box-shadow: var(--shadow-control) var(--paper);
}
:root[data-theme="dark"] .bg-dark .btn--secondary,
:root[data-theme="dark"] .cta-band .btn--secondary { --shadow: var(--paper); }
/* The ghost has no face, so its label and frame are drawn in the band's own
   type, the ink. */
.bg-dark .btn--ghost, .cta-band .btn--ghost { color: var(--on-field); border-color: var(--on-field); }
.bg-dark .btn--ghost:hover, .cta-band .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.61: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. site/lib/stories.mjs:93-100 maps
   mint→assessment, cyan→screening, lilac→interviewing, yellow→policy,
   pink→teams, teal→pipeline; the class names are slots, not colours (§7.9).
   Copying the reference's map verbatim 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.61:1 and paper on ink is 19.95: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, the dark block), 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, read through
   the status roles (DESIGN_SYSTEM.md §4.3) so a status is named as one. */
.pill--green { --cluster-ground: var(--ok-fill);   color: var(--on-ok); }
.pill--amber { --cluster-ground: var(--warn-fill); color: var(--on-warn); }
.pill--rust  { --cluster-ground: var(--bad-fill);  color: var(--on-bad); }

/* On a band or the footer a pill is never its cluster colour (§7.7): the
   dark yellow and the dark tint are the same luminance (1.00:1), and
   pipeline's ink ground reads as a hole punched in the band. Every pill there
   takes ONE construction: the paper, an inset 1px ink ring, ink type. */
.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: var(--font-display);
  font-feature-settings: "liga" 1, "ss15" 1, "kern" 1, "calt" 1;
  font-weight: var(--weight-medium);
  font-size: var(--text-wordmark);
}

.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 past
     the breakpoint below. The label rung takes the bar back under 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); }
/* `flex: none`: an svg is a shrinkable flex item, so a bar one pixel short of
   room took the pixels out of the chevrons first — 0px wide at 1240, under the
   12px of §7.1 until 1325 — and hid the defect it should have shown. */
.c-nav_link svg { flex: none; 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: the lift, a 1px
     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); }

/* THE FULL NAV SHOWS FROM 1344px (84rem); THE BURGER BELOW. `white-space:
   nowrap` stops a label wrapping but cannot make room. Drawn whole — seven
   items with their five 12px chevrons, the 32px gaps, the three actions and
   the 40px gutters — the bar needs 1328px (mark 85 + nav 718 + actions 381 +
   64 + 80, measured on /how-it-works and an answer page). 1344 is that plus
   16px for a longer label or a wider platform face.

   DESIGN_SYSTEM.md §7.1 and §11 say 1344. The switch was 1240 for a day,
   from a measurement of 1227 taken while the chevrons were being squeezed to
   nothing: at 1240 they drew 0px wide and stayed under 12px until 1325, with
   the primary 13px from the viewport edge, and once they cannot shrink
   (`flex: none` above) 1240 scrolls the page sideways by 47px. Both rules
   below move together, and so does `mqDesktop` in components.js — the pair
   swaps the bar for the burger, and moving one alone shows neither or both. */
@media (max-width: 1343px) {
  .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-tint);
}
.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: 1343px) { .c-burger { display: flex; } }   /* moves with the rule above */

.c-mobile {
  position: fixed; inset: var(--header-h) 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;
}
/* NO GRADIENT BEHIND THE HERO. A gradient wash is the one thing this
   language has no ground for (§5.2): the page has a single paper ground, and
   the headline carries the fold on type and space. The two blooms that sat
   here are retired, with their tokens (§15). */
.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. An inset box-shadow ring
     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: var(--weight-semi); 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: var(--weight-regular); 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;
}
/* Invalid (§7.12): the frame takes the red ink at 2px — the 1px border plus a
   1px inset ring, so nothing shifts. The deep green it used to take is the
   link hover, not an error. */
.c-form [aria-invalid="true"] {
  border-color: var(--ink-red);
  box-shadow: inset 0 0 0 var(--frame-border) var(--ink-red);
}

.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;
}
/* The hero column is the reading measure (§6.3): it was typed as 42rem, 8px
   short of the column every other block on the page runs. */
.c-page-hero_inner { position: relative; z-index: 1; max-width: var(--measure); }
.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 closing band's card-sized sibling: the blue tint, the
   ink as its type (15.39:1 / 4.55:1) and its shadow — never the muted ink,
   which is 2.36:1 on the dark tint. No page renders one today;
   sections.mjs can emit it. */
.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 1px 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 IS A LIGHT ISLAND, IN BOTH THEMES (DESIGN_SYSTEM.md
   §4.4, §5.1): an illustration that carries its own sheet does not invert.
   media.mjs emits each plate's dimension pair on the SVG as --w (the wash) and
   --k (its ink), and draws its sheets and text bars through the two names
   below. The wash is the ground, the ink carries every mark, the sheets are
   white and the bars the plate rule (§4.5), whatever the page's theme.

   It used to swap in dark — the wash moved onto the marks and the sheets and
   bars took the page's lift and a --hairline — and --hairline is declared only
   by the home page's story.css, so on every inner page the bars resolved to
   nothing and filled black: #000000 bars on #201d17 sheets, the most visible
   defect in the default theme. The guide's rule is the island, so the swap
   went rather than being patched. */
.c-card_media svg, .c-rowmedia svg {
  --plate-bg: var(--w);
  --plate-fg: var(--k);
  /* --plate-sheet (#ffffff) and --plate-rule (#e4e6ec) come from tokens.css,
     the same value in both theme blocks. */
}

/* 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
   blue tint, the lightest step of the button's own hue, 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
   15.39:1 in light and 4.55:1 in dark. */
.c-acc_item {
  border-bottom: 0;
  background: var(--blue-tint);
  border-radius: var(--radius-tint);
}
.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(--blue-action); color: var(--on-blue-action);
  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 closing band the attribution is the band's ink: the muted step is
   2.36:1 on the dark tint 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 THE PROSE LINK. 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 measures 6.61:1 on
   the light paper and 8.37:1 on the dark one (§4.6) — 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 pale 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 — the blue band, a directory of columns
   ========================================================================== */

/* THE FOOTER IS THE PAGE'S SECOND AND LAST LOUD BAND, in the blue shade
   (§7.17, decided 2026-09-23): #022740 with paper type at 14.59:1 in light,
   #27465b with cream at 8.39:1 in dark. Everything inside takes `--on-field`,
   never the muted ink, and nothing green or blue sets on it.

   RE-POINTED HERE, NOT REPAINTED AT THE TOKEN. `--field` is still the closing
   band above the footer (`.bg-dark`, the tint), and the two bands are meant to
   read as two things rather than one band printed twice (11.26:1 apart in
   light, 1.84:1 in dark). So the footer declares its own `--field`/`--on-field`
   pair as the shade's, and every rule below that reads the pair (column heads,
   links, the base rule, the theme chip's own re-pointing) follows without
   being edited. The var() inside a custom property resolves on THIS element,
   which is the aliasing rule tokens.css warns about and here is exactly what
   is wanted: `--blue-shade` is theme-aware on :root, so the footer follows the
   theme through it, and the hand-written home page's footer
   (alt-front-v2/index.html) follows too, because it loads this stylesheet. */
/* Same construction as `.bg-dark`: every EDGE on the footer takes its type,
   --on-field, rather than the page's --ink — black on the light shade is
   1.37:1 and would vanish. */
.c-footer {
  --field: var(--blue-shade);
  --on-field: var(--on-blue-shade);
  background: var(--field);
  color: var(--on-field);
  --shadow: var(--on-field);
  --logo-ring: var(--on-field);
  padding-block: var(--section-pad) var(--space-6);
}
/* The focus ring is an edge on the footer too (DESIGN_SYSTEM.md §7.4, §12).
   The page's --focus is the ink: black on the light shade is 1.37:1, an
   invisible ring on every footer link. --on-field here is the shade's own
   type, 14.59:1 and 8.39:1 — the same reversal as `.bg-dark :focus-visible`. */
.c-footer :focus-visible { outline-color: var(--on-field); }
.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: the footer's pair is the shade's, and a chip
      drawn in it floats over the paper everywhere else on the page.
      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.
      Computed from tokens.css, both themes, the reversed active rung:
        light  --paper type on --ink ground   = 19.95:1
        dark   --paper type on --ink ground   = 15.43:1
      And the resting label, --ink-muted on the paper: 8.29:1 and 8.01:1.
      The chip floats, so the worst ground under it is the footer's shade,
      which the 90% paper wash lets through at 10%. Composited there
      (2026-09-23, the footer in the shade): --ink-muted is 6.82:1 in light and
      7.67:1 in dark, and the edge, --rule, 16.41:1 and 14.78:1. Every figure
      is 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 (the contact form, the article header).
      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);
}

/* The chip floats over the page AND over the footer, and no one ring colour
   reads on both: the ink is 19.95:1 on the paper and 1.37:1 on the light
   shade; the shade's type is the reverse. So the chip's ring stays the page's
   ink (--on-field is re-pointed to it above) and carries its own ground — a
   paper backing one pixel wider than the ring — so it is ink on paper
   wherever the chip happens to sit. */
.c-footer .c-theme_btn:focus-visible {
  box-shadow: 0 0 0 calc(var(--focus-offset) + var(--focus-width) + 1px) var(--paper);
}

@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: 0; }
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(--ok-fill);   color: var(--on-ok); }
.pill--warn { --cluster-ground: var(--warn-fill); color: var(--on-warn); }
.pill--bad  { --cluster-ground: var(--bad-fill);  color: var(--on-bad); }

/* On a band all three take the band's one pill construction — paper ground,
   ink outline, ink text (§7.7) — because a status colour on the tint loses its
   edge: the dark yellow and the dark tint are 1.00:1 apart. A status keeps its
   word. */
.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(--ink-red); box-shadow: inset 0 0 0 var(--frame-border) var(--ink-red); }


/* ==========================================================================
   Button states
   ==========================================================================
   No block repaints a button on hover. In this language a control does not
   change colour under the cursor: it TRAVELS 2px toward its own offset
   shadow, which is declared once with the button. The nav's hover reveals a
   green rule instead of growing a background. */

/* ==========================================================================
   The wash rotation
   ==========================================================================
   THE ROTATION IS THE PALETTE (§7.9): it gives peers in a set distinct
   colours by position, and a rotation colour marks a position and nothing
   else. The nine class NAMES survive because 2,448 built elements carry them
   and the renderers pick them by index; they are slot names, not colours —
   `.wash-mint` paints the yellow.

   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 slot names are NOT clusters. They are the tail of the
   rotation that the renderers reach on a long row, and they take the row tint
   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 coloured 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.29: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. Every one takes the action blue: its
   type on it is 7.87:1 in light and 8.19:1 in dark. */
.c-acc_item .c-acc_icon { background: var(--blue-action); color: var(--on-blue-action); }

.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 smaller than the number — the price is the thing
   being read. It takes the meta rung: the 0.42em it was set at came to 10px,
   under the 11px floor and on no rung. */
.c-plan_price span {
  font-size: var(--text-meta);
  font-weight: var(--weight-medium);
  letter-spacing: 0;
  color: var(--ink-muted);
}


/* --------------------------------------------------------------------------
   The mark on a band
   --------------------------------------------------------------------------
   On the closing band and the footer the mark reverses with the band: the
   disc and the wordmark take the band's type, --on-field, with everything else
   on it, 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. On the footer
   that is the shade's pair — paper on #022740 at 14.59:1, cream on #27465b at
   8.39:1 — and on the closing band the ink on the tint. */
.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(--accent);
  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. It takes the same rung as
   `.c-quote` too, the large h3 (§3.2): it was a fluid clamp of its own that
   reached 32px, a size outside the scale for a sentence. */
.e-pull {
  position: relative;
  font-family: var(--font-body);
  font-weight: var(--weight-semi);
  font-size: var(--text-h3);
  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-tint);
  /* 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: var(--header-h);
  height: calc(100vh - var(--header-h));
  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,
.e-turn_steps .step {
  grid-area: 1 / 1;
  align-self: start;
  justify-self: end;
  width: min(380px, 100%);
  margin-top: calc(100vh - var(--header-h) - 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,
.e-turn_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,
.e-turn_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 - var(--header-h)); 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 blue tint ground,
   because both are a native <details> a reader opens. It is square: the 5px
   tint radius is for the panels DESIGN_SYSTEM.md §5.3 names (FAQ details and
   accordion items, the calculator, the limits box, the open burger), and this
   is a table of sources, not a row. */
.e-receipts {
  margin-block: var(--space-9) var(--space-8);
  border-radius: 0;
  background: var(--blue-tint);
  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(--blue-action); color: var(--on-blue-action);
  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); }
/* The panel is a tint, so what sits on it follows the tint's rules (§4.2,
   §7.7): the column heads take the ink, because the muted ink is 2.36:1 on the
   dark tint; and a pill takes the paper with an inset ink ring, as on every
   band, because a coloured pill on a tint loses its edge. */
.e-receipts .c-table thead th { color: var(--ink); }
.e-receipts .pill {
  --cluster-ground: var(--paper);
  color: var(--ink);
  box-shadow: inset 0 0 0 var(--frame-border) var(--rule);
}

@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; }
}

/* ==========================================================================
   Essay blocks — added with the second post (2026-09-24)
   ==========================================================================
   Every class below belongs to a block a post opts into (site/lib/story.mjs),
   so the first post, which uses none of them, draws exactly as it did.

   THE DEFAULT IS THE FINISHED STATE. A test turn renders with every reply and
   verdict showing and its step cards as a plain list; the scroll mechanics
   switch on only when blocks.js marks the turn `data-live`, which it does not
   do under reduced motion. So no script and reduced motion get the same
   complete page, and a script that fails leaves nothing hidden. */

/* --- the notation marks ---------------------------------------------------
   A pill is the app's token colour behind the app's on-pill ink; pitch is a
   coloured glyph on the page itself. Colour is never the only signal — the
   mark's own characters are the notation — and every mark carries its
   meaning as a tooltip. */
.jm { font: inherit; }
.jm--pill {
  background: var(--jm);
  color: var(--jm-on-pill);
  border-radius: var(--radius-card);
  padding: 0 0.22em;
  -webkit-box-decoration-break: clone;
  box-decoration-break: clone;
}
.jm--glyph { color: var(--jm-pitch); font-weight: var(--weight-bold); }
.jm--pause        { --jm: var(--jm-pause); }
.jm--emphasis     { --jm: var(--jm-emphasis); }
.jm--quiet        { --jm: var(--jm-quiet); }
.jm--faster       { --jm: var(--jm-faster); }
.jm--loud         { --jm: var(--jm-loud); }
.jm--stretch      { --jm: var(--jm-stretch); }
.jm--stretch-long { --jm: var(--jm-stretch-long); }
.jm--breath       { --jm: var(--jm-breath); }
.jm--cutoff       { --jm: var(--jm-cutoff); }

/* --- the hero: speech, then the voice written in -------------------------- */
.e-hero {
  max-width: calc(60rem + var(--gutter) * 2);
  margin: var(--space-7) auto 0;
  padding-inline: var(--gutter);
}
.e-hero_label { margin-bottom: var(--space-3); }
.e-hero_line { font-family: var(--font-mono); font-size: var(--text-lead); line-height: 2; color: var(--ink); }
.e-hero_line + .e-hero_line { margin-top: var(--space-2); }
.e-hero_caption { margin-top: var(--space-3); font-size: var(--text-meta); color: var(--ink-muted); }
.e-hero.is-writing .mk-ins:not(.is-on),
.e-hero.is-writing .mk-o:not(.is-on),
.e-hero.is-writing .mk-c:not(.is-on) { display: none; }
.e-hero.is-writing .mk-wrap:not(.is-on) { background: none; color: inherit; padding: 0; }
.mk-caret {
  display: inline-block; width: var(--stroke-display); height: 1.15em;
  background: var(--accent); vertical-align: -0.2em; margin: 0 1px;
  animation: mk-blink 0.9s steps(1) infinite;
}
.mk-caret.is-done { display: none; }
@keyframes mk-blink { 50% { opacity: 0; } }

/* --- the headline, at the large display rung ------------------------------ */
.e-headline {
  max-width: calc(64rem + var(--gutter) * 2);
  margin: var(--space-8) auto;
  padding: var(--space-8) var(--gutter);
  border-block: var(--rule-hair) solid var(--rule);
}
.e-headline_lead { font-size: var(--text-lead); line-height: 1.36; color: var(--ink-muted); max-width: 40ch; }
.e-headline_main {
  font-family: var(--font-display);
  font-weight: var(--weight-bold);
  text-transform: uppercase;
  font-size: var(--text-display); /* the site's one display rung; the 96px rung was retired with the restyle */
  line-height: 1;
  letter-spacing: var(--tracking-display);
  margin-top: var(--space-4);
  max-width: 15ch;
  text-wrap: balance;
}
.e-headline_main .t-em { color: var(--accent); }
.e-headline_stake { font-weight: var(--weight-semi); font-size: var(--text-h3); line-height: var(--leading-heading); margin-top: var(--space-6); max-width: 32ch; }
.e-headline_limit { font-size: var(--text-meta); line-height: 1.5; color: var(--ink-muted); margin-top: var(--space-3); max-width: 52ch; }

/* --- a numbered section opening ------------------------------------------- */
.e-section { max-width: var(--measure); margin: var(--space-9) auto var(--space-5); padding-inline: var(--gutter); }
.e-section_num { border-top: var(--rule-section) solid var(--rule); padding-top: var(--space-3); color: var(--accent); }
.e-section .e-section_h { max-width: none; margin: var(--space-3) 0 var(--space-4); padding-inline: 0; }
.e-section_dek { font-size: var(--text-lead); line-height: 1.36; color: var(--ink-muted); }

/* --- step card parts: the kicker, the figure, the fine print --------------
   Qualified by `p.` because the step card's own `p` rule would otherwise
   size a caps label as a sentence. */
.step p.e-step_kicker { font-size: var(--text-label); line-height: 1.55; color: var(--ink-muted); margin-bottom: var(--space-2); }
.step p.e-step_figure {
  font-family: var(--font-display); font-weight: var(--weight-bold); text-transform: uppercase;
  font-size: var(--text-h2); line-height: 1; color: var(--accent); margin-bottom: var(--space-3);
}
.step p.e-step_fine { margin-top: var(--space-3); padding-top: var(--space-2); border-top: var(--rule-hair) solid var(--rule); }

.e-figure_title { margin-bottom: var(--space-3); }

/* --- a test turn, side by side --------------------------------------------- */
.e-turn { position: relative; margin-block: var(--space-8); }
.e-turn_sticky { padding: var(--space-5) var(--gutter) 0; }
.e-turn_stage { width: 100%; max-width: 72rem; margin-inline: auto; }
.e-turn_title { font-family: var(--font-mono); font-size: var(--text-meta); color: var(--ink-muted); }
.e-turn_cols { display: grid; grid-template-columns: 1fr 1fr; gap: var(--space-6); margin-top: var(--space-4); align-items: start; }
.e-turn_col { border-top: var(--rule-section) solid var(--rule); padding-top: var(--space-3); }
.e-turn_label {
  font-family: var(--font-body); font-weight: var(--weight-bold); font-size: var(--text-h3);
  letter-spacing: var(--tracking-caps); text-transform: uppercase; line-height: 1.1; color: var(--ink);
}
.e-turn_col--voice .e-turn_label { color: var(--accent); }
.e-turn_sub {
  font-size: var(--text-label); font-weight: var(--weight-semi); letter-spacing: var(--tracking-caps);
  text-transform: uppercase; color: var(--ink-muted); margin-top: var(--space-3);
}
.e-turn_input { font-family: var(--font-mono); font-size: var(--text-ui); line-height: 1.8; margin-top: var(--space-2); color: var(--ink); }
.e-turn_copy + .e-turn_copy { margin-top: var(--space-5); }
.e-turn_reply { margin-top: var(--space-3); padding-left: var(--space-3); border-left: var(--stroke-display) solid var(--rule); font-size: var(--text-ui); line-height: 1.5; }
.e-turn_reply p + p { margin-top: var(--space-2); }
.e-turn_reply .e-turn_who {
  font-size: var(--text-label); font-weight: var(--weight-semi); letter-spacing: var(--tracking-caps);
  text-transform: uppercase; color: var(--ink-muted); margin-bottom: var(--space-1);
}
.e-turn_verdict { margin-top: var(--space-3); }
.e-turn_chip {
  font-size: var(--text-meta); line-height: 1.4; padding: var(--space-1) var(--space-2);
  border: var(--rule-hair) solid var(--rule); color: var(--ink-muted); margin-top: var(--space-2);
}
.e-turn_chip b { color: var(--ink); font-weight: var(--weight-bold); }
.e-turn_chip.is-win { border: var(--frame-border) solid var(--accent); color: var(--ink); }
/* The verdict lights the column the judge favoured. */
.e-turn_stage[data-stage="3"][data-win="voice"] .e-turn_col--voice,
.e-turn_stage[data-stage="all"][data-win="voice"] .e-turn_col--voice,
.e-turn_stage[data-stage="3"][data-win="plain"] .e-turn_col--plain,
.e-turn_stage[data-stage="all"][data-win="plain"] .e-turn_col--plain { border-top-color: var(--accent); }
.e-turn_legend { display: flex; flex-wrap: wrap; gap: var(--space-1) var(--space-4); margin-top: var(--space-3); }
.e-turn_leg { font-size: var(--text-meta); color: var(--ink-muted); }
.e-turn_leg .jm { font-family: var(--font-mono); margin-right: 0.25em; }
/* Static (no script, reduced motion): the four cards as a list under the stage. */
.e-turn:not([data-live]) .e-turn_steps {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr)); gap: var(--space-4);
  max-width: 72rem; margin: var(--space-5) auto 0; padding-inline: var(--gutter);
}
.e-turn:not([data-live]) .e-turn_steps .step {
  position: static; grid-area: auto; opacity: 1; transform: none; margin-top: 0; width: auto;
}

/* Live: the stage holds still, one layer per step card — the inputs, then
   Sonnet 5's replies, then Opus 5.5's, then the verdict. The cards take a
   reserved column on the left, so none ever covers the words it describes.

   THE STAGE NEVER CHANGES HEIGHT BETWEEN STEPS. Each input's replies and
   verdict share one grid cell (`.e-turn_swap`, every layer on grid-area 1/1),
   so the cell is always as tall as its tallest layer and a step change is a
   cross-fade in place. They used to be display:none until their step: the
   stage's height changed at every step, the centred sticky box re-centred,
   and the words jumped by up to 132px at 1280 (tools/check-turn-scroll.mjs,
   2026-09-24). A hidden layer is opacity 0 AND visibility hidden, so it is
   not read out; visibility waits for the fade-out before it flips. */
.e-turn[data-live] { height: var(--h, 440vh); }
.e-turn[data-live] .e-turn_sticky {
  position: sticky; top: 72px; height: calc(100vh - 72px);
  display: flex; align-items: center; padding: var(--space-5) var(--gutter);
}
.e-turn[data-live] .e-turn_stage { padding-left: calc(22rem + var(--space-7)); }
.e-turn[data-live] .e-turn_stage:not([data-stage="all"]) .e-turn_swap { display: grid; grid-template-columns: minmax(0, 1fr); }
.e-turn[data-live] .e-turn_swap > .e-turn_input { grid-area: 1 / 1; }
.e-turn[data-live] .e-turn_swap > .e-turn_reply,
.e-turn[data-live] .e-turn_swap > .e-turn_verdict { grid-area: 2 / 1; }
.e-turn[data-live] .e-turn_reply,
.e-turn[data-live] .e-turn_verdict {
  opacity: 0; visibility: hidden; transform: translateY(8px);
  transition: opacity var(--dur-base) var(--ease), transform var(--dur-base) var(--ease), visibility 0s linear var(--dur-base);
}
.e-turn[data-live] .e-turn_stage[data-stage="1"] .e-turn_reply[data-model="1"],
.e-turn[data-live] .e-turn_stage[data-stage="2"] .e-turn_reply[data-model="2"],
.e-turn[data-live] .e-turn_stage[data-stage="3"] .e-turn_verdict,
.e-turn[data-live] .e-turn_stage[data-stage="all"] .e-turn_reply,
.e-turn[data-live] .e-turn_stage[data-stage="all"] .e-turn_verdict {
  opacity: 1; visibility: visible; transform: none;
  transition: opacity var(--reveal-dur) var(--ease), transform var(--reveal-dur) var(--ease), visibility 0s;
}
.e-turn[data-live] .e-turn_col { transition: border-color var(--reveal-dur) var(--ease); }
.e-turn[data-live] .e-turn_steps {
  position: absolute; inset: 0; display: grid;
  max-width: calc(72rem + var(--gutter) * 2); margin-inline: auto;
  padding-inline: var(--gutter); pointer-events: none;
}
.e-turn[data-live] .e-turn_steps .step {
  justify-self: start; width: min(22rem, 100%);
  margin-top: calc(72px + var(--space-8)); top: calc(72px + var(--space-8));
}

@media (max-width: 899px) {
  .e-turn_cols { grid-template-columns: 1fr; gap: var(--space-4); }
  .e-turn_label { font-size: var(--text-lead); }
  .e-turn_input, .e-turn_reply { font-size: var(--text-meta); }
  .e-turn_input { line-height: 1.6; }
  .e-turn[data-live] .e-turn_sticky { align-items: flex-start; padding: var(--space-4) var(--gutter) 0; }
  .e-turn[data-live] .e-turn_stage { padding-left: 0; }
  /* A phone shows one layer at a time — the inputs, or a model's replies, or
     the verdict — in the same cell, so each fits above the card: with the
     inputs kept, two of the five stages ran 70–90px past a 390x844 screen.
     Every layer starts at the same line so the cross-fade lands in place. */
  .e-turn[data-live] .e-turn_swap > .e-turn_input,
  .e-turn[data-live] .e-turn_swap > .e-turn_reply,
  .e-turn[data-live] .e-turn_swap > .e-turn_verdict { grid-area: 1 / 1; margin-top: var(--space-2); }
  .e-turn[data-live] .e-turn_stage:not([data-stage="0"]):not([data-stage="all"]) .e-turn_input {
    opacity: 0; visibility: hidden; transform: translateY(8px);
    transition: opacity var(--dur-base) var(--ease), transform var(--dur-base) var(--ease), visibility 0s linear var(--dur-base);
  }
  .e-turn[data-live] .e-turn_input {
    transition: opacity var(--reveal-dur) var(--ease), transform var(--reveal-dur) var(--ease), visibility 0s;
  }
  /* The cards PASS OVER the stage rather than pinning to its foot. A pinned
     card is a fixed third of the screen the stage can never use, and a pair's
     three inputs do not fit in the rest; a card that travels leaves every
     line of the stage in view at some point of its own step. Card i starts
     just under the fold as step i begins, and has crossed the screen by the
     time step i ends. */
  .e-turn[data-live] .e-turn_steps { display: block; }
  .e-turn[data-live] .e-turn_steps .step {
    position: absolute; left: var(--gutter); right: var(--gutter); width: auto;
    top: calc((var(--h) - 100vh) * var(--i) / var(--n) + 90vh);
    margin-top: 0; opacity: 1; transform: none;
  }
  .e-hero_line { font-size: var(--text-ui); }
}

/* --- a test turn in full, in the receipts --------------------------------- */
.e-log {
  margin-block: var(--space-4);
  padding: var(--space-4) var(--space-5);
  background: var(--lift);
  border: var(--rule-hair) solid var(--rule);
}
.e-log_title { font-family: var(--font-mono); font-size: var(--text-meta); color: var(--ink-muted); margin-bottom: var(--space-3); }
.e-log_body > * + * { margin-top: var(--space-2); }
.e-log_body p, .e-log_body li { font-size: var(--text-meta); line-height: 1.55; }
.e-log_body .e-log_head { font-weight: var(--weight-semi); margin-top: var(--space-4); }
.e-log_lead { font-weight: var(--weight-semi); }
.e-log_marked { font-family: var(--font-mono); }
.e-log_body ul li { position: relative; padding-left: 1.5em; }
.e-log_body ul li::before { content: "\2014"; position: absolute; left: 0; top: 0; color: var(--ink-muted); }
.e-log_body ol { counter-reset: n; }
.e-log_body ol li { counter-increment: n; position: relative; padding-left: 1.9em; }
.e-log_body 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;
}
.e-log_body li + li { margin-top: var(--space-1); }
.e-log figcaption { margin-top: var(--space-3); font-size: var(--text-meta); color: var(--ink-muted); }

/* --- the receipts' long form, inside the same panel as the table -----------
   A post with a long appendix carries dozens of receipts, and `.c-table`'s
   680px floor is wider than the panel it sits in, so the Source column —
   the one a reader opened the panel for — scrolled out of view. The long
   form fixes the four columns to the panel instead, at the meta rung. */
.e-receipts--long .c-table { min-width: 0; table-layout: fixed; }
.e-receipts--long .c-table td,
.e-receipts--long .c-table th[scope="row"] { font-size: var(--text-meta); line-height: 1.5; }
.e-receipts--long .c-table th[scope="row"] { overflow-wrap: anywhere; }
.e-receipts--long .c-table .pill { white-space: nowrap; }
.e-receipts--long .c-table thead th:nth-child(1) { width: 25%; }
.e-receipts--long .c-table thead th:nth-child(2) { width: 20%; }
.e-receipts--long .c-table thead th:nth-child(3) { width: 34%; }
.e-receipts--long .c-table thead th:nth-child(4) { width: 21%; }
@media (max-width: 767px) { .e-receipts--long .c-table { min-width: 34rem; } }

.e-appendix { padding: 0 var(--space-6) var(--space-6); }
.e-appendix > .e-prose,
.e-appendix > .e-h3,
.e-appendix > .e-figure { max-width: none; padding-inline: 0; }
.e-appendix > .e-h3 { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2) var(--space-3); margin: var(--space-7) 0 var(--space-3); font-size: var(--text-lead); }
.e-appendix .e-prose p { font-size: var(--text-ui); margin-bottom: var(--space-4); }
@media (max-width: 767px) {
  .e-appendix { padding: 0 var(--space-4) var(--space-5); }
  .e-log { padding: var(--space-3) var(--space-4); }
}

/* --- the reader's guess ---------------------------------------------------- */
.e-guess {
  max-width: var(--measure); margin: var(--space-9) auto 0;
  padding: var(--space-6) var(--gutter); border-block: var(--rule-section) solid var(--rule);
}
.e-guess .e-guess_q {
  font-family: var(--font-body); font-weight: var(--weight-semi); text-transform: none;
  letter-spacing: 0; font-size: var(--text-h2); line-height: var(--leading-heading);
}
.e-guess_field { display: flex; align-items: center; gap: var(--space-4); margin-top: var(--space-5); }
.e-guess_field input {
  font-family: var(--font-mono); font-size: var(--text-lead); width: 6rem;
  padding: var(--space-2) var(--space-3); border: var(--frame-border) solid var(--rule);
  border-radius: 0; background: var(--lift); color: var(--ink);
}
.e-guess_answer { margin-top: var(--space-5); font-size: var(--text-body); line-height: var(--leading-body); min-height: 1.67em; }
.e-guess_answer b { color: var(--accent); font-weight: var(--weight-bold); }

@media (prefers-reduced-motion: reduce) {
  .mk-caret { display: none; }
}

/* ==========================================================================
   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 a {
  font-family: inherit;
  font-size: inherit;
  color: inherit;
  text-transform: none;
  letter-spacing: 0;
  overflow-wrap: anywhere;
}
/* The URL and the edition id keep the foot's size, colour, case and tracking
   but take the mono: every code is Plex (§7.13). */
.figure__foot code {
  font-family: var(--font-mono);
  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-tint);
  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.
     Square (§5.3, §7.13): a snippet is not one of the tinted panels the 5px
     radius is kept for. */
  background: var(--row-tint);
  border: var(--frame-border) solid var(--rule);
  border-radius: 0;
  /* 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));
  }
  /* The header is NOT capped to the article here. It was (at the article's
     1299px, to line the mark up with the column and the actions with the
     rail), but the drawn nav needs 1328px, so inside 1299 the bar never fit at
     any width: the chevrons were squeezed to 1.5-7px on all 737 answer pages.
     The header keeps the width it has on every other page. */
  .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: var(--tracking-caps); 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
   the blue tint and the disclosure circle the action blue — two steps of one
   hue, so the two read as one family (§4.2). */
.faq { display: flex; flex-direction: column; row-gap: var(--space-3); }
.faq details { background: var(--blue-tint); border-radius: var(--radius-tint); }
.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. Its type on the action blue is 7.87:1
   in light and 8.19:1 in dark; WHITE on it is 2.67:1 and fails AA, which is
   why the glyph is never white. */
.faq summary::after {
  content: "+";
  flex: 0 0 auto;
  display: inline-flex; align-items: center; justify-content: center;
  width: 30px; height: 30px;
  background: var(--blue-action); color: var(--on-blue-action);
  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); }
/* ON A TINT THE LINK RULE IS THE INK (DESIGN_SYSTEM.md §4.6, §7.5). The FAQ
   row and the accordion row are the blue tint, and green on the
   dark tint is 2.47:1 — a link rule the reader cannot see. The ink is 15.39:1
   and 4.55:1 there. Hover keeps the ink too: the deep green is 3.07:1 on the
   dark tint, under what a sentence's own words need. */
.faq details a, .c-acc_item a,
.faq details a:hover, .c-acc_item a:hover { color: var(--ink); border-bottom-color: var(--ink); }
/* The essay's receipts panel is the same blue tint, and its source links
   carried no mark at all — a link told apart by nothing. They take the
   reference link's 1px rule (§7.5), in the ink because they sit on a tint. The
   reading band's own links, should it carry one beside its button, take the
   band's ink at the body link's 2px. */
.e-receipts a { color: var(--ink); border-bottom: var(--rule-hair) solid var(--ink); }
.cta-band a:not(.btn) { color: var(--on-field); border-bottom: var(--stroke-display) solid var(--on-field); }

/* ---------------------------------------------------------- the reading band

   The article's one editorial band (`.cta-band`, §7.10), at the top of the
   reading column: the blue tint, the column's full width, radius 0 — a band
   and not a card. Ink on it is 15.39:1 in light and 4.55:1 in dark.
   `color` is declared ON THE BAND, not only on the title, so the paragraph,
   any link and any small print take the band's type rather than inheriting
   the article's, and the two ranks cannot disagree. The button's frame and
   offset on the band follow the tint-band rule under Buttons. */
.article__col .cta-band {
  background: var(--blue-tint);
  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); }

/* ---------------------------------------------------------------- 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.29:1), never --ink-faint (4.70:1). */
.note {
  background: var(--row-tint); border-radius: 0;
  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 — 16.67:1 in light,
   12.80:1 in dark. 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.61:1 and paper on ink at 19.95:1 (§4.3). Recompute these from
   tokens.css when the paper moves; they were carried forward through two
   ground changes once without being recomputed. */
[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;
}
/* The ink slab (§5.1): the ink as a ground with the paper as its type —
   19.95:1 in light, 15.43:1 in dark. */
.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: var(--tracking-caps); 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: 0;
  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 --blue-tint 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 { font-size: var(--text-label); 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
     reading 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 READING BAND'S FOCUS RING is the band's type, the ink: 15.39:1 on the
   light tint and 4.55:1 on the dark one (§12). Its primary is the one primary
   the site has — the inverted primary this block used to paint in dark (a
   dark-paper face with a cream label) retired with the one-blue rule; the
   button's frame and offset on the band are the tint-band rule above. */
.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;
  }
}

/* builders lane */
/* ==========================================================================
   Rules for markup the page builders emit (site/lib/*.mjs). Each class here
   replaces an inline style or a hard-coded colour a builder used to write.
   Owned by the builders lane; nothing above this block is edited from here.
   ========================================================================== */

/* --- The six dimension tones (DESIGN_SYSTEM.md §4.4) ----------------------
   One class per tone, named by site/lib/media.mjs's DIMENSIONS map. A drawing
   reads --w (the wash) and --k (its ink); tokens.css gives both themes. */
.tone-mint   { --w: var(--wash-mint);   --k: var(--ink-mint); }
.tone-lilac  { --w: var(--wash-lilac2); --k: var(--ink-lilac); }
.tone-cyan   { --w: var(--wash-cyan);   --k: var(--ink-cyan); }
.tone-yellow { --w: var(--wash-yellow); --k: var(--ink-yellow); }
.tone-pink   { --w: var(--wash-pink);   --k: var(--ink-pink); }
.tone-teal   { --w: var(--wash-teal);   --k: var(--ink-teal); }

/* The vignette plates are a light island too (DESIGN_SYSTEM.md §4.4, §4.5,
   §8): the light wash as the ground, the light ink on the marks, and the
   white sheets and #e4e6ec bars the plate rule above this block gives them,
   whatever the page's theme. The .tone-<name> classes read the tone's tokens,
   which follow the theme, so the plates join the mock's pin below: in dark,
   every token either drawing reads is its light value. */

/* --- The report mock (sections.mjs reportMock, /get-started) --------------
   A LIGHT ISLAND in both themes (DESIGN_SYSTEM.md §4.4, §4.5, §5.1.6, §8): the
   white sheet, the light dimension pairs, the light status pairs. The rules
   read tokens; in dark, the .c-mock scope below pins every token they read
   to its light value, so the drawing does not invert with the page and only
   its frame (site.css .c-frame) follows the theme. Pinned as literals because
   tokens.css holds one value per theme; each is the :root value, same hex. */
.c-mock .m-sheet  { fill: var(--lift); }
.c-mock .m-chrome { fill: var(--paper); }
.c-mock .m-quiet  { fill: var(--row-tint); }
.c-mock .m-display { fill: var(--ink); font-family: var(--font-display); }
.c-mock .m-muted  { fill: var(--ink-muted); font-family: var(--font-body); }
.c-mock .m-dwash  { fill: var(--w); }
.c-mock .m-dink   { fill: var(--k); font-family: var(--font-body); }
.c-mock .m-glyph  { stroke: var(--lift); }
/* The status marks (§7.8): each chip is a wash carrying its word, so no
   outcome reads louder than another (§19). "Demonstrated" is the status soft
   green (#8bd8b2, emitted by sections.mjs from media.mjs STATUS_GREEN) with
   the ink, 12.56:1; the green ink #067429 on it is 3.55:1 and does not pass. */
.c-mock .m-disc--part { fill: var(--warn); }
.c-mock .m-disc--no   { fill: var(--bad); }
.c-mock .m-chip--part { fill: var(--wash-yellow); }
.c-mock .m-chip--no   { fill: var(--bad-fill); }
.c-mock [class^="m-word--"] { font-family: var(--font-body); }
.c-mock .m-word--ok   { fill: var(--ink); }
.c-mock .m-word--part { fill: var(--ink-yellow); }
.c-mock .m-word--no   { fill: var(--on-bad); }
/* The tally's bars beside the status green's soft bar. */
.c-mock .m-tally--part { fill: var(--highlight); }
.c-mock .m-tally--no   { fill: var(--wash-red); }
:root[data-theme="dark"] .c-mock,
:root[data-theme="dark"] svg.c-plate {
  --lift: #ffffff;  --paper: #f9f9f9;  --row-tint: #e5e5e5;
  --ink: #000000;   --ink-muted: #4b4b4b;
  --warn: #746019;  --highlight: #ffe566;
  --wash-red: #ffc6c6;  --ink-red: #600000;
  --bad: #600000;   --bad-fill: #ffc6c6;  --on-bad: #600000;
  --wash-mint:   #d1f7ea;  --ink-mint:   #067429;
  --wash-lilac2: #e5e2ff;  --ink-lilac:  #4f45d6;
  --wash-cyan:   #ccf4ff;  --ink-cyan:   #003e57;
  --wash-yellow: #fff8d6;  --ink-yellow: #746019;
  --wash-pink:   #fde0f0;  --ink-pink:   #a13aab;
  --wash-teal:   #c3faf5;  --ink-teal:   #0e4343;
}

/* --- Classes that replace the builders' inline styles ---------------------
   Spacing utilities on the scale (§6.1): the builders wrote each of these as
   an inline margin, which no stylesheet can move. */
.u-mt-2 { margin-top: var(--space-2); }
.u-mt-3 { margin-top: var(--space-3); }
.u-mt-4 { margin-top: var(--space-4); }
.u-mt-5 { margin-top: var(--space-5); }
.u-mt-6 { margin-top: var(--space-6); }
.u-mt-7 { margin-top: var(--space-7); }
.u-mb-5 { margin-bottom: var(--space-5); }
.u-mb-7 { margin-bottom: var(--space-7); }
.u-ink    { color: var(--ink); }
.u-muted  { color: var(--ink-muted); }
.u-center { text-align: center; }
.u-measure { max-width: var(--measure); }

/* The reveal stagger (sections.mjs reveal()): 0.08s a step, capped at five. */
[data-reveal-step="1"] { --reveal-delay: 0.08s; }
[data-reveal-step="2"] { --reveal-delay: 0.16s; }
[data-reveal-step="3"] { --reveal-delay: 0.24s; }
[data-reveal-step="4"] { --reveal-delay: 0.32s; }
[data-reveal-step="5"] { --reveal-delay: 0.4s; }

/* Headings inside a long document (the toolshed's rendered markdown, the
   legal prose): the ink, and a step of the scale above. They were 1.4-1.8em
   inline, off the scale. */
.c-doc-h2 { margin-top: var(--space-7); color: var(--ink); }
.c-doc-h3 { margin-top: var(--space-6); color: var(--ink); }

/* The trail separator on the toolshed, benchmark and law pages, and the
   byline's pipe. It was an inline opacity: .45, an unnamed grey; the faint
   ink is the chrome step (§4.3). */
.c-crumbsep { padding-inline: var(--space-2); color: var(--ink-faint); }

/* The calculators' input rows (toolshed.mjs). */
.c-fieldrow {
  display: grid;
  gap: var(--space-4);
  grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr));
  align-items: end;
}

/* The copyable credit line under a toolshed document. */
.c-credit {
  margin-top: var(--space-3);
  max-height: 14rem;
  overflow: auto;
  white-space: pre-wrap;
  word-break: break-word;
  font-size: var(--text-meta);
}

/* One jurisdiction's rows, stacked (laws.mjs). */
.c-lawrows { display: grid; gap: var(--space-8); }

/* The trusted-by strip (sections.mjs strip): a short band of pills. */
.section.c-strip { padding-block: var(--space-7); }
.c-strip_items {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3) var(--space-5);
  justify-content: center;
}

/* end builders lane */

/* ==========================================================================
   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-label);
  font-weight: var(--weight-semi); line-height: 1;
  letter-spacing: var(--tracking-caps); text-transform: uppercase;
  cursor: pointer;
}
/* currentColor, NOT var(--focus): the ring takes the pair the control is
   handed, the same as its border and label, so it reads wherever the pair
   does. site.css adds a paper backing one pixel wider than the ring, because
   the chip floats over the paper and the footer's shade alike (§7.15). */
.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; }
}
