/* ============================================================================
   SAVOY DRUGSTORE — tokens.css
   The single declaration site for every colour, type role, space, container,
   duration and easing on this site.

   BUILD ORDER:  tokens.css → base.css → components.css → sections/*.css

   LAW (DESIGN_SPEC §10.33): a raw hex, rgb() or named colour may appear in
   THIS FILE AND NOWHERE ELSE. Section, component and base files consume
   custom properties only.

   Section numbers in the comments below map 1:1 onto DESIGN_SPEC.md so this
   file can be audited line by line against the specification.
   ========================================================================= */


/* ============================================================================
   THE CASCADE LAYER ORDER — declared once, here, before anything is written.

   base < components < figures < (unlayered section CSS)

   This single line is what makes the build order in §11 real. Without it the
   files load in that order but the CASCADE does not follow it: an unlayered
   declaration outranks every layered one regardless of specificity, so
   base.css's element reset (`p { font-size }`, `button { padding: 0 }`,
   `a { color: inherit }`) silently beat every component rule applied to those
   elements — and the only remedy was an ever-growing hand-maintained
   `revert-layer` patch list, which is a trap that fires every time a new
   component is authored on a <p> or a <button>. (It fired: .foot__legal was
   left off the list and rendered at 17px instead of 10.5px.)

   With the order declared, ordinary specificity governs inside each layer and
   the later layer wins between them. Section files stay UNLAYERED on purpose:
   an unlayered rule beats every layer, which is exactly the precedence §11
   wants for the file a section agent owns.
   ========================================================================= */

@layer base, components, figures;


/* ============================================================================
   §3.1  FONT FACES — three families, three jobs, no fourth voice.
   Files are self-hosted subset WOFF in ../fonts/. No network, no CDN.
   `font-display: block` is decreed by §3.1: this is a display-serif site whose
   tracked micro-labels re-flow badly under a FOUT, so we accept the short
   block period rather than paint fallback metrics.
   lora / poppins / carlito / charter exist in ../fonts/ but are BANNED (§3.1).
   ========================================================================= */

/* Pagella — the voice. All display type, all editorial body. */
@font-face {
  font-family: "Pagella";
  src: url("../fonts/pagella-regular.woff") format("woff");
  font-weight: 400;
  font-style: normal;
  font-display: block;
}
@font-face {
  font-family: "Pagella";
  src: url("../fonts/pagella-italic.woff") format("woff");
  font-weight: 400;
  font-style: italic;
  font-display: block;
}
@font-face {
  /* Loaded for the wordmark and figure lockups only. §3.3 bans bold running copy. */
  font-family: "Pagella";
  src: url("../fonts/pagella-bold.woff") format("woff");
  font-weight: 700;
  font-style: normal;
  font-display: block;
}

/* Heros — the system. Form UI, nav fallback, button fallback. */
@font-face {
  font-family: "Heros";
  src: url("../fonts/heros-regular.woff") format("woff");
  font-weight: 400;
  font-style: normal;
  font-display: block;
}
@font-face {
  font-family: "Heros";
  src: url("../fonts/heros-bold.woff") format("woff");
  font-weight: 700;
  font-style: normal;
  font-display: block;
}

/* HerosC (condensed) — every tracked micro-label, every chart unit. */
@font-face {
  font-family: "HerosC";
  src: url("../fonts/herosc-regular.woff") format("woff");
  font-weight: 400;
  font-style: normal;
  font-display: block;
}
@font-face {
  font-family: "HerosC";
  src: url("../fonts/herosc-bold.woff") format("woff");
  font-weight: 700;
  font-style: normal;
  font-display: block;
}


:root {

  /* ==========================================================================
     §2.1  LIGHT SURFACE SYSTEM — the primary ground.
     Exactly four steps. Two adjacent sections may never share a step (§4.5.2).
     ======================================================================= */

  --paper:        #F7F4EE;   /* hsl(40 36% 95%)  default page ground            */
  --paper-warm:   #FBF9F5;   /* hsl(40 43% 97%)  one step forward; also on-dark */
  --paper-deep:   #EFEADE;   /* hsl(42 35% 90%)  one step back                  */
  --paper-edge:   #E6E0D2;   /* hsl(42 29% 86%)  field fill — never a ground    */


  /* ==========================================================================
     §2.2  INK — type on light.

     CONTRAST FIGURES ARE MEASURED, NOT QUOTED. Every ratio in this file is the
     WCAG 2.x relative-luminance computation against the stated ground, to two
     decimals, and the same two-decimal convention is printed on the specimen
     sheet's contrast register. Where a figure here differs from the one in
     DESIGN_SPEC §2.2/§2.3 (which rounds to one decimal and is 0.2–3.0 out on
     six pairs), the measurement governs: a house of record may not print two
     different numbers for one pair. No pair changed its PASS/FAIL standing.
     ======================================================================= */

  --ink:          #15171C;   /* 16.33:1  display, headings, structural rules      */
  --ink-soft:     #4A4D56;   /*  7.69:1  editorial body                           */
  --ink-mute:     #63656D;   /*  5.29:1  THE FLOOR FOR ANY TEXT — micro, captions */
  --ink-ghost:    #8B8D95;   /*  3.02:1  NON-TEXT ONLY — disabled, decorative     */


  /* ==========================================================================
     §2.3  DARK SURFACE SYSTEM — immersive bands (E3).
     ======================================================================= */

  --night:        #0A1226;   /* hsl(223 59% 9%)   band base / gradient bottom    */
  --night-2:      #0E1730;   /* hsl(224 55% 12%)  band mid                       */
  --night-3:      #141F3E;   /* hsl(224 51% 16%)  top-centre radial light source */
  --night-3-fade: rgba(20, 31, 62, 0);  /* --night-3 at zero alpha: the terminus
                                           stop of the E3 radial. Declared so the
                                           gradient can be composed outside this
                                           file without a raw rgb().             */

  --on-night:     #F2F1EC;              /* 16.47:1 primary text on dark          */
  --on-night-70:  rgba(242, 241, 236, .70); /*  8.38:1 body copy on dark         */
  --on-night-52:  rgba(242, 241, 236, .52); /*  5.11:1 micro — floor on dark     */


  /* ==========================================================================
     §2.4  IDENTITY — royal blue. The brand's original --primary, unchanged.
     Rules: one full-bleed royal field per page; never text on a dark ground
     (use --beam); never a gradient into any hue but --royal-deep.
     ======================================================================= */

  --royal:        #1147BB;   /* hsl(221 83% 40%) 7.24:1 on paper — the anchor    */
  --royal-deep:   #0A2A72;   /* hsl(221 84% 24%)  royal band gradient bottom     */
  --royal-lit:    #2E6BE0;   /* hsl(219 74% 53%)  luminous STROKE on dark — not text */
  --beam:         #5E92FF;   /* hsl(219 100% 68%) 6.24:1 on night — the only blue text on dark */
  --royal-tint:   #E7ECF9;   /* hsl(223 60% 94%)  selection, focus wash, chart track */
  --royal-wash:   rgba(255, 255, 255, .12); /* the royal field's top-left light (§8.9) */
  --royal-wash-0: rgba(255, 255, 255, 0);   /* …its terminus stop                     */
  --royal-deep-0: rgba(10, 42, 114, 0);     /* --royal-deep at zero alpha, §8.9 sweep  */


  /* ==========================================================================
     §2.5  ACCENT — champagne (strict).
     Max three champagne elements per section (a set counts as one). Never a
     gradient, glow or bevel. Never a fill larger than 96×96px. Never touches
     royal directly: ≥16px apart or separated by an ink hairline.

     §2.5.5 — CHAMPAGNE DOES NOT LAND ON THE ROYAL FIELD AT ALL. As text it
     measures 4.49:1 there (under the 4.5 floor); as a hairline it measures
     2.45:1 (under the 3:1 graphical floor); and §2.5.4's separation rule
     cannot be honoured on a ground that IS royal. base.css therefore re-points
     BOTH champagne aliases — text and line — to the --on-night ramp on a royal
     ground, so no section, component or figure can put gold on royal by
     accident. On night nothing changes: the decreed tones are correct there.
     ======================================================================= */

  --champagne:      #B9954A;   /* hsl(41 44% 51%) 2.56:1 — NEVER text on light   */
  --champagne-ink:  #7A5E1E;   /* hsl(42 61% 30%) 5.55:1 — the only champagne text on light */
  --champagne-lit:  #D9C08A;   /* hsl(41 51% 70%) 10.52:1 on night               */
  --hair-gold:      rgba(185, 149, 74, .42); /* decorative hairline on paper     */

  /* THERE IS NO FLAT CHAMPAGNE FILL TOKEN, AT ANY ALPHA.
     A `--champagne-40` used to exist so the Gantt's upper-bound extension could
     be stated as a flat wash below 820px, on the argument that a 2px/4px hatch
     inside a 10px box shows one stroke. That traded away the ONE semantic mark
     §7.1 gives the art language — "Hatch … used exclusively for extended /
     upper-bound quantities" — for a beige rectangle, and it did it at exactly
     the width where the chart has the least other information to carry the
     distinction. It also painted a colour the system does not contain
     (rgb(224,209,177) = champagne at 42% over paper), so the sheet's own colour
     register could not account for the largest gold area on the page.
     The hatch is now drawn in OPAQUE --champagne at every width (figures.css
     §7), where one stroke against the paper gap is unmistakably a hatch. */


  /* ==========================================================================
     §2.6  STRUCTURE — borders and hairlines.
     All rules are 1px at 1x. The ONLY 2px rule on the site is the 56px opener
     under a page H1 (--rule-ink).
     ======================================================================= */

  --rule:             rgba(21, 23, 28, .16);    /* standard hairline on paper    */
  --rule-soft:        rgba(21, 23, 28, .09);    /* row separator, secondary      */

  /* THE INTERACTIVE BOUNDARY IS NOT THE DECORATIVE HAIRLINE (§10.42).
     --rule was doing both jobs: a table's row separator AND the only visible
     edge of the secondary CTA, every text input, the select and the textarea.
     Measured, .16 alpha is 1.40:1 against paper and 1.40:1 against the field
     fill, where WCAG 2.2 SC 1.4.11 — reproduced as §10.42 — requires 3:1 for a
     UI boundary. A ghost button and a form field are the two controls a
     regulatory site cannot afford to render as an invisible rectangle.
     .55 is the alpha at which the boundary clears 3:1 against EVERY step of the
     paper ramp, measured on the composite, not estimated:
       on --paper 3.76:1 · --paper-warm 3.93:1 · --paper-deep 3.44:1 ·
       --paper-edge 3.14:1 · and 3.44:1 for a field fill on a paper-deep ground.
     Decorative hairlines keep --rule: a row separator is not a control, and
     lifting it would turn every ledger into a grid of dark lines. */
  --rule-control:     rgba(21, 23, 28, .55);    /* button / field / select edge  */
  --rule-ink:         #15171C;                  /* THE structural rule — one per component */
  --rule-night:       rgba(242, 241, 236, .16); /* hairline on dark              */
  --rule-night-soft:  rgba(242, 241, 236, .09); /* row separator on dark         */
  /* The inverse hairline at fixed alpha. base.css re-points --rule-night-strong
     one rung brighter on the royal field (a .42 hairline measures 2.45:1 there,
     under §10.42's 3:1 graphical floor for an interactive boundary), so any
     mark that must keep .42 on EVERY dark ground — the brandmark's rule, which
     is decoration rather than a control boundary — reads this token instead.
     It exists so the two readings are one declared value, not two literals. */
  --rule-inverse:     rgba(242, 241, 236, .42);
  --rule-night-strong:var(--rule-inverse);      /* .btn--ghost-inverse edge (§8.3) */

  --rule-w:           1px;    /* every hairline, everywhere                      */
  --rule-w-page:      2px;    /* the single exception: the page-H1 opener        */
  --rule-l-page:      56px;   /* its width                                       */

  /* §6.4 — the focus-visible ring. Identical on every interactive element. */
  --focus-w:      2px;
  --focus-offset: var(--s1);  /* 4px */


  /* ==========================================================================
     §5 (E4)  FROST — the only two blur surfaces on the site.
     ======================================================================= */

  --frost-light:  rgba(247, 244, 238, .86);  /* nav at rest-stuck               */
  --frost-dark:   rgba(10, 18, 38, .88);     /* mobile menu panel               */


  /* ==========================================================================
     §2.7  SEMANTIC ALIASES.
     ***  SECTION AND COMPONENT CSS USES THESE, NOT THE RAW TOKENS ABOVE.  ***
     base.css re-points the text/line/accent aliases inside dark and royal
     grounds, so a section written against the aliases inverts correctly and
     can never put --royal on a dark surface (§10.37).
     ======================================================================= */

  /* surfaces */
  --sf-page:        var(--paper);
  --sf-raised:      var(--paper-warm);
  --sf-sunken:      var(--paper-deep);
  --sf-field:       var(--paper-edge);

  /* text */
  --tx-display:     var(--ink);
  --tx-body:        var(--ink-soft);
  --tx-micro:       var(--ink-mute);
  --tx-inverse:     var(--paper-warm);

  /* lines */
  --ln-hair:        var(--rule);
  --ln-soft:        var(--rule-soft);
  --ln-structural:  var(--rule-ink);
  /* The boundary of an INTERACTIVE control at rest (§10.42). base.css re-points
     it to the inverse hairline ramp on dark and royal grounds. */
  --ln-control:     var(--rule-control);

  /* accents */
  --ac-identity:    var(--royal);
  --ac-metal:       var(--champagne);
  --ac-metal-text:  var(--champagne-ink);

  /* The italic voice clause carries its own alias because it does NOT follow
     --ac-identity onto dark grounds: §3.4 sends it to --champagne-lit there,
     while --ac-identity becomes --beam. */
  --ac-voice:       var(--royal);

  /* §3.6 — the documentary numeral (`No. 61109`, a licence or file number set
     in Pagella italic inside a value). It is --royal on light and, unlike
     --ac-identity, --champagne-lit on night: the one inversion rule §3.6 gives
     it. Without its own alias the numeral followed --ac-identity to --beam and
     one dark surface applied two contradictory inversion rules three rows
     apart — gold voice clause, blue numeral. On royal it steps to --on-night
     with every other text tier (§2.5.5). */
  --ac-figure:      var(--royal);


  /* ==========================================================================
     §3.1  STACKS — three families, three jobs.
     The scale jump between --f-display (≥16px) and --f-micro (≤12px) IS the
     hierarchy. Nothing is set in the 12–16px band except form UI.
     ======================================================================= */

  --f-display: "Pagella", "Palatino Linotype", Georgia, serif;
  --f-ui:      "Heros", "Helvetica Neue", Arial, sans-serif;
  --f-micro:   "HerosC", "Heros", "Arial Narrow", sans-serif;


  /* ==========================================================================
     §3.2  THE MODULAR SCALE.
     Each role carries a size (--t-*), a leading (--lh-*) and a tracking
     (--tr-*) token. Display sizes are fluid; every micro size is FIXED px and
     is never clamped — clamping breaks the tracking (§3.2).

     THE SCALE IS ACTUALLY MODULAR — AT BOTH ENDS OF ITS RANGE.
     Measured at 1440 the display ramp steps
       d1 115.2 → d2 88 → d3 70.4 → d4 54.4 → h3 42.4 → h3-s 32 → h4 24.8
       ratios  1.31 · 1.25 · 1.29 · 1.28 · 1.33 · 1.29
     — one consistent step, where §3.2's published ceilings gave 1.44 / 1.136 /
     1.294 / 1.283 / 1.710. Two of those were defects, not decisions: d2 and d3
     were 9.6px apart (set one above the other they read as one size, so the
     page-H1 tier and the major-H2 tier carried no hierarchy), and there was a
     1.71 hole between h3 and h4 with nothing in it. Two ceilings are therefore
     re-cut from the spec's table — d2's, and one new tier — and every other
     ceiling is unchanged.

     THE MINIMUMS ARE NOW CUT ON THE SAME RATIO. They were not: every ceiling
     was re-derived while every clamp minimum was inherited from §3.2's table
     untouched, so the ramp was only ever designed at the top of its range. At
     390 all seven clamps fall to their minimum, and the inherited set stepped
       48.8 · 42.4 · 38.4 · 32.8 · 26.4 · 23.2 · 19.84px
       ratios 1.151 · 1.104 · 1.171 · 1.242 · 1.138 · 1.169
     — a 2.46× total range in which the hero display and an inner-page H1 differ
     by 14% and the H1 and the major H2 by 10.4%: below the step a reader can
     register. A
     scale that is a scale only at one width is not a scale.

     Every minimum below is therefore re-derived on ONE ratio of 1.20, anchored
     on --t-h4 (1.24rem, the one tier whose minimum the spec and the ramp agree
     on, and the tier that sits on body copy so it cannot move):
       h4 1.24 · h3-s 1.49 · h3 1.79 · d4 2.14 · d3 2.57 · d2 3.09 · d1 3.70rem
     = 19.84 / 23.84 / 28.64 / 34.24 / 41.12 / 49.44 / 59.2px at 390, a 2.99×
     range on a visible 20% step. Ceilings are untouched, so nothing at 1440
     moves; only d2's vw coefficient is restored to §3.2's 6.6 so the ramp stays
     monotonic through the middle of its range as well (at 900 the d2:d3 step is
     1.22 rather than 1.13).
     ======================================================================= */

  /* 01 — Hero display L1/L2 · Pagella 400 */
  --t-d1:       clamp(3.70rem, 9.8vw, 7.2rem);
  /* §3.2 publishes .86, which is a collision by construction: Pagella's
     cap-height (.692em) plus its descender depth (.215em) is .907em, so ANY
     two-line setting of d1 overlaps — measured at 390 the "y" of "Regulatory"
     bottomed out 4px below the cap line of "Excellence".
     BUT .94 WAS TOO FAR THE OTHER WAY, AND IT SHOWS IN THE HERO: measured
     baseline-to-baseline on the two-line lockup it gave 108px on a 115.2px
     face, which opens a carved display block into two stacked lines and loses
     exactly the quality .86 exists to buy. .88 is the compromise the geometry
     actually allows — .907em of glyph extent plus a hair, so descender and cap
     clear by 0.5px at 1440 — and the display block reads as one mass again.
     The remaining .02em against the decree is bought back optically, not by
     leading: `.t-d1`/`h1` carry `margin-block: -.04em` in base.css §2, so the
     lockup's outer boxes sit where a .86 setting would have put them. */
  --lh-d1:      .88;
  --tr-d1:      -.036em;

  /* 02 — Page H1, inner pages · Pagella 400.
     Ceiling re-cut from 5.0rem to 5.5rem: 88px is 1.25 × d3, which restores the
     step between the page-H1 tier and the major-H2 tier. */
  --t-d2:       clamp(3.09rem, 6.6vw, 5.5rem);
  --lh-d2:      .92;
  --tr-d2:      -.032em;

  /* 03 — Section H2, major · Pagella 400 */
  --t-d3:       clamp(2.57rem, 5.4vw, 4.4rem);
  --lh-d3:      .95;
  --tr-d3:      -.030em;

  /* 04 — Section H2, minor · Pagella 400 */
  --t-d4:       clamp(2.14rem, 4.2vw, 3.4rem);
  --lh-d4:      1.0;
  --tr-d4:      -.028em;

  /* 05 — Row / step title · Pagella 400 */
  --t-h3:       clamp(1.79rem, 3.2vw, 2.65rem);
  --lh-h3:      1.04;
  --tr-h3:      -.026em;

  /* 06 — Row title, narrow column · Pagella 400.
     THE TIER THAT CLOSES THE 1.71 HOLE between --t-h3 (42.4px) and --t-h4
     (24.8px). A row title set in a 4.1fr column needs a display size; a
     sub-head sitting on body copy needs a text size; nothing in the published
     scale served the space between them, so sections were reaching for h3 and
     wrapping it or reaching for h4 and losing the hierarchy. */
  --t-h3-s:     clamp(1.49rem, 2.4vw, 2.0rem);
  --lh-h3-s:    1.12;
  --tr-h3-s:    -.020em;

  /* 07 — Sub-head / card title · Pagella 400 */
  --t-h4:       clamp(1.24rem, 1.9vw, 1.55rem);
  --lh-h4:      1.2;
  --tr-h4:      -.014em;

  /* 08 — Italic voice clause · Pagella ITALIC in --royal (--champagne-lit on dark).
     THERE IS NO ABSOLUTE SIZE TOKEN FOR THE VOICE CLAUSE, BY DECREE OF §3.4.
     "Size relationship is fixed: voice-size ≤ 0.46 × roman-size … at any larger
     ratio this is the 'two serifs in one headline' defect and it is a
     rejection." An absolute clamp cannot satisfy a RELATIONSHIP: the published
     clamp(1.55rem, 4.3vw, 3.15rem) held 0.46 against a --t-d1 roman and against
     nothing else — measured 0.72 against d3, 0.93 against d4, 0.65 at 390.
     So the clause is now sized as a fraction of the roman line it modifies:
     base.css publishes --roman-fs from every display role and `.voice` reads
     `calc(var(--roman-fs) * var(--ratio-voice))`. The ratio below is the only
     number in the system that governs it, and it is a ceiling, not a preference. */
  --lh-voice:   1.02;
  --tr-voice:   -.016em;
  /* The fallback roman, used only if a `.voice` is authored with no display
     line above it in the same block. Sized against the major H2. */
  --roman-fs:   var(--t-d3);

  /* 09 — Big figure (18–35) · Pagella 400, tabular lining */
  --t-fig:      clamp(3.1rem, 8.4vw, 6.6rem);
  --lh-fig:     .84;
  --tr-fig:     -.042em;

  /* ── THE TEXT TIERS: THREE, NOT FOUR ──────────────────────────────────────
     The display ramp above is a real 1.28–1.31 modular scale. The bottom of the
     system was not a scale at all: lede 20.8 → body 18.08 → body-s 16 → ui 15
     put FOUR declared tiers inside a 5.8px band, at ratios of 1.15, 1.13 and
     1.07. In the specimen you could not tell a lede paragraph from a body
     paragraph from an editorial-small paragraph without reading the label
     beside it — three names for one size, plus rounding error. §3.1 also
     promises "nothing is set in the 12–16px band except form UI", and
     --t-body-s (16px at its ceiling, 15.04px at 390) sat in it, one pixel from
     --t-ui, in a different typeface.

     So --t-body-s IS DELETED and its uses are reassigned to --t-body. What is
     left is three tiers with real contrast and one job each:
       --t-lede  23.0px  Pagella   the opening paragraph        (lede/body 1.27)
       --t-body  18.1px  Pagella   all running editorial copy
       --t-ui    15.0px  Heros     form controls — the only thing in 12–16px
     The lede's clamp minimum is re-cut on the same 1.27 ratio against the
     body's own minimum (1.27 × 1.00rem = 1.27rem), so the step holds at 390 as
     well as at 1440 — the same discipline §3.2's display ramp is held to. */

  /* 09 — Editorial lede · Pagella 400.
     THE MINIMUM IS A MEASURE DECISION, NOT A SCALE DECISION. At 1.27rem the
     lede set 20.3px into the 348px column a 390 viewport offers, which is 36
     characters per line against §3.3's 46ch floor: eight short lines and a
     stepped rag on the opening paragraph of every page. §3.3's floor and §3.2's
     ramp cannot both be satisfied at 390 — 46ch in 348px needs 15.9px, and
     §3.1 keeps every editorial tier at or above 16px — so the tier that yields
     is the one whose job is the measure. 1.09rem (17.4px, §3.2's own published
     minimum, restored) sets 42ch and closes two thirds of the gap; below 560px
     base.css §3 takes the last step and merges the lede into --t-body, where
     the opening paragraph is told from running copy by its drop cap and its
     leading rather than by 1.4px of size. The 1.27 ratio against body is
     unchanged from 900px up, which is every width the ramp is read at. */
  --t-lede:     clamp(1.09rem, 1.8vw, 1.44rem);
  --lh-lede:    1.62;
  /* Below 560px the lede takes the body SIZE (see base.css §3) — so the tier
     has to survive in the one axis left to it. At 1.62 it inherited a leading
     TIGHTER than body's 1.66, i.e. the "opening paragraph" set closer than the
     running copy it opens: the three-tier scale collapsed to one. 1.88 restores
     a visible 3.5px step at 16px and reads as air around an opening. */
  --lh-lede-sm: 1.88;
  --tr-lede:    0;

  /* 10 — Editorial body · Pagella 400. Also the annotation `.note` and every
     `small` — there is no fourth text tier below it. */
  --t-body:     clamp(1.00rem, 1.2vw, 1.13rem);
  --lh-body:    1.66;
  --tr-body:    0;

  /* 11 — Editorial small · THE NAME SURVIVES, THE TIER DOES NOT.
     The tier was deleted for the reason argued above — four declared sizes
     inside a 5.8px band is one size with rounding error — but §8.6 (the
     ledger's caption) and §8.9 (the footer's licence paragraph) both NAME this
     token, and a decreed component naming a token that does not exist resolves
     to `font-size: ;` — i.e. silently to the parent's size, which in the footer
     is the 1rem link tier. So the name is kept as an ALIAS of the tier its uses
     were reassigned to. Nothing in the system resolves to nothing, and a
     section agent following §8.6 to the letter gets the right size. */
  --t-body-s:   var(--t-body);
  --lh-body-s:  var(--lh-body);
  --tr-body-s:  var(--tr-body);

  /* 12 — UI / form text · Heros 400. Fixed. */
  --t-ui:       15px;
  --lh-ui:      1.5;
  --tr-ui:      .002em;

  /* ── THE MICRO RAMP ────────────────────────────────────────────────────────
     A RAMP, NOT FOUR NAMES ON TWO SIZES.
       m1  12px  .26em  HerosC 700  --champagne-ink   eyebrow / section opener
       m2  11px  .20em  HerosC 400  --ink-mute        keys, indices, buttons
       m3  10px  .15em  HerosC 400  --ink-mute        tags, axis ticks, units
       m4   9px  .28em  HerosC 400  --champagne-ink   FIG. captions only

     IT SHIPPED AS 11 / 11 / 10.5 / 10 AND THAT IS NOT A FOUR-TIER SYSTEM.
     A 1px span across four named tiers means m1 and m2 were the same size AND
     the same weight — separated by .06em of tracking, which at 11px is .66px a
     letter — and m3 and m4 were half a pixel apart. Every label on the site
     inherits from this ramp, so a collapsed micro scale is not a detail of the
     specimen sheet: it is the reason a page of eyebrows, keys, tags and
     captions reads as one grey texture instead of as four ranks.

     Respaced to 12 / 11 / 10 / 9 — steps of 1.091 · 1.100 · 1.111, a 3px span,
     and every step above the ~8% threshold at which two label sizes can be told
     apart side by side. The bottom of the ramp is 9px rather than 10 because m4
     is set at .28em: tracked capitals read a size larger than their nominal, and
     9px at .28em measures optically level with 10px at .15em.

     AND m2 DROPS TO HerosC 400, so the eyebrow tier and the key tier are told
     apart by WEIGHT — a difference visible at a glance across a whole page —
     rather than only by .06em of tracking and a hue. That leaves exactly one
     bold micro tier on the site, the eyebrow, which is the tier whose job is to
     open a section. Weights are published as tokens (--fw-mN) so a consumer can
     never quietly disagree with its own tier.

     THE ONE EXCEPTION IS THE BUTTON, and it is declared where it happens
     (components.css §8.3): §3.2 and §8.3 both name the control label at
     HerosC 700, and a control's label is an affordance rather than a caption.
     Every other m2 consumer — ledger keys, field labels, footer column heads,
     row / accordion / chart indices, the stage key — takes --fw-m2.

     TRACKING IS UNCHANGED AND STAYS ON §10.35's PERMITTED SET
     (.15 / .20 / .26 / .28em). The ramp is respaced on size and weight, which
     are the two axes §10.35 does not constrain; inventing a fifth tracking
     value to buy separation would have traded one rejection for another.
     Sizes are FIXED px and never clamped; below 390 they stay put. */

  /* 13 — Micro L1, eyebrow · HerosC 700 UPPERCASE, --champagne-ink. Fixed. */
  --t-m1:       12px;
  --lh-m1:      1.2;
  --tr-m1:      .26em;
  --fw-m1:      700;

  /* 14 — Micro L2, keys, indices & buttons · HerosC 400 UPPERCASE. Fixed. */
  --t-m2:       11px;
  --lh-m2:      1.2;
  --tr-m2:      .20em;
  --fw-m2:      400;

  /* 15 — Micro L3, tags & axis · HerosC 400 UPPERCASE. Fixed. */
  --t-m3:       10px;
  --lh-m3:      1.3;
  --tr-m3:      .15em;
  --fw-m3:      400;

  /* 16 — Micro L4, FIG. caption · HerosC 400 UPPERCASE. Fixed.
     §3.2 fixes this tier at 10px PRECISELY — "micro sizes are fixed px, never
     clamped … or the tracking breaks". It shipped at 9px, which is both off the
     decreed ramp and the smallest type on the sheet; restored. */
  --t-m4:       10px;
  --lh-m4:      1.4;
  --tr-m4:      .28em;
  --fw-m4:      400;

  /* The control-label weight, published so §8.3's one deviation from the ramp
     is a named value rather than a magic 700 inside a button rule. */
  --fw-control: 700;

  /* 17 — The big-figure unit word (§3.6) · Pagella ITALIC --ink, baseline-aligned
          immediately after the numeral. Both values are ratios OF THE NUMERAL.
          The gap is written in the unit word's own em — where 1em is already
          .42 × the numeral — so N numeral-em is N/.42 unit-em.

          §3.6 DECREES .12em AND THE LOCKUP STILL COLLIDES AT IT. Measured on
          `18–35 weeks` at a 101px numeral, the 5's right-curling terminal ends
          at x 329.5 and the italic w's entry stroke begins at x 329.0 — the two
          glyphs overlap by half a pixel and share a 6px column. .12em is a
          SIDEBEARING-TO-SIDEBEARING measurement; the two faces in this lockup
          are a roman lining figure with a flared terminal and an italic with a
          left lean, and neither carries the sidebearing the number assumes.
          .16 numeral-em (.3810 unit-em) is the value at which the measured
          INK-to-INK gap is the 12px the decree is trying to buy. The composition
          is locked and reused on /, /process, /assessment and /404, so the
          correction is made once, here, and never per-placement. */
  --t-fig-unit:       .42em;
  --t-fig-unit-gap:   .3810em;

  /* Locked ratios that govern the two composed lockups. */
  /* §3.4 PUBLISHES .46 AS A CEILING, AND A CEILING IS NOT A SETTING.
     Shipping the ratio AT its ceiling left the device with no headroom under
     any reading of the decree, and the two readings do not agree: measured
     CAP-TO-CAP rather than em-to-em the hero clause came out at 0.475 and the
     dark band's at 0.479, because Pagella's italic carries a taller cap per em
     than its roman. Either measurement is a fair reading of "voice-size ≤ 0.46
     × roman-size", and the one that fails is the one a critic takes with a
     ruler on the render. .44 puts the em ratio 4% inside the ceiling and the
     measured cap ratio at .455 — under it on BOTH readings, at every tier,
     with the clause still unmistakably subordinate. */
  --ratio-voice:      .44;     /* voice-size ≤ 0.46 × roman-size (§3.4). Hard ceiling. */
  --tr-voice-delta:   .004em;  /* voice tracking relative to its roman line (§3.4).    */

  /* §3.4 — the voice clause's stepped indent. */
  --voice-indent-hero:  clamp(4px, 20vw, 392px);
  --voice-indent:       clamp(0px, 6vw, 96px);

  /* §3.7 — THE DROP CAP'S OPTICAL GAP.
     "The cap's right sidebearing must be optically kerned so the gap to the
     following letter matches the paragraph's word-space ±2px." Pagella's E
     carries almost no right sidebearing of its own, so at the spec's declared
     .06em the measured ink gap was 3–5px against a 7–8px word space at 1440:
     four pixels outside the tolerance, with the v of "very" and the c of
     "compliance" visibly touching the cap's arms.
     THE VALUE IS AN EM OF THE PARAGRAPH, NOT OF THE CAP. `initial-letter`
     scales the glyph but leaves the used font-size — and therefore every em on
     ::first-letter — at the paragraph's own size, which is why .06em resolved
     to 1.2px rather than to the 7px it reads as. Measured: .24em = 4.8px of
     added gap at the 20.16px lede, landing the ink gap at 7–9px.
     The 2-line mobile cap is 0.586 × the 3-line cap (3.40em against 5.80em), so
     its sidebearing is 0.586 × as wide and its correction scales with it.
     The cap's LEFT edge needs no correction: Pagella's E has a full serif at
     top and bottom, and both already land on the paragraph's own ink edge
     (measured 74px against 74px at 1440) — a negative margin here would hang
     the serif OUTSIDE the measure, which is a different defect. */
  --cap-gap:      .24em;
  /* The two-line cap's own sidebearing is .586 × the three-line cap's, and the
     TARGET (the paragraph's word space) does not shrink with it — so the
     correction has to grow. See base.css §6. Measured at 390 on the render:
     the 16px mobile lede's own word space measures 4.5–5.0 CSS px (sampled
     across three lines of the rendered paragraph), and .26em = 4.16px of added
     gap lands the cap's ink gap at exactly 5.0px — a 0–0.5px delta against
     §3.7's ±2px. A round-4 critique reported a 7–8px mobile word space and a
     4px gap and asked for .09em more; both halves of that were measured off a
     dpr-2 capture read as CSS pixels. Taking the correction to .38em was tried
     and moved the ink gap to 7.0px, i.e. 2.0–2.5px WIDE of the word space and
     at the far edge of the same tolerance. .26em is the measured answer. */
  --cap-gap-sm:   .26em;

  /* §3.3 — editorial measure. 46–62ch, hard ceiling 64ch. */
  --measure:        62ch;
  --measure-tight:  46ch;
  --measure-max:    64ch;   /* a paragraph wider than this is a rejection */
  /* §3.7 — THE DROP-CAP MEASURE. A three-line initial eats the first three
     lines' opening 2.4 characters, so a drop-capped paragraph set to the same
     62ch as its neighbours reads four characters longer than them on the lines
     that matter most. It is also the one paragraph on the page whose left edge
     is occupied, and the eye reads its right rag against the ledger beside it.
     58ch is inside §3.3's band and gives the measure a deliberate right edge
     rather than letting it fill to the grid line. */
  --measure-drop:   58ch;
  /* §8.5 publishes 50ch for the indexed row's description. That was written
     against a description column narrower than the one §4.4 actually decrees:
     in a 5.2fr track (649px at 1440) a 50ch paragraph leaves 199px of the
     column empty on every row, which stacks down a six-row list into a
     column-height band of blank paper beside the copy. 62ch is the top of
     §3.3's own range (its hard ceiling is 64), it fills the decreed track to
     within 91px, and it is one number rather than two rules fighting. */
  --measure-row:    62ch;   /* §8.5 description, at §3.3's upper measure     */


  /* ==========================================================================
     §4.1  THE LATTICE — base unit 4px. These twelve values are the only
     margins, paddings and gaps that exist. A value off this lattice (or a
     clamp() between two of them) is a rejection (§10.34).
     ======================================================================= */

  --s1:   4px;
  --s2:   8px;
  --s3:   12px;
  --s4:   16px;
  --s5:   24px;
  --s6:   32px;
  --s7:   48px;
  --s8:   64px;
  --s9:   96px;
  --s10:  128px;
  --s11:  160px;
  --s12:  200px;


  /* ==========================================================================
     §4.2  CONTAINERS.
     Above 1680px the shell stays at 1420px. The art-bleed layer may run wider
     but its outermost geometry must terminate ≥40px inside the viewport (§7.2).
     ======================================================================= */

  --shell:        1420px;
  --shell-narrow: 1080px;
  --shell-text:   720px;
  --gutter:       clamp(20px, 4.4vw, 72px);

  --bleed:        min(1680px, 100vw - 80px);   /* the art-bleed layer  */
  --bleed-inset:  40px;                        /* geometry stops here  */


  /* ==========================================================================
     §4.3  GRID + SECTION RHYTHM.
     ======================================================================= */

  --grid-cols:      12;                          /* ≥1080px            */
  --grid-cols-md:   8;                           /* 768–1079px         */
  --grid-cols-sm:   4;                           /* <768px             */
  --grid-gap:       clamp(16px, 2vw, 28px);

  --sec-pad:        clamp(80px, 10vw, 152px);    /* default section    */
  --sec-pad-breath: clamp(56px, 7vw, 96px);      /* a "breath" section */
  --sec-pad-dark:   clamp(96px, 12vw, 200px);    /* a dark band        */

  /* The seven breakpoints. No others exist (§4.3). Media queries cannot read
     custom properties — these are declared for JS (getComputedStyle) and for
     auditability; the literal values are repeated in base.css @media blocks. */
  --bp-390:   390px;
  --bp-560:   560px;
  --bp-768:   768px;
  --bp-900:   900px;
  --bp-1080:  1080px;
  --bp-1360:  1360px;
  --bp-1680:  1680px;

  /* §4.4 — the archetype column templates, so no section re-derives them. */
  --g-ledger:       1.6fr 1fr;             /* A2  LEDGER       */
  /* A3 INDEX — `76px / 4.1fr / 5.2fr`, VERBATIM.
     Capping the third track at the description's measure and giving the
     surplus to the title inverted the archetype: measured at 1440 the title ran
     710px against the description's 481 — 1.48:1, where the decree makes the
     DESCRIPTION the wider of the pair at 0.79:1 — and the longest service name
     in the brief wrapped at 570px, so 316px of the title track was never used
     by any row. The trade was one kind of unfilled column for a worse one, and
     it silently rewrote a published archetype that /services, /compliance and
     /partnerships all build on.
     So the decreed ratio is restored, and the surplus is dealt with where it
     belongs — in the description's own measure (--measure-row below), which
     rises to §3.3's 62ch so the wide track is filled by the copy rather than by
     a hole. At 1440: 76 / 512 / 649 with 28px gaps, description text to 558px,
     91px of rag — inside §4.6 on both axes. */
  --g-index:        var(--col-index) minmax(0, 4.1fr) minmax(0, 5.2fr);
  --g-chart:        4.4fr 7fr;             /* A4  CHART        */
  --g-band:         5fr 7fr;               /* A6  BAND         */
  --g-declaration:  7.6fr 4fr;             /* A7  DECLARATION  */

  /* §4.5.3 — the H2 COLUMN STEP. "No two adjacent sections may start their H2
     at the same grid column. Rotate through col 1, col 2, col 3."
     One grid column plus one gap, expressed against the containing block's own
     width, so the rotation is exact at every viewport instead of being three
     hand-picked indents. base.css re-states it for the 8- and 4-column
     breakpoints (a media query cannot read a custom property) and zeroes it
     below 768, where a stepped H2 would eat the measure. */
  --col-step:   calc((100% - (var(--grid-cols) - 1) * var(--grid-gap)) / var(--grid-cols) + var(--grid-gap));

  --col-index:  76px;   /* A3 / §8.5 index gutter        */
  /* A8 / §8.9 register key column. §4.4 and §8.9 both publish 78px, and the
     longest key the content brief contains — `OFFICE HOURS` at --t-m3 with
     .15em of tracking — measures 92px. At 78 it wrapped to two lines beside a
     one-line value in the footer, and it would wrap again on /contact §2 and
     /assessment, which are given the same column: one wrong number, three
     pages. 96px is the next lattice step (--s9) and clears the longest key with
     four pixels of air, so the register is one measurement everywhere. */
  --col-key:    96px;   /* A8 / §8.9 register key column */
  --col-rail:   56px;   /* A9 / §8.8 accordion rail      */

  /* §8.2 — the nav reserves this height; every scroll-margin-top uses it. */
  --nav-h:        88px;
  --nav-h-stuck:  64px;


  /* ==========================================================================
     §6.1  MOTION — named easings and durations. Four curves, seven durations.
     Nothing on this site animates on an unnamed timing function.
     ======================================================================= */

  --e-wipe:   cubic-bezier(.76, 0, .24, 1);     /* clip-path reveals            */
  --e-out:    cubic-bezier(.16, .84, .32, 1);   /* rises, draws, scroll-linked  */
  --e-ui:     cubic-bezier(.20, .70, .25, 1);   /* hover / focus / press        */
  --e-stamp:  cubic-bezier(.16, 1.10, .30, 1);  /* seals, badges landing        */

  --t-xs:     160ms;
  --t-s:      220ms;
  --t-m:      320ms;
  --t-l:      520ms;
  --t-xl:     900ms;
  --t-wipe:   1150ms;
  --t-draw:   2600ms;


  /* ==========================================================================
     §7.1  ART KIT — stroke weights and stroke colours for the SVG language.
     Declared here (not in figures.js or a section file) because §10.33 forbids
     a raw colour anywhere else. No other stroke weight exists.
     ======================================================================= */

  /* §7.1 Tick: 1px wide, 6 units long, --hair-gold, perpendicular to its axis.
     Off the 4px lattice by decree, so it is named rather than silently rounded
     to --s2. Lives here rather than in figures.css because the kit's tick is
     also drawn outside a figure — the scroll indicator's foot (§6.5). */
  --art-tick:   6px;

  --sw-stem:    1;      /* .stem   */
  --sw-hair:    1;      /* .hair   */
  --sw-accent:  1.25;   /* .accent */
  --sw-live:    1.5;    /* .live   */

  --stroke-stem:        rgba(21, 23, 28, .34);
  --stroke-stem-night:  rgba(242, 241, 236, .34);
  --stroke-hair-night:  rgba(217, 192, 138, .55);
  --stroke-node-night:  rgba(242, 241, 236, .75);
  --ln-leader:          rgba(21, 23, 28, .20);   /* 1px dashed 2 6 leader line */

  /* §5 — the blueprint lattice, drawn ONLY inside declared art bands. */
  --lattice-opacity:  .022;
  --lattice-pitch:    112px;
}
