/* tokens.css V2.23 - 2026-10-03, Nolan. MST inks pinned in the locked street (dark) and pond (light) styles. */
/* tokens.css V2.22 - 2026-10-03, Nolan. MST: --mst-season, --mst-ahead, and light-theme values for the MST tokens. */
/* tokens.css V2.21 - 2026-10-03, Nolan. MST: --info-rgb, --mst-season-rgb. */
/* tokens.css V2.20 - 2026-10-03, Nolan. MST: --mst-record, --mst-untracked, --mst-track, --gold (+ rgb, cup gradient, ink on it). */
/* tokens.css V2.19 - 2026-10-01, Nolan. Centre Ice CI-2: `--ci-teal` (+ rgb, ink on it) and `--ci-label-purple`. */
/* tokens.css V2.18 - 2026-10-01, Nolan. `--avatar-ghost-ring` is one value in every theme. */
/* tokens.css V2.17 - 2026-10-01, Nolan. `--avatar-ghost-ink`: initials on the no-account black disc (readable in both themes). */
/* tokens.css V2.16 - 2026-10-01, Nolan. Walk v45: `--avatar-black` / `--avatar-ghost-ring` (roster avatar rings). */
/* tokens.css V2.15 - 2026-09-30, Nolan. `--glass-btn-tint` / `--glass-btn-blur`: the pinned buttons' glass, 50% heavier (Karl, walk v45). */
/* tokens.css V2.14 - 2026-09-30, Nolan. `--goalie` (blue) and the pinned sheet footer's glass (`--glass-sheet-*`, heavier than the nav). */
/* tokens.css V2.13 - 2026-09-30, Nolan. The glass nav tint 5% darker (.35 → .40): bright green behind it washed out the labels (Karl). */
/* tokens.css V2.12 */
/* V2.12 - 2026-09-29, Nolan. Glass nav tint 25% -> 35% (Karl: about 10% darker). */
/* V2.11 - 2026-09-29, Nolan. `--glass-nav-*`: the glass bottom nav's approved values (Sloane's handoff). */
/* V2.10 - 2026-09-28, Nolan. `--ring-visitor`: the game roster's orange V ring (Sloane's preview). */
/* V2.9 - 2026-09-26, Calder. The dark `--dock-*` surfaces move from neutral grey
   to the approved Messenger blue-black. Nothing else in this file changed. */
/* V2.8 - 2026-09-25. Move the sole launch presentation one controlled step
   darker across shared cards, pills, elevations and tinted active fills. */
/* V2.7 - 2026-09-25. Correct the Open Ice launch palette that was still
   overriding the new blue-black surfaces with legacy greys, and add shared
   filled active-state surfaces for the approved green and purple glows. */
/* V2.6 - 2026-09-25. Adopt the approved preview's blue-black launch surfaces
   instead of neutral grey cards. Dark/Open Ice is the sole launch presentation. */
/* V2.5 - 2026-09-25. Promote the approved Impact display face through the
   existing shared heading and styled-label roles app-wide. Body/interface copy
   remains Public Sans; alternate style overrides remain intact. */
/* V2.4 - 2026-09-25. Add the approved condensed display role used by the
   interactive preview. It requests the real installed Impact face and keeps
   readable interface/body typography on the existing shared roles. */
/* V2.3 - 2026-09-19, Jackie. `S2-43`: --info was set against WHITE and fails AA
   on every greyer light surface it lands on. Both LIGHT values move to #1663B6;
   the two dark values are untouched. Full measurement at the --info line below.
   ⚠️ ENTERED ON KARL'S EXPLICIT INSTRUCTION, NOT ON MY OWN JUDGEMENT.
   `hub.css:3419` names this file as Vera's and needs a palette entry measured
   in both themes and all four styles; it was handed to her at 12:34 with the
   numbers and had not moved seven hours later. Karl ruled it to me directly and
   said so: *"i have not nuded the orchestrator, i made this decision without
   him."* All four styles measured before the edit, recorded below.
   ⚠️ TWO-SURFACE: byte-identical with h365-site/assets/css/tokens.css,
   updated in the same pass.
   
   V2.2 - 2026-09-22, Vera. `SYS-51`: `--chrome-rail-hover` REMOVED. Its one
   consumer was a rule body that never won the cascade, and the value could not
   have shipped — declared in the dark block only, never redeclared for light,
   so it painted #161616 on a light grey rail and measured 2.62:1 against the
   row's ink. Two pinned chrome values in that group now, not three.
   ⚠️ TWO-SURFACE: byte-identical with h365-site/assets/css/tokens.css, updated
   in the same pass. */
/* V2.1 - 2026-09-22, Vera. `SYS-49`. `--head-weight-sub`, the ninth paint slot,
   declared in all five blocks. It was READ by base.css from the day
   `.h-sec-label--sub` landed and was never DECLARED, so the quiet heading level
   always computed 700 — the same value as `--head-weight` on the default, on gm
   and on pond. Measured across all ten theme x style combinations: SIX had zero
   weight contrast. openice and street keep the exact value they already
   computed, so the two styles Karl has walked do not move a pixel.
   ⚠️ TWO-SURFACE: byte-identical with h365-site/assets/css/tokens.css, updated
   in the same pass. */
/* V2.0 - 2026-09-19, Vera. street's `--head-shadow` deleted. The purple offset
   behind every section heading was doing the same job as the new `--head-rule`
   and doing it worse — at 11px a 2px hard shadow fills the counters and reads
   as a misprint. Karl ruled it. One token, no layout, street only. */
/* V1.9 - 2026-09-19, Vera. The EIGHTH paint slot, `--head-rule`, for the ruled
   heading build (A + B, dimmed rule). :root carries `rgba(var(--green-rgb),
   .42)`, so the rule follows each style's own green and flips purple in light
   without a second declaration. street pins it to `--brand-purple` and pond to
   `--pond-rule`; gm and openice take the default. Nothing else moves: the slot
   is read by base.css as a background image, which has no layout cost. */
/* V1.8 — 2026-09-15, Vera. `SYS-04`: the seven PAINT slots, on Karl's ruling
   that a style repaints but never moves anything.
   · --head-font --head-case --head-track --head-weight --head-shadow
     --card-tex --card-edge. Every :root default equals today's rendering, so
     landing them is a zero delta until a style sets one.
   · openice and gm set the three heading slots. street and pond, both LOCKED,
     pin all seven — the lock rule: pin every token you USE.
   · THE EDGE IS AN INSET SHADOW, NEVER A BORDER. A border grows the box and
     shifts what is inside it; an inset shadow paints in the same place and
     moves nothing. That substitution is what makes a 3px style edge free.
   · No colour, radius or shadow token touched. */
/* V1.7 — 2026-09-07, Vera. `SYS-30`: adds `--scrim-identity`, rgba(0,0,0,.62),
   the same value in every theme and every style on purpose — it covers group
   data, not a themed surface. Additive; only `.pg-hero` reads it. */
/* V1.6 — 2026-09-07, Vera. `FND-02` STEPS TWO AND THREE. COMPLETE.
   · The 6 fill sites now read `--purple-solid`, so `--purple` is free to be
     what its name says: the ink and edge token. Light #07A608 -> #067A07.
       ink on --panel  3.25 -> 5.53 · ink on --bg 2.65 -> 4.52 · edge 2.65 -> 4.52
     Two live contrast failures closed, and the fill Karl called too dark is now
     #12D411 rather than #07A608.
   · `--purple-rgb` follows it to 6,122,7.
   · #067A07 is Pond Hockey's green, already in this file and already measured.
   · Dark and both locked styles: zero delta, they never used the light value.
   · ORDER HELD: fills moved first (V1.5 + the 6 call sites), ink second. The
     reverse would have darkened every fill, which is the opposite of the ask. */
/* V1.5 — 2026-09-07, Vera. `FND-02` ROOT CAUSE, STEP ONE OF THREE.
   · Adds `--purple-solid`, the FILL shade. Light #12D411 (dark ink 9.85);
     dark and both locked styles resolve to their own --purple, so those move
     by nothing at all.
   · Measured cause: light `--purple` #07A608 does three jobs and fails two —
     fill 6.10 ok, ink 3.25 FAIL, edge 2.65 FAIL. Same shape as
     --warn/--warn-solid, which this file already solved once.
   · Pinned in street AND pond, per the lock rule: a locked style pins every
     token it USES. Pond is locked LIGHT, so without the pin it would inherit
     the light block's GREEN fill while its own --purple is purple.
   · ADDITIVE ONLY. Nothing reads the new token yet, so today's delta is zero.
     Steps two and three (move the 6 fill call sites, then correct `--purple`
     to #067A07 for its ink job) are handed over and MUST land in that order.
   · No other token touched. */
/* V1.4 — 2026-09-05, Vera. LIGHT LEANS PURPLE ON OPEN ICE RUSH.
   · Karl's ruling: every style with a dark/light switch leans purple in light.
     Open Ice Rush stops overriding `--action` in light, so the page's own
     accent (#6B3AF5 / #FFFFFF) comes back. 5.92 ink, 5.92 vs --panel,
     4.83 vs --bg. Dark unchanged at 13.50.
   · REVERSES the V1.3 decision that the action stays brand green in both
     modes. That was mine; this is his.
   · Closes the "light owes this style a border on the action" debt V1.3 filed
     — the purple fill has its own edge, so that component rule is not needed.
   · GM needs nothing (never overrides --action). Street/Pond are mode-locked.
   · One block added, one comment rewritten. No other token touched. */
/* V1.3 — 2026-09-04, Vera. THE SECOND AXIS AND THE FOCUS RING.
   · `data-style` — the personality axis, with all four launch styles:
     openice (default), gm, street, pond. Two themed, two LOCKED.
   · `--action` / `--on-action` — the primary action as a ROLE. `.h-btn` reads
     these now; "the primary action" and "the semantic accent" were one token
     and had to be able to disagree.
   · `--ring-inner` / `--ring-outer` / `--ring-w` — the two-tone focus ring.

   🚨 THE FIVE NAMES ABOVE EXIST ONLY IN THIS FILE, AND THAT IS WHY THIS STAMP
   MATTERED MORE THAN THE OTHERS. Sol asked three times. Between V1.2 and V1.3
   this file went 31 KB to 43 KB while the number stood still, and `base.css`,
   `components.css` and `ui.js` were all correctly bumped around it — so
   anybody syncing by version takes the three that moved, leaves the one that
   did not, and gets **buttons with no colour and a focus ring that resolves to
   nothing**. A stamp is not documentation; it is the instruction somebody
   else's tooling follows.

   V1.2 — 2026-09-04, Vera. The token layer got everything that was living
   outside it: chart slots, provenance inks, the share card, the messenger dock
   family, the ice tag, scrims and veils, brand rgb triples, the two-tone focus
   ring, five hand-written shadows, the staff tier glow, and the wash triples.
   66 tokens added, 6 theme-scoped selectors deleted. V1.1 -> V1.2 rather than
   V2.0: nothing here CHANGES an existing value except the focus ring, which
   was measured at 1.00:1 and is a fix rather than a redesign. */

/* ============================================================================
   HOCKEY365 — DESIGN TOKENS
   Source of truth: docs/Hub_Prototype.html (v3.2) + the app's Theme Variables CSS.
   THEME MODEL (locked):
     - Chrome (app bar, bottom nav, desktop rail) stays DARK in both themes.
       The wordmark and the raised Find circle keep ONE identity and never restyle.
     - Only the CONTENT area themes.
     - Primary accent flips green (dark) -> purple (light). Every accent surface
       reads --green / --green-rgb / --on-green, so the whole app flips at once.
     - --srcGreen is the LITERAL brand green. Use it only where the mark itself
       must stay green regardless of theme.
   ========================================================================== */

:root,
[data-theme="dark"] {
  /* ---- Brand constants (never themed) ---- */
  --brand-black:    #0A0A0A;   /* Faceoff Black    */
  --brand-green:    #07F708;   /* BreakAway Green  */
  --brand-purple:   #7E51FF;   /* Power Play Purple*/
  --brand-neutral:  #F0F0F0;   /* Neutral Zone     */
  --brand-charcoal: #2B2B2B;   /* Charcoal         */
  --brand-precision:#6D6E71;   /* Precision Grey   */

  /* ---- Content surfaces (DARK = default) ---- */
  --bg:     #080B0D;
  --panel:  #0C1113;
  --panel2: #101619;
  --elev:   #151D21;
  --line:   #293338;
  --line2:  #3A464D;
  /* Walk v45 (Karl): roster avatar rings — a member's plain ring (temporary,
     until the avatar studio) and the no-account player's black disc. */
  --avatar-black: #000000;
  --avatar-ghost-ring: #7b7f82;
  --avatar-ghost-ink: #c4c5c8;

  /* ---- Content ink ---- */
  --ink:   #F0F0F0;
  --muted: #9a9b9e;   /* 7.0:1 on --bg — safe for any text a member must read */
  --faint: #8a8b8e;   /* was #6D6E71 = 3.88:1, below AA. Now ~5.6:1.
                         Still the quietest tier, but no longer unreadable. */

  /* ---- Semantic accent (THE flip) ---- */
  --green:      #07F708;
  --green-rgb:  7,247,8;
  --on-green:   #04120a;
  --green-dim:  rgba(7,247,8,.13);
  --active-green-bg: #0C1810;
  --active-green-ink: #68FF78;
  --srcGreen:   #07F708;

  /* ---- Community / secondary accent (swaps the other way) ---- */
  --purple:     #7E51FF;
  /* `FND-02`: the FILL shade. In dark the accent already works as a fill
     (white on it is 4.66), so this is --purple itself and dark moves by
     zero. The light block is where the two jobs actually separate. */
  --purple-solid: var(--purple);
  --purple-rgb: 126,81,255;
  --on-purple:  #ffffff;
  --purple-dim: rgba(126,81,255,.16);
  --active-purple-bg: #15121F;
  --active-purple-ink: #D0C3FF;

  /* ---- Centre Ice (V11, Karl 2026-09-29) ----
     Teal is Centre Ice's own mark (the audience choice, Open Ice, your
     reaction, Post, promote); the soft purple is the PROGRAM / GROUP type
     label on a cover. Join, Apply and Register stay Hockey365 green. */
  --ci-teal:     #2DD4BF;
  --ci-teal-rgb: 45,212,191;
  --on-ci-teal:  #061312;
  --ci-label-purple: #B79CFF;
  /* MST (Karl's accepted Stat Tracker + Milestones): the fixed trust colours
     that had no token yet (Verified = --green, Adjusted = --warn, Season total
     = --ci-label-purple), the trophy gold and its cup gradient, and the ring
     track. */
  --mst-record:   #A9C1CC;
  --mst-season:   #B79CFF;
  --mst-ahead:    #2DD4BF;
  --info-rgb:     77,163,255;
  --mst-season-rgb: 183,156,255;
  --mst-untracked:#6f7a80;
  --mst-track:    #182226;
  --gold:         #e5b84c;
  --gold-rgb:     229,184,76;
  --gold-hi:      #fbe39a;
  --gold-mid:     #e9bf5c;
  --gold-lo:      #b9862f;
  --on-gold:      #2b1f05;

  /* ---- Status ---- */
  --success: #07F708;
  --warn:    #ffb84d;
  --danger:  #ff6b6b;
  --info:    #4da3ff;
  --q:       #ffcf4d;   /* annotation / hint pin */
  /* The roster's VISITOR ring (Sloane's preview: orange, "V"). A ring, never ink. */
  --ring-visitor: #ff9f43;
  /* THE GLASS BOTTOM NAV (Sloane's handoff, Karl-approved 2026-09-28). Chrome
     black at 25%, an 8px blur with saturate(1.8), a light top edge, a faint
     sheen, and bright inactive ink so labels read over photos. The nav turns
     back to today's solid chrome (`--bg` under `.h-chrome`) at the floor. */
  /* GOALIES ARE BLUE, EVERYWHERE (Karl, walk v37 TM-01): relief from amber,
     which already means warnings, Manage and family. One token, so no page can
     disagree. It is the app's info blue. */
  --goalie: var(--info);
  --glass-nav-tint: rgba(13, 13, 13, .40);
  /* The pinned sheet footer's glass: HEAVIER than the nav's (Karl, walk v37). */
  --glass-sheet-tint: rgba(13, 13, 13, .62);
  --glass-sheet-blur: 22px;   /* .25, +10% (Karl, 2026-09-29), +5% (Karl, 09-30: bright green behind washed out the labels) */
  /* Pinned BUTTONS' own glass (Karl, walk v45: "a heavier glass effect ... by
     50%"): the nav's tint .40 x 1.5 = .60, the sheet blur 22px x 1.5 = 33px. */
  --glass-btn-tint: rgba(13, 13, 13, .60);
  --glass-btn-blur: 33px;
  --glass-nav-edge: rgba(255, 255, 255, .16);
  --glass-nav-hi: rgba(255, 255, 255, .07);
  --glass-nav-sheen-top: rgba(255, 255, 255, .07);
  --glass-nav-sheen-low: rgba(255, 255, 255, .015);
  --glass-nav-ink: #C9CDD0;
  --glass-nav-ink-shadow: rgba(0, 0, 0, .7);
  --glass-nav-icon-shadow: rgba(0, 0, 0, .6);

  /* FILLED status chips need their own pair. --warn is tuned to sit ON the page
     as ink; painting it as a BACKGROUND flips which side needs the contrast,
     and no single fixed text colour passes in both themes. --warn-solid is the
     fill, --on-warn the text that goes on it. An 11px bold count badge is SMALL
     text under WCAG, so this pair has to clear 4.5:1, not 3:1. */
  --warn-solid: #ffb84d;
  --on-warn:    #101114;   /* 11.0:1 */

  /* AND THE SAME PAIR FOR DANGER, which never got one — filed against this seat
     by the Stat Tracker seat on 2026-08-12 and measured in 08-17w:
     `.h-btn--danger` painted #fff on --danger #ff6b6b, which is **2.78:1**.
     That is every destructive button in the app, and small bold text needs 4.5.

     Unlike warn, this pair is deliberately THE SAME IN BOTH THEMES. White on
     #c92a2a is 5.46:1, it clears 3:1 against every surface it sits on in either
     theme (3.12 at worst, on --panel2 in dark), and white-on-red is what a
     destructive button looks like everywhere — so there is no reason for it to
     change identity when the lights go on. --danger itself stays per-theme,
     because it is also used as INK, which is the whole distinction. */
  --danger-solid: #c92a2a;
  --on-danger:    #ffffff;   /* 5.46:1 */

  /* THE TRIPLES, so a tint can be built from the token instead of from a
     remembered hex. `--green-rgb` already existed for exactly this; the status
     colours did not have one, so every status wash in the app was mixed from
     the ACCENT — a high-severity icon sat on a green (light: purple) wash. */
  --danger-rgb: 255,107,107;
  --warn-rgb:   255,184,77;

  /* THE WASH TRIPLES, AND THEY ARE NOT THE SAME JOB AS THE ONES ABOVE.
     `--warn-rgb` / `--danger-rgb` are tuned to the INK: in light they are
     153,77,0 and 201,42,42, the same colours as `--warn` and `--danger`
     themselves. Mixing a WASH from them drags the background toward the ink,
     which spends contrast rather than making it. Measured 2026-09-04:
     substituting them into the app's existing washes fails a 4.5 bar at 5 of
     12 warn combinations and 6 of 12 danger ones, worst 3.16:1.

     So the wash gets its own name and KEEPS ITS IDENTITY IN BOTH THEMES while
     the ink moves — which is not a new idea here, it is exactly what `.ice-tag`
     does and says: "the wash keeps the teal identity in both, and the ink is
     the one that moves". `--green-dim` and `--purple-dim` are the same shape.

     Light deliberately repeats the dark values, because those are the literals
     the app already ships in light and they measure 4.68–5.74 there. Every
     migrated site is therefore byte-identical to what it rendered before; what
     changes is only that a new style can now reach them. */
  --warn-wash-rgb:   255,184,77;
  --danger-wash-rgb: 255,107,107;

  /* THE SELECTION CHIP'S SCRIM. `.h-co__selacts button` said
     `color: var(--on-purple)` on a scrim over a GREEN chip; in dark that token
     is white and looked right, in light it flips to #0A0A0A and the buttons
     rendered black on dark green at 3.69:1. White alone does not fix it either
     — 2.63 over a .25 scrim on #07F708 — so the scrim went to .55, where white
     is 6.39 dark and 10.48 light. One ink, one scrim, both themes. */
  --scrim-chip:        rgba(0, 0, 0, .55);
  --scrim-chip-hover:  rgba(0, 0, 0, .68);
  --hairline-on-scrim: rgba(255, 255, 255, .55);

  /* THE UNFILLED HALF OF AN INDICATOR — the empty star in a rating, and any
     track that comes after it. Karl, 2026-08-18: "resolve it the way you best
     see fit."

     It was `--line2`, a BORDER token, and it measured **1.37:1** in dark. At
     that level `★★★☆☆` and `★★★` look the same, so the rating loses its
     denominator — and in the review form those empty stars are the thing you
     click, which makes an invisible one a target nobody can find.

     3:1, not 4.5. This is a track, not text carrying meaning by its
     letterforms, and 3:1 is the bar WCAG sets for a UI component that has to be
     identifiable. Deliberately NOT `--faint` (5.0): that is the weight of body
     sub-text and the ghost effect would be gone. 3.44 dark / 3.45 light, with
     the filled star at 11.6 / 4.8 above it — the gap is what makes a rating
     readable at a glance. */
  --track: #6f7073;

  /* THE INK FOR A WARN WASH, AS A TOKEN RATHER THAN A THEME-SCOPED SELECTOR.

     `.h-urg-pill` used `[data-theme="light"] .h-urg-pill { color: #9a5a00 }`,
     and that selector matches whenever ANY ancestor is light — while the token
     it sits beside comes from the NEAREST theme scope. Nest the scopes (kit.html
     puts a light frame inside a dark page, and `.h-chrome` pins dark inside a
     light one) and the two disagree: the page-contrast pass measured the pill
     at **1.24:1**, light ink on a wash mixed over a DARK panel.

     A token cannot disagree with itself that way — it is resolved by the same
     cascade as everything around it. (08-18e) */
  --warn-ink-on-wash: #ffb84d;

  /* ---- THE CHART SLOTS (moved out of stats.css, 2026-09-04) ----
     They were declared in `stats.css` under `:root`, which LOADS TENTH and
     `tokens.css` FIRST. Same specificity, later wins — so a `[data-theme="x"]`
     block written HERE for a new style would have been silently overridden by
     the `:root` in stats.css, and the chart would have kept its old colours
     with nothing to show why. That is the whole reason these moved rather than
     a tidying preference.

     Slot 1 in dark is NOT BreakAway Green. #07F708 measures L=0.846 in OKLab,
     fails the categorical lightness band (0.48-0.67), and glares as a large
     filled area on a dark panel. #05B006 is the same hue one step down. Chart
     FILLS only — every button, pill and accent still uses --green. */
  --chart-1:    #05B006;
  --chart-2:    #7E51FF;
  --chart-grid: #2B2B2B;

  /* THE INK FOR THE PROVENANCE PILLS, and neither one can be its own accent.
     `--purple` on a 16% `--purple` tint is 3.41:1 in dark and the pill is 10px
     bold — small text, so the bar is 4.5. Same shape as --warn/--warn-solid: a
     colour tuned to sit ON the page behaves differently once it is also the
     background. Measured against the WORST composited background each pill
     actually lands on, not the panel it nominally sits over.
       --prov-mixed-ink     dark  #AE90FF on rgb(37,30,58)    = 6.23:1
       --prov-verified-ink  light #4B28AB on the panel        = 6.66:1 */
  --prov-mixed-ink:    #AE90FF;
  --prov-verified-ink: var(--green);

  /* ---- THE TWO PINNED-DARK SURFACES THAT ARE NOT CHROME ----
     The shareable card is an artefact that LEAVES the app — posted to a group
     chat, saved to a camera roll — so the brand has to look the same in
     everybody's feed regardless of which theme the parent who made it was
     using. Same reasoning as `.h-chrome`, different surface, so it reads the
     --chrome-* inks and gets its own ground. */
  --share-bg-1:      #101010;
  --share-bg-2:      #1a1a1a;
  --share-bg-3:      #0A0A0A;
  --share-cell:      rgba(255,255,255,.055);
  --share-cell-line: rgba(255,255,255,.10);

  /* ---- THE MESSENGER DOCK — THE SECOND SURFACE THAT DELIBERATELY IS NOT THE
     PAGE, and it is built in the same shape as --chrome-* rather than a new one.

     Karl's ruling, 2026-09-04: the messenger keeps a distinct identity, but as a
     THEME-DEFINED FAMILY, not a frozen palette. The distinctness is a RULE --
     this panel contrasts with its page -- so it survives into styles nobody has
     drawn yet. A frozen blue only stays distinct by luck: the day a style's
     background drifts near it the messenger stops standing out, and nobody can
     fix that without editing component CSS.

     THE VALUES BELOW ARE TODAY'S, UNCHANGED. Slots first, palette when Karl
     picks one. A slot holding today's value is honest; a slot holding a colour
     he never chose is not.

     WHY FOUR ACCENT SLOTS FOR ONE BLUE, which looks like too many and is the
     honest count: a colour that works as a FILL does not work as INK.
       --dock-accent         identity, shapes only (grip pill, unread dot)
       --dock-accent-strong  the fill under white text -- #3b82f6 with white is
                             3.68:1 and fails AA at 13px; this is 5.17:1
       --dock-accent-text    the same blue as FOREGROUND, which has to move the
                             OTHER way per theme (axe caught 3.3:1 on the reply
                             quote, the reply bar and the owner badge)
       --dock-accent-ink     what sits on --dock-accent-strong

     The surfaces are lighter and slightly cool on purpose. Against the app's
     #0A0A0A page, --panel (#141414) is barely a step up and the dock read as a
     HOLE rather than a window. Light theme keeps the app's own panels, which
     already stand out against #E8E8E8 -- so the light values below are
     redirects, exactly as .h-chrome redirects the globals. */
  --dock-accent:        #3b82f6;
  --dock-accent-strong: #2563eb;
  --dock-accent-text:   #60a5fa;
  --dock-accent-ink:    #ffffff;
  /* BLUE-BLACK SINCE 2026-09-26 (Calder, Stage 1 Messenger). Karl's launch
     ruling is one premium dark theme on blue-black surfaces, not neutral
     grey; these are the approved preview's Messenger values. Still a step up
     from the page (--bg #080B0D, --panel #0C1113) so it reads as a window,
     not a hole — the reason this family exists. Were #1a1d24 / #0f1115 /
     #222732 / #e8eaed / #8b909a / #2a2e38. */
  --dock-surface:       #111823;
  --dock-surface-2:     #0d141d;
  --dock-raised:        #182131;
  --dock-ink:           #e8eef5;
  --dock-muted:         #8ea0b5;
  --dock-line:          #273546;

  /* ---- THE BRAND CONSTANTS' MISSING HALVES ----
     `--brand-purple` and `--brand-green` existed; their rgb triples did not, so
     every tint mixed from a brand colour was written out by hand. These are
     NEVER themed, exactly like the hexes they pair with. Note they are NOT
     --purple-rgb / --green-rgb: those two FLIP with the theme, which is right
     for a semantic accent and wrong for a wash that has to stay Power Play
     Purple whatever the page is doing. */
  --brand-purple-rgb: 126,81,255;
  --brand-green-rgb:  7,247,8;
  --on-brand-purple:  #fff;

  /* ---- INK AND HAIRLINES ON A SURFACE THE THEME DOES NOT OWN ----
     An avatar disc, a space badge and a photo hero are painted a colour that
     comes from DATA, not from the palette — a generated identity colour or an
     uploaded image. White ink and a white hairline are correct on all of them
     in both themes, so these are constants with a ROLE name rather than
     `#fff` repeated nine times. */
  --on-colour:                 #fff;
  --hairline-on-colour:        rgba(255,255,255,.08);
  --hairline-on-colour-strong: rgba(255,255,255,.5);
  --chip-on-photo:             rgba(255,255,255,.22);
  --veil-photo-top:            rgba(0,0,0,.04);
  --veil-photo-base:           rgba(0,0,0,.28);
  --sh-ink-on-photo:           0 1px 3px rgba(0,0,0,.25);
  --switch-knob:               #fff;
  --hero-green-deep:           #063d08;

  /* ---- MORE PINNED CHROME. The app bar and the desktop rail are chrome by
     the theme model at the top of this file, and these three values were the
     part of them still written into components.css. */
  --chrome-appbar-top: #101010;
  --chrome-rail-line:  #1e1e1e;
  /* ⛔ `--chrome-rail-hover` REMOVED, `SYS-51`, 2026-09-22, Vera. Its ONE
     consumer was a `.h-rail__item:hover` body that never won the cascade, and
     the value could not have shipped: it was declared here in the dark block
     only and never redeclared for light, so it painted #161616 on a light grey
     rail. Measured at 2.62:1 against the row's ink before removal. Two pinned
     chrome values here now, not three. */

  /* ---- TWO PAIRS THAT REPLACE A THEME-SCOPED SELECTOR EACH ----
     --hairline is the rule BETWEEN rows; --cell-bg is the sunk square a
     calendar day sits in. Both had a `[data-theme="light"] .x` override, and
     such a selector matches on any light ANCESTOR while the tokens beside it
     resolve from the NEAREST scope. Same defect --warn-ink-on-wash exists for. */
  --hairline: rgba(255,255,255,.06);
  --cell-bg:  var(--bg);

  /* ---- THE PUBLIC ICE TAG ----
     The hue is the schedule's fifth calendar (SCHEDULE_SOURCES in
     functions/lib/spaces.js), repeated here because a stylesheet cannot import
     a JS constant, and asserted equal in tests/icefeed.test.mjs so the two
     cannot drift.

     IT SHIPPED AT 2.12:1 IN LIGHT. Meryl measured it 08-21. #3DB8B0 on a 16%
     wash of itself is 5.88:1 over the dark panel and 2.12:1 over the white one
     — the same two lines, one readable and one not, visible only to somebody
     who had switched themes.

     NO SINGLE INK CLEARS BOTH, which is not a failure of imagination: a 16%
     tint over #141414 and over #FFFFFF are 200 luminance points apart. So the
     WASH KEEPS THE TEAL IDENTITY IN BOTH AND THE INK IS THE ONE THAT MOVES —
     the same split as --warn / --warn-solid.
         dark   #3DB8B0 on 16% over #141414 = 5.88:1
         light  #0B4F4B on 18% over #FFFFFF = 8.06:1
     Both recomputed from these declarations by the test, never from these
     numbers, because a comment cannot go red. Moved here from ice.css by Vera
     2026-09-04: it was a correct per-theme decision at the wrong address, and
     a new style could only have reached it by overriding a COMPONENT selector. */
  --ice-tag-ink:  #3DB8B0;
  --ice-tag-wash: rgba(61, 184, 176, .16);

  /* ---- SCRIMS AND VEILS ----
     --scrim dims a surface so an overlay on top of it can be read. It is a
     TOKEN rather than a `[data-theme="light"]` selector on purpose: the light
     rule it replaces matched whenever ANY ancestor was light, while the tokens
     beside it resolve from the NEAREST theme scope. Nest the scopes and the
     two disagree — the same defect --warn-ink-on-wash was created to fix.

     --veil-on-accent is the opposite job: a fixed darkening over an ACCENT
     fill, so `--on-green` keeps its measured ratio when the accent flips. It
     does not take a light value because it is not reacting to the page — it is
     reacting to the token underneath it, which already flipped. */
  --scrim:          rgba(10,10,10,.30);
  --scrim-modal:    rgba(0,0,0,.5);   /* under a sheet, in both themes */
  /* `SYS-30`. THE ONE SCRIM THAT MUST NOT FOLLOW THE THEME, because what it
     covers is not the theme — it is GROUP DATA. `.pg-hero`'s gradient starts
     at the group's own `colour2`, and `functions/lib/groupidentity.js:79`
     accepts ANY valid hex, so the ground under that text can be pure white.
     A theme-aware scrim would be tuned for a page it is not sitting on.
     .62 is the value that holds white ink at 4.5 against the WORST case a
     group can choose: black at .62 over #FFFFFF is #606060, white on it 6.20.
     At .55 it is 4.76 — passing but with no headroom — and at .50 it fails. */
  --scrim-identity: rgba(0,0,0,.62);

  /* ==========================================================================
     `SYS-04` — THE SEVEN PAINT SLOTS. A STYLE MAY REPAINT; IT MAY NOT MOVE.

     Karl, 2026-09-15: *"It should be essentially repaint only, but a label
     should appear differently on pond hockey than on street hockey... This
     should be accomplishable without redistributing the elements on the page."*

     The gap these close is measurable. His own `Hockey365_GeneralStyles_
     Exploration.html` gave Street Hockey uppercase headings with a purple
     offset, a halftone texture, a hard shadow and a skew. The app's Street
     Hockey kept the palette and NONE of the treatments, because the style layer
     had somewhere to put a colour and nowhere to put any of that.

     EVERY DEFAULT BELOW IS TODAY'S RENDERING, so adding them moves nothing.

     🔻 AND THE EDGE IS AN INSET SHADOW, NOT A BORDER. That is the one trap in
        the whole item: a border grows the box and shifts everything inside it,
        an inset shadow paints in exactly the same place and moves nothing. It
        is what lets Street Hockey carry a 3px edge at zero layout cost. */
  /* Karl's 2026-09-25 launch ruling intentionally moves shared display and
     category labels onto the same condensed face as the approved preview. */
  --head-font:   var(--font-display);
  --head-case:   uppercase;
  --head-track:  .07em;
  --head-weight: 700;
  /* `SYS-49` — THE NINTH SLOT, AND IT EXISTS BECAUSE I SHIPPED ITS NAME BEFORE
     I SHIPPED THE TOKEN. `base.css` read `var(--head-weight-sub, 700)` against a
     name declared nowhere, so it always computed 700 — and `--head-weight` is
     ALSO 700 here, on gm and on pond. 📏 MEASURED ACROSS ALL TEN THEME x STYLE
     COMBINATIONS: SIX OF TEN HAD ZERO WEIGHT CONTRAST between a chapter heading
     and the quiet level directly under it. Only openice (800) and street (900)
     had any — which is exactly why a happy walk could not see it, because Karl
     walks openice.
     ⚖️ ONE STEP DOWN FROM `--head-weight`, AND THE STEP IS CHOSEN FROM WHAT THE
     STYLE'S FACE CAN ACTUALLY RENDER rather than from a tidy number. 600 here
     because `index.html` loads Public Sans at 400;500;600;700;800. */
  --head-weight-sub: 600;
  --head-shadow: none;
  /* THE EIGHTH SLOT, the ruled heading build. The section label's rule, drawn
     as a BACKGROUND IMAGE in base.css rather than a border, so it costs no
     layout. It follows `--green-rgb`, which every style and the light ground
     already redefine, so the rule turns purple in light on its own — Karl's
     09-05 ruling, for free, with nothing here naming a second colour.
     ⚠️ AND IT ONLY WORKS BECAUSE `data-theme` SITS ON `<html>`. A custom
     property is substituted where it is DECLARED, not where it is used, so
     this `var(--green-rgb)` resolves against whatever `:root` holds. theme.js
     sets both axes on `document.documentElement`, which IS `:root`, so the
     light block's `--green-rgb` lands on the same element and the rule flips.
     Move `data-theme` to `<body>` and it silently stops flipping — verified,
     because my first test did exactly that and read back the dark green. */
  --head-rule:   rgba(var(--green-rgb), .42);
  --card-tex:    none;
  --card-edge:   0 0 0 0 transparent;
  --veil-on-accent: rgba(0,0,0,.17);

  /* ---- THE ACTION, AS A ROLE RATHER THAN A COLOUR ----
     `.h-btn` painted `--green` directly, which meant "the primary action" and
     "the semantic accent" were the same token and could never disagree. They
     need to: Open Ice Rush wants its action to stay BRAND GREEN when the lights
     go on, while `--green` correctly flips to purple for everything else.
     Defaults to exactly what `.h-btn` already used, so nothing moves until a
     style says otherwise. */
  --action:    var(--green);
  --on-action: var(--on-green);

  /* ---- Type ---- */
  --font-display: Impact,Haettenschweiler,'Arial Narrow Bold',sans-serif;
  --font-head: var(--font-display);
  --font-body: 'Public Sans',-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;
  --fs-xs:  10px;  --fs-sm:  11.5px; --fs-md: 13px;
  --fs-base:15px;  --fs-lg:  17px;   --fs-xl: 20px;  --fs-2xl: 24px;
  --lh: 1.5;

  /* ---- Radius ---- */
  --r-sm: 8px;  --r-md: 11px; --r-lg: 14px; --r-xl: 16px;
  --r-2xl:20px; --r-pill: 999px; --r-phone: 34px;

  /* ---- Spacing ---- */
  --sp-1: 4px;  --sp-2: 7px;  --sp-3: 9px;  --sp-4: 13px;
  --sp-5: 16px; --sp-6: 22px; --sp-7: 30px;

  /* ---- Elevation ---- */
  --sh-1: 0 2px 8px rgba(0,0,0,.35);
  --sh-2: 0 6px 16px rgba(0,0,0,.45);
  --sh-3: 0 30px 80px rgba(0,0,0,.6);
  --sh-accent: 0 3px 12px rgba(var(--green-rgb),.28);

  /* ---- THE FOCUS RING, AND IT IS AN ACCESSIBILITY FIX RATHER THAN A STYLE ----
     Measured 2026-09-04: `tokens.css` had NO focus token at all, and the global
     rule in base.css was `outline: 2px solid var(--green)`.

     🚨 --green IS THE ACCENT, SO THE FOCUS RING WAS THE ACCENT COLOUR ON THE
     ACCENT FILL: **1.00:1 on the primary button, in BOTH themes.** Also 1.17
     on --warn-solid in dark and 1.08 on --danger-solid in light. WCAG 2.2 asks
     3:1 for a focus indicator against what it sits on. The keyboard ring was
     invisible on exactly the controls a keyboard user most needs to find.

     NO SINGLE COLOUR FIXES IT, and that is not a failure of imagination -- the
     same shape as .ice-tag. In dark the accent fill #07F708 is very light, so
     only a DARK ring shows on it; in light the chrome stays #0d0d0d, so only a
     LIGHT ring shows there. Measured across 12 real surfaces per theme, every
     single-colour candidate (--green, --ink, --info, white, black) bottoms out
     between 1.00 and 1.53.

     ▶️ SO THE RING IS TWO-TONE, and one half always contrasts. Worst case
     across all 24 surface/theme combinations is 4.25:1, and the two halves
     separate from each other at 17.37:1.

     THEY DO NOT FLIP WITH THE THEME, WHICH IS THE POINT: the ring's job is to
     contrast with whatever is UNDER it, not with the page. A ring that themed
     would go back to guessing. */
  --ring-inner: #0A0A0A;
  --ring-outer: #F0F0F0;
  --ring-w:     2px;

  /* ---- FOUR SHADOWS THAT ARE BORIS'S RUNGS WRITTEN OUT BY HAND ----
     Every one of these is `--sh-1` or `--sh-2` with a different blur radius,
     typed straight into hub.css. Tokenised at TODAY'S VALUES so nothing moves;
     ▶️ COLLAPSING THEM ONTO --sh-1/--sh-2 IS PASS ②'s JOB, because that IS a
     visual change and the ladder is the thing that says which rung each takes.
     Named so that collapse is obvious rather than archaeological:
       --sh-dock     the messenger panel, directional (it slides from the edge)
       --sh-popover  the emoji picker   -- rung 2, floating control
       --sh-menu     the message menu   -- rung 2, floating control
       --sh-chip     the selection chip -- rung 1, organised surface
       --sh-hairline the dock's grab handle: a 1px ring, not depth at all */
  --sh-dock:     -24px 0 60px rgba(0,0,0,.32);
  --sh-popover:  0 6px 20px rgba(0,0,0,.34);
  --sh-menu:     0 8px 24px rgba(0,0,0,.36);
  --sh-chip:     0 2px 10px rgba(0,0,0,.35);
  --sh-hairline: 0 0 0 1px rgba(0,0,0,.14);

  /* A CANCELLED GAME IS NOT AN ERROR. `.h-gs__cancelled` carries a danger
     BORDER but its wash is its own pink -- softer than --danger, because the
     game being off is information rather than something going wrong. */
  --wash-cancelled: rgba(255,92,138,.12);

  /* ---- THE STAFF TIER GLOW (Karl picked Direction A, 2026-09-04) ----
     AMBIENT ABOUT CONTEXT, NOT SEMANTIC ABOUT CONTENT, and that is why these
     get their own hues rather than borrowing one. Every other colour in this
     app says something about the THING it is on -- this is a warning, this is
     danger, this is the accent. These say where you ARE and what you can
     break, which is a different axis.

     TWO HUES, NOT THREE. Cyan and orchid are the only regions of the wheel
     this app has not spent: green is the accent in dark, purple the accent in
     light and the brand secondary, amber is --warn, red is --danger, #4da3ff
     is --info, #3DB8B0 is the ice tag, #3b82f6 is the messenger.
     ⛔ Founder is deliberately NOT a third hue and NOT amber: amber is --warn,
     and the founder screens are the ones carrying the destructive controls. A
     permanent amber wash around a screen full of dangerous buttons teaches the
     eye to ignore amber. Founder escalates by TREATMENT -- a hazard stripe --
     which also makes the most dangerous tier the one that is readable without
     seeing colour at all.

     THE ESCALATION IS STEEP, NOT EVEN, AND THAT IS DELIBERATE. Staff screens
     get used all day; manager and founder screens are rare. A tier signal that
     shouts on the screen you live in is one you stop seeing by Wednesday. */
  --tier-staff-rgb:   56,189,248;
  --tier-manager-rgb: 232,121,249;
  --tier-founder-rgb: 232,121,249;
  /* THE CHIP IS A SOLID FILL WITH A MEASURED INK, not tinted text on its own
     wash -- the first draft was exactly that and `contrast.test.mjs` caught it
     at 3.34:1 against a 4.5 bar, because 9.5px bold is SMALL text. Same shape
     as --warn / --warn-solid: a colour tuned to sit ON the page behaves
     differently once it is also the background. In dark both fills are bright,
     so one near-black ink serves both: staff 9.24:1, manager 8.05:1. */
  --on-tier: #0A0A0A;

  /* ---- Motion ---- */
  --t-fast: .12s ease;
  --t-med:  .22s cubic-bezier(.2,.6,.2,1);
  --t-sheet:.26s cubic-bezier(.4,0,.2,1);

  /* ---- Chrome (pinned dark, referenced by .h-chrome) ---- */
  --chrome-bg:     #0d0d0d;
  --chrome-panel:  #141414;
  --chrome-panel2: #1c1c1c;
  --chrome-line:   #2B2B2B;
  --chrome-line2:  #343434;
  --chrome-ink:    #F0F0F0;
  --chrome-muted:  #9a9b9e;
  --chrome-faint:  #8a8b8e;   /* chrome sits on #0d0d0d; #6D6E71 there was 3.8:1 */

  color-scheme: dark;
}

/* ============================================================================
   LIGHT THEME — content area only. Accent flips green -> purple.
   NOTE: the dark block above is also selected by [data-theme="dark"], not just
   :root, so a dark subtree nested inside a light page (or vice versa) resolves
   correctly. The kit page relies on this to show both themes side by side.
   ========================================================================== */
[data-theme="light"] {
  --bg:     #E8E8E8;
  --panel:  #FFFFFF;
  --panel2: #F0F0F0;
  --elev:   #F0F0F0;
  --line:   #D0D0D0;
  --line2:  #B0B0B0;
  --avatar-black: #000000;
  --avatar-ghost-ring: #7b7f82;
  --avatar-ghost-ink: #c4c5c8;

  --ink:   #0A0A0A;
  --muted: #595A5C;   /* was #6D6E71 = 4.16:1, just under AA. Now ~5.9:1. */
  --faint: #616265;   /* MEASURED AGAINST THE PAGE, NOT THE PANEL. #6E6F72 was
                         put here as "~4.6:1" — which is true on --panel
                         (#FFFFFF, 5.02) and false on --bg (#E8E8E8, 4.10),
                         and small print sits on the page far more often than
                         on a white card. axe caught it on the coaching surface
                         the first time that surface was audited. #616265 is
                         4.98 on the page, 5.35 on --panel2 and 6.10 on white,
                         so it clears AA wherever it lands. */

  --green:     #6B3AF5;   /* was #7E51FF = 3.80:1 as link text on light. Now ~5.1:1.
                             The fill stays visually Power Play Purple; this is the
                             INK-safe shade for text and borders. */
  --green-rgb: 107,58,245;
  --on-green:  #FFFFFF;   /* white on the purple fill = 6.4:1. #F0F0F0 was thinner. */
  --green-dim: rgba(126,81,255,.13);
  --active-green-bg: #E8E1FF;
  --active-green-ink: #4B28AB;
  --srcGreen:  #07A608;          /* darkened brand green, for contrast on light */

  /* STEP THREE of `FND-02`, and it is only safe now that the 6 fill sites read
     --purple-solid. #07A608 was the FILL value living in the ink token; with
     the fills moved, this can finally be tuned for the two jobs it still does.
       as INK on --panel   3.25 -> 5.53      (4.5 bar)
       as INK on --bg      2.65 -> 4.52      (4.5 bar)
       as an EDGE vs --bg  2.65 -> 4.52      (3.0 bar)
     #067A07 is not a new colour in this file: it is Pond Hockey's green,
     already measured and already shipping. Reusing it keeps the light palette
     to one dark green rather than two that differ by a shade nobody can name. */
  --purple:     #067A07;         /* community accent swaps the other way */
  --purple-rgb: 6,122,7;         /* follows --purple, per step three */
  /* ==========================================================================
     `FND-02` — THE FILL SHADE, AND WHY ONE TOKEN COULD NOT DO THIS JOB.
     Karl: "the green on Fill a Roster is much too dark (this might be a vera
     thing and should be an app wide change if this dark green is being used
     throughout)." He is right that it is app-wide, and the cause is the one
     --warn/--warn-solid and --danger/--danger-solid already answer in this
     file: A FILL AND AN INK ARE TWO JOBS AND ONE VALUE CANNOT HOLD BOTH.

     Measured, `--purple` #07A608 in light, across the three jobs it is doing:
       as a FILL, dark ink on it            6.10   ok       6 call sites
       as INK on --panel                    3.25   FAIL     2 call sites
       as an EDGE against --bg              2.65   FAIL    16 call sites
     So the value is tuned for the fill, fails the other two, and the fill is
     the one Karl says looks wrong. It is not too dark for its job. It is doing
     three jobs and it is wrong for two of them.

     --purple-solid is the FILL, and #12D411 carries dark ink at 9.85 — the
     brighter green Karl is asking for.

     ⚠️ ORDER MATTERS, AND GETTING IT WRONG HANDS KARL THE OPPOSITE.
     The 6 fill sites still read `var(--purple)`. Until they move to
     `var(--purple-solid)`, `--purple` must NOT be corrected to its ink value
     (#067A07, which is DARKER) — that would darken every fill. This block is
     ADDITIVE and its delta today is ZERO. The ink correction is step three. */
  --purple-solid: #12D411;
  /* NEAR-BLACK, NOT NEAR-WHITE. #F0F0F0 on this green is 2.85:1 — a bad fail,
     and it was the declared ink for the token, so every `.h-btn--purple` in
     the light theme had unreadable text. Dark ink on the same fill is 6.1:1.
     axe found it on the coaching calendar; it was never specific to that. */
  --on-purple:  #0A0A0A;
  --purple-dim: rgba(126,81,255,.12);
  --active-purple-bg: #DDF7E1;
  --active-purple-ink: #065A12;

  --success: #07A608;
  /* #cc6600 is 3.13:1 on the page and 3.48:1 on its own 12% tint — both fails,
     and this token is used as INK for pending pills and warning panels.
     #994D00 is 5.02 and 5.57. The fill shade lives in --warn-solid. */
  /* MST tokens, measured for text on the light surfaces (the light theme is
     retired for members but every rule is still measured in both). */
  --mst-record:   #3E5A68;
  --mst-untracked:#59646a;
  --mst-season:   #6B4FD8;
  --mst-season-rgb: 107,79,216;
  --mst-ahead:    #0B7A6C;
  --gold:         #8A6410;
  --gold-rgb:     138,100,16;
  --warn:    #994D00;
  /* #c92a2a IS 4.45:1 ON THE PAGE — a fail, by 0.05, and it stood for weeks
     because of the note eight lines below this one. That note says white on
     #c92a2a "clears 3:1 against every light surface too (4.45 at worst, on
     --bg)", which is TRUE and is about the FILL. Forty rules use this token as
     `color:` on text, where the bar is 4.5, not 3 — so the measurement that
     justified the value was taken for a different job than the one it does.
     A number in a comment is only as good as the role it was measured for.

     Reported twice by the Sol seat, from two different surfaces, by eye —
     which is the part that matters, because 31 harnesses measure composited
     contrast and all of them passed. `.h-error` exists on login and claim but
     is EMPTY until something goes wrong, and the walker only measures elements
     that actually paint text. A clean sweep over a state the page never renders.

     #bd2424 measured on the rendered page, not from the palette: 4.98 on --bg,
     5.35 on --panel2, 6.10 on --panel — the same three numbers --faint was
     moved to for the same reason, so it clears AA wherever it lands rather than
     scraping past on one surface. #c72a2a would have passed at 4.52; a 0.02
     margin is not a fix, it is the same bug with a better number.

     The FILL shade stays #c92a2a in --danger-solid below. Ink token and fill
     token, the pattern --warn/--warn-solid and --green/--on-green already
     use. */
  --danger:  #bd2424;

  /* MEASURED AGAINST THE PAGE AND THE PANEL, NOT JUST WHITE — the third time
     this block has had to say that, after --faint and --danger, and by the same
     route: a value set against #FFFFFF, correct there, and wrong on every
     greyer surface it actually lands on.

     #1A6FCC was 5.01 on --panel, 4.40 on --panel2/--elev and 4.09 on --bg. It
     is INK in two places and both of them failed: `.h-ch__av.is-support`, the
     support avatar in Messages, and `.h-post__scope--area`, the area badge on a
     Centre Ice post. Only the first had a harness looking at it.

     #1663B6 is the same hue three steps darker: 6.01 on --panel, 5.27 on
     --panel2/--elev, 4.90 on --bg. #1867BE would have passed at 4.60 — and
     --danger already says here why a value chosen for the narrowest margin is
     the same bug with a better number. The pond style takes the identical value
     below and clears its timber page at 4.90 where it was 4.08.

     DARK IS UNTOUCHED. #4da3ff measures fine in both dark styles and was never
     part of this. */
  --info:    #1663B6;

  /* Light needs a deeper fill so white text clears the small-text bar.
     #cc6600 with white is 4.43:1 — a fail. #A85200 is 7.5:1. */
  --warn-solid: #A85200;
  --on-warn:    #FFFFFF;   /* 7.5:1 */

  /* --danger-solid / --on-danger are NOT overridden here on purpose: white on
     #c92a2a is 5.46:1 in both themes and clears 3:1 against every light surface
     too (4.45 at worst, on --bg). One appearance for one meaning. */
  --danger-rgb: 201,42,42;
  --warn-rgb:   153,77,0;
  /* THE WASH KEEPS ITS IDENTITY, THE INK MOVES — see the note in the dark
     block. These repeat the dark values on purpose; they are what the app
     already renders in light, and they measure 4.68–5.74 there against
     --warn / --danger as ink. */
  --warn-wash-rgb:   255,184,77;
  --danger-wash-rgb: 255,107,107;

  /* Light needs it slightly lighter to sit at the same distance from the page:
     3.45 at worst, against #FFFFFF, #F0F0F0 and #E8E8E8. */
  --track: #7a7b7e;
  --warn-ink-on-wash: #7a4700;   /* 6.61 on the .28 wash over --panel */

  /* The slots FLIP with the theme exactly as --green/--purple do. A chart that
     refused to would be the one green thing on a purple screen. */
  --chart-1:    #6B3AF5;
  --chart-2:    #07A608;
  --chart-grid: #D0D0D0;

  /* #04500A on the real composited ground rgb(181,201,198) = 5.63:1. Hand-
     computing against --panel gave a comfortable 4.90 for a different colour
     and the tool then measured the actual background at 3.41. */
  --prov-mixed-ink:    #04500A;
  /* `var(--green)` here is #6B3AF5, which measures 4.45:1 on the panel — a pass
     by four hundredths — and 4.11:1 on --panel2, so it was legible only because
     of what happened to be behind it. #4B28AB is the same hue three steps
     darker: 6.66 on panel, 5.36 on panel2, 9.59 on white for the printed
     report. Dark was never close and is untouched. */
  --prov-verified-ink: #4B28AB;

  /* THE DOCK IN LIGHT. The text blue goes DARKER, not lighter -- on a white
     panel it is the PALE blue that fails. The surfaces hand back to the page
     tokens because they already stand out against #E8E8E8; the ACCENT slots do
     not, and must not: pointing them at --green would make the messenger blend,
     which is the one thing Karl asked it not to do. */
  --dock-accent-text: #1d4ed8;
  --dock-surface:     var(--panel);
  --dock-surface-2:   var(--panel2);
  --dock-raised:      var(--panel2);
  --dock-ink:         var(--ink);
  --dock-muted:       var(--muted);
  --dock-line:        var(--line);

  --hairline: rgba(0,0,0,.07);
  --cell-bg:  #f2f2f5;   /* a touch lighter than --bg #E8E8E8, so the day square
                            reads as sunk rather than as the page showing through */

  --ice-tag-ink:  #0B4F4B;
  --ice-tag-wash: rgba(61, 184, 176, .18);

  --scrim: rgba(240,240,240,.42);

  /* A cyan tuned for a near-black page is washed out on a near-white one. */
  /* Staff goes DEEPER in light than the first draft (#0284C7 -> #0369A1) so one
     ink serves both tiers here too: white on staff 5.93:1, on manager 6.32:1.
     #0284C7 forced a per-tier ink flip and still only reached 4.83. */
  --tier-staff-rgb:   3,105,161;
  --tier-manager-rgb: 162,28,175;
  --tier-founder-rgb: 162,28,175;
  --on-tier: #FFFFFF;

  --sh-1: 0 2px 8px rgba(0,0,0,.10);
  --sh-2: 0 6px 16px rgba(0,0,0,.14);
  --sh-3: 0 30px 80px rgba(0,0,0,.18);

  color-scheme: light;
}

/* ============================================================================
   THE SECOND AXIS: `data-style`. THE PERSONALITY, NOT THE MODE.
   Karl's launch set, 2026-09-04. Ratified by the Orchestrator 17:42.

   ---------------------------------------------------------------------------
   TWO ATTRIBUTES, NOT MORE VALUES ON ONE, AND THE DISTINCTION IS LOAD-BEARING.

     <html data-theme="dark|light" data-style="openice|gm|street|pond">

   `data-theme` is the MODE and it keeps doing exactly what it did. `data-style`
   is the personality. **Open Ice Rush is not a peer of dark and light -- it HAS
   a dark and a light.** Folding styles into `data-theme` would have destroyed
   that pairing, and it is the mistake I nearly shipped: see Chat Inboxes/Vera.md
   2026-09-04 16:43, where I described the wrong mechanism and corrected it.

   Boris's exploration file has carried `data-theme` AND `data-vibe` as separate
   attributes since 08-31. The model was proved in the catalogue before it was
   written here.

   ---------------------------------------------------------------------------
   ORDER IS LOAD-BEARING, FOR THE SAME REASON THE CHART SLOTS WERE.

   `[data-style="x"]` and `[data-theme="light"]` have IDENTICAL specificity
   (0,1,0), so the cascade breaks the tie on DOCUMENT ORDER. **Every style block
   below therefore comes AFTER both theme blocks.** Written the other way round,
   the light theme would silently overwrite a style and the style would look
   half-applied with nothing on screen to say why -- exactly the defect that
   made `--chart-1/2/grid` move out of stats.css this morning.

   A per-theme refinement is `[data-style="x"][data-theme="light"]` (0,2,0),
   which beats both, and is where a THEMED style says what light does to it.

   ---------------------------------------------------------------------------
   THEMED STYLES vs LOCKED STYLES, AND WHY TWO OF FOUR ARE LOCKED.

     THEMED   openice, gm     -- a dark and a light. Two token sets, two
                                 contrast runs.
     LOCKED   street, pond    -- ONE appearance. The mode switch does nothing
                                 and `settings.js` greys it out.

   A locked style is the `.h-chrome` move: it pins its own values so the page
   theme cannot reach it. That is not a shortcut, it is the honest answer for a
   style whose whole identity is one light condition -- and it halves the
   measurement. Street Hockey in LIGHT would have been the hardest combination
   of the eight: `#07F708` on `#FFFFFF` is **1.34:1**, and that style leans on
   lime for borders, shadows and the primary button.

   🔻 A LOCKED STYLE MUST PIN EVERY TOKEN IT USES, NOT EVERY TOKEN IT SETS.
      Learned the hard way at 17:10: Pond Hockey pinned `--green` and not
      `--on-green`, so the green check disc's INK still followed the page --
      near-black in dark, WHITE in light, where it measured 3.25:1. An unpinned
      ink makes the lock a half-truth, and the half that leaks is invisible
      until somebody flips the mode the style claims not to have.
   ========================================================================== */

/* ---- OPEN ICE RUSH -- the default. Fast, square-ish, brand green on the
   action. Its surfaces ARE the app's surfaces; what makes it itself is the
   geometry and where the green lands. ------------------------------------- */
[data-style="openice"] {
  --r-sm: 6px;  --r-md: 8px;  --r-lg: 10px; --r-xl: 12px; --r-2xl: 14px;
  /* Fast and square. The heading is tracked caps; nothing else is painted. */
  --head-case:   uppercase;
  --head-track:  .16em;
  --head-weight: 800;
  /* 700 against an 800 chapter — the value this level ALREADY computed to, so
     openice renders byte-for-byte as Karl walked it and said it was perfect. */
  --head-weight-sub: 700;
  /* DARK: the action is brand green with dark ink. 13.50:1. */
  --action:    var(--brand-green);
  --on-action: var(--brand-black);
}
/* LIGHT LEANS PURPLE. Karl, 2026-09-05, and he stated it as a rule for EVERY
   style that has a dark/light switch, not as a tweak to one screen:
   "Light mode should lean more into our purple themed highlights rather than
   sticking with Green on light, thats consistent across all variations that
   have a dark/theme switcher."

   So in light the style stops overriding the accent and the page's own accent
   comes back: --green is #6B3AF5 there and --on-green is #FFFFFF.
     white ink on the action   5.92
     the action vs --panel     5.92    (its own edge, 3.0 bar)
     the action vs --bg        4.83
   Dark is untouched: #0A0A0A on #07F708 = 13.50.

   🔻 AND THIS CLOSES A DEBT I FILED MYSELF IN V1.3, four lines below. I wrote
      that a lime fill on a pale page has almost no boundary of its own --
      1.20:1 on --bg, 1.47 on --panel -- and that "the light mode owes this
      style a border on the action", filed as a component rule. It does not owe
      it any more. The purple fill carries its own edge at 4.83 and 5.92, so the
      component rule that was going to paper over a 1.20 boundary is not needed
      and should NOT be written.

   ⚠️ REVERSES MY OWN V1.3 DECISION, deliberately and on Karl's ruling. The
      comment that used to sit in this block said the action must not change
      colour when the lights go on. That was my call, not his, and his overrides
      it. GENERAL MANAGER needs no equivalent block -- it never overrides
      --action, so it already inherits the purple. Street and Pond are locked to
      one mode each, so the ruling does not reach them.

   NOT AN ATTRIBUTE-COUNT TIE: [data-style][data-theme] is (0,2,0) against the
   block above at (0,1,0), so this wins on specificity and not on document
   order. It still sits after the theme blocks, as assertion (1) in
   tests/styles.test.mjs requires of every style block. */
[data-style="openice"][data-theme="light"] {
  --action:    var(--green);
  --on-action: var(--on-green);
}
/* 🪞 THERE WAS A `[data-style="openice"][data-theme="light"]` BLOCK HERE AND IT
   WAS WRONG, WHICH IS WORTH LEAVING THE NOTE FOR RATHER THAN QUIETLY DELETING.
   It flipped the action to `#07A608` with WHITE ink -- **3.25:1, a fail**, and
   the identical pairing that was caught in Pond Hockey two hours earlier. It
   also contradicted the comment four lines above it, which says the action must
   NOT change colour when the lights go on.

   THE TRAP IS STILL LIVE AND THAT IS WHY THIS NOTE STAYS: do not solve "light
   needs a different action" by DARKENING THE GREEN. #07A608 with white ink is
   3.25:1 and it will look plausible. The light action is PURPLE, in the block
   above, and the numbers are there.

   The rest of what this note used to say -- that brand green in light costs the
   button its edge (1.20:1 on `--bg`), so light owed the style a border -- is
   SUPERSEDED, not deleted, because it is why the edge numbers are quoted above.
   The purple carries its own boundary, so that component rule is not owed. */

/* ---- GENERAL MANAGER -- front-office authority. Square, ruled, no fills. -- */
[data-style="gm"] {
  --r-sm: 0px;  --r-md: 0px;  --r-lg: 0px;  --r-xl: 0px;  --r-2xl: 0px;
  /* Front office: wider tracking, lighter weight, no fills, no texture. */
  --head-case:   uppercase;
  --head-track:  .22em;
  --head-weight: 700;
  /* gm's chapter is 700 and the quiet level was also 700: no contrast at all.
     Public Sans, so 600 is a weight the page actually loads. */
  --head-weight-sub: 600;
  --sh-1: none; --sh-2: none; --sh-3: none;
  --action:    var(--green);
  --on-action: var(--on-green);
}

/* ---- STREET HOCKEY -- LOCKED DARK. Every token it uses is pinned. ------- */
[data-style="street"] {
  /* `SYS-04`. The treatments from Karl's own exploration, which this style has
     been missing since it shipped: heavy uppercase with a purple offset behind
     the type, a halftone ground, and a lime edge. LOCKED STYLE, so every slot
     it uses is pinned here — the lock rule, and the edge and texture are slots
     it uses. */
  --head-case:   uppercase;
  --head-track:  .03em;
  --head-weight: 900;
  /* Mona Sans is a variable face declared `font-weight: 200 900`, so any value
     is real here; 700 keeps street exactly as approved.
     📏 MEASURED IN INK, NOT IN ADVANCE WIDTH, and the distinction is the whole
     finding: at 12.5px Mona renders 600 with 16% fewer dark subpixels than 700
     (5618 vs 6693) while the advance width is IDENTICAL to two decimals. A
     width probe said "no difference" and was the wrong instrument. */
  --head-weight-sub: 700;
  /* 🔴 THE DROP SHADOW IS GONE, AND IT WAS `2px 2px 0 var(--brand-purple)`.
     Karl, on the More sheet: the headings needed more weight, and FIND read as
     a smudge. It was not clipped and it was not broken — I rendered it headless
     and counted the purple pixels with `overflow: hidden` on and off, 592
     either way — it painted exactly as written. The trouble was proportion: a
     2px hard offset is about a quarter of the cap height at this size, so it
     filled the counters and the gaps at `.03em` tracking and read as a printing
     misregistration rather than emphasis.
     WITH THE RULE BELOW IT, IT WAS ALSO REDUNDANT: two devices doing one job,
     and the one that worked was the rule. Ruled by Karl 2026-09-19. */
  --head-shadow: none;
  /* PURPLE, NOT GREEN. Every other accent street owns is purple; a green rule
     under a heading would be the one place it is not. NOT dimmed either — a
     .42 wash is an Open Ice register and street does not have one. */
  --head-rule:   var(--brand-purple);
  --head-font:   var(--font-head);
  --card-tex:    radial-gradient(rgba(126,81,255,.30) 1px, transparent 1px);
  --card-edge:   inset 0 0 0 3px var(--brand-green);
  --bg:     #080B0D;  --panel:  #0F1518;  --panel2: #131A1E;  --elev: #182126;
  --ink:    #F0F0F0;  --muted:  #9a9b9e;  --faint:  #8a8b8e;
  --line:   #343434;  --line2:  #343434;  --track: #6f7073;
  --green:     var(--brand-green);
  --green-rgb: var(--brand-green-rgb);
  --on-green:  var(--brand-black);
  --green-dim: rgba(7,247,8,.13);
  --active-green-bg: #102015;
  --active-green-ink: #68FF78;
  --purple:     var(--brand-purple);
  /* `FND-02`: pinned. A locked style pins every token it USES. */
  --purple-solid: var(--brand-purple);
  --purple-rgb: var(--brand-purple-rgb);
  --on-purple:  #FFFFFF;
  --active-purple-bg: #1B1728;
  --active-purple-ink: #D0C3FF;
  --action:    var(--brand-green);
  --on-action: var(--brand-black);
  --r-sm: 4px; --r-md: 4px; --r-lg: 4px; --r-xl: 4px; --r-2xl: 4px;
  /* MST (2026-10-03): the Stat Tracker / Milestones inks, pinned with the lock. */
  --mst-record: #A9C1CC; --mst-season: #B79CFF; --mst-ahead: #2DD4BF;
  --mst-season-rgb: 183,156,255; --mst-untracked: #6f7a80;
  --gold: #e5b84c; --gold-rgb: 229,184,76;
  color-scheme: dark;

  /* 🔒 THE COMPLETE INK SET, ADDED 2026-09-04 AFTER `pages-contrast`
     FAILED THIS STYLE. The lock pinned the GROUND and not the INKS, so
     when the viewer's `data-theme` disagreed with the lock every semantic
     colour was still the OTHER theme's -- chosen for a ground that is not
     there. `--danger` on `p.h-error` measured 2.26:1 on login, claim,
     checkout and staff-payments: the doors somebody meets before they have
     an account, and the one where they pay.

     Generated from the matching theme block rather than typed, because 33
     hand-copied values is how the 34th gets missed. */
  --cell-bg: var(--bg);
  --chart-1: #05B006;
  --chart-2: #7E51FF;
  --chart-grid: #2B2B2B;
  --danger: #ff6b6b;
  --danger-rgb: 255,107,107;
  --dock-accent-text: #60a5fa;
  --dock-ink: #e8eaed;
  --dock-line: #2a2e38;
  --dock-muted: #8b909a;
  --dock-raised: #222732;
  --dock-surface: #1a1d24;
  --dock-surface-2: #0f1115;
  --hairline: rgba(255,255,255,.06);
  --ice-tag-ink: #3DB8B0;
  --ice-tag-wash: rgba(61, 184, 176, .16);
  --info: #4da3ff;
  --on-tier: #0A0A0A;
  --on-warn: #101114;
  --prov-mixed-ink: #AE90FF;
  --prov-verified-ink: var(--green);
  --purple-dim: rgba(126,81,255,.16);
  --scrim: rgba(10,10,10,.30);
  --sh-1: 0 2px 8px rgba(0,0,0,.35);
  --sh-2: 0 6px 16px rgba(0,0,0,.45);
  --sh-3: 0 30px 80px rgba(0,0,0,.6);
  --success: #07F708;
  --tier-founder-rgb: 232,121,249;
  --tier-manager-rgb: 232,121,249;
  --tier-staff-rgb: 56,189,248;
  --warn: #ffb84d;
  --warn-ink-on-wash: #ffb84d;
  --warn-rgb: 255,184,77;
  --warn-solid: #ffb84d;
}

/* ---- POND HOCKEY -- LOCKED LIGHT. Cream, hand-cut, quiet.
   THREE PURPLES AND TWO GREENS, WHICH IS NOT EXCESS: a fill, an ink and a
   border are three jobs, measured 2026-09-04 against the timber ground.
     --pond-purple-fill  #8B62FF  black ink on it = 4.99  (the Join Game button)
     --pond-purple       #6B3AF5  the chip border
     --pond-purple-ink   #5B2FD1  chip text on its own 13% tint = 5.29
     --pond-green        #07A608  the check disc, with #0A0A0A ink = 6.10
     --pond-green-line   #067A07  the success border vs timber = 4.51 -------- */
[data-style="pond"] {
  /* `SYS-04`. Rustic and hand-made: a serif heading in sentence case, a painted
     board edge, no texture. LOCKED STYLE, so every slot it uses is pinned. */
  --head-font:   Georgia, 'Iowan Old Style', 'Times New Roman', serif;
  --head-case:   none;
  --head-track:  .005em;
  --head-weight: 700;
  /* ⚠️ 400, NOT 600, AND POND IS THE ONE STYLE WHERE THE TIDY NUMBER WOULD HAVE
     BEEN A LIE. Its `--head-font` is Georgia, a system serif with exactly two
     real weights: 400 and 700. A 600 here does not exist in the face — the
     browser snaps or synthesises it — so it would have been the same shape of
     defect as the undeclared token this row is about: a number that reads as a
     decision and renders as nothing. 400 is a weight Georgia actually has. */
  --head-weight-sub: 400;
  --head-shadow: none;
  /* POND RULES IN POND'S OWN LINE COLOUR. A green rule under a serif heading on
     paper is the wrong century, and this is the same value its card edge uses,
     so the screen carries ONE line colour rather than two. */
  --head-rule:   var(--pond-rule);
  --card-tex:    none;
  --card-edge:   inset 0 0 0 2px var(--pond-rule);
  --pond-timber: #EFE7DA;  --pond-rule: #C9BCA6;
  --bg:     var(--pond-timber);  --panel: #FFFFFF;  --panel2: #FFFFFF;
  --elev:   #FFFFFF;
  --ink:    #0A0A0A;  --muted: #595A5C;  --faint: #616265;
  --line:   var(--pond-rule);  --line2: var(--pond-rule);  --track: #7a7b7e;
  --pond-green:      #067A07;   /* ink on cream 4.51, white ON it 5.53 */
  --pond-green-line: #067A07;
  --pond-purple:      #6B3AF5;
  --pond-purple-ink:  #5B2FD1;
  --pond-purple-fill: #8B62FF;
  --green:     var(--pond-green);
  --green-rgb: 7,166,8;
  /* 🔻 WHITE, NOT NEAR-BLACK, AND THE HISTORY IS THE WARNING.
     This said #0A0A0A, which was correct for the green that USED to be here:
     #07A608, where near-black measured 6.10. When that green moved to #067A07
     -- because the old one failed as INK on the cream ground at 2.65 -- **the
     fill changed and its ink stayed behind.** #0A0A0A on #067A07 is 3.58:1,
     and `pages-contrast` found it on the selected segmented pill on
     admin-invoices and admin-programs, in both themes.

     The generated ink set could not catch it: that pass only filled tokens
     that were MISSING, and this one was present -- just paired with a colour
     that no longer existed. **A pinned ink is not a correct ink.**
     #FFFFFF on #067A07 is 5.53. */
  --on-green:  #FFFFFF;
  --green-dim: rgba(7,166,8,.13);
  --active-green-bg: #E1F3E1;
  --active-green-ink: #065A12;
  --purple:     var(--pond-purple);
  /* `FND-02`: pinned, or this locked LIGHT style inherits the light
     block's green fill shade while its own --purple is purple. */
  --purple-solid: var(--pond-purple);
  --purple-rgb: 126,81,255;
  --on-purple:  #FFFFFF;
  --purple-dim: rgba(126,81,255,.13);
  --active-purple-bg: #EEE8FF;
  --active-purple-ink: #4B28AB;
  --action:    var(--pond-purple-fill);
  --on-action: #0A0A0A;
  --r-sm: 10px; --r-md: 14px; --r-lg: 18px; --r-xl: 22px; --r-2xl: 26px;
  /* MST (2026-10-03): the Stat Tracker / Milestones inks, pinned with the lock. */
  --mst-record: #3E5A68; --mst-season: #6B4FD8; --mst-ahead: #0B7A6C;
  --mst-season-rgb: 107,79,216; --mst-untracked: #59646a;
  --gold: #8A6410; --gold-rgb: 138,100,16;
  color-scheme: light;

  /* 🔒 THE COMPLETE INK SET, ADDED 2026-09-04 AFTER `pages-contrast`
     FAILED THIS STYLE. The lock pinned the GROUND and not the INKS, so
     when the viewer's `data-theme` disagreed with the lock every semantic
     colour was still the OTHER theme's -- chosen for a ground that is not
     there. `--danger` on `p.h-error` measured 2.26:1 on login, claim,
     checkout and staff-payments: the doors somebody meets before they have
     an account, and the one where they pay.

     Generated from the matching theme block rather than typed, because 33
     hand-copied values is how the 34th gets missed. */
  --cell-bg: #f2f2f5;
  --chart-1: #6B3AF5;
  --chart-2: #07A608;
  --chart-grid: #D0D0D0;
  --danger: #bd2424;
  --danger-rgb: 201,42,42;
  --dock-accent-text: #1d4ed8;
  --dock-ink: var(--ink);
  --dock-line: var(--line);
  --dock-muted: var(--muted);
  --dock-raised: var(--panel2);
  --dock-surface: var(--panel);
  --dock-surface-2: var(--panel2);
  --hairline: rgba(0,0,0,.07);
  --ice-tag-ink: #0B4F4B;
  --ice-tag-wash: rgba(61, 184, 176, .18);
  /* SAME MOVE AS THE LIGHT THEME ABOVE, AND POND NEEDED IT MORE THAN I FIRST
     SAID. I reported on 09-19 that pond "passes today" because it sets --panel
     and --panel2 to #FFFFFF. That is true of the panels and FALSE of the page:
     --bg is --pond-timber #EFE7DA, where #1A6FCC measured 4.08. #1663B6 is 6.01
     on the panels and 4.90 on the timber. The correction is recorded rather
     than quietly folded in — it is the same mistake this value is being fixed
     for, made once more while fixing it. */
  --info: #1663B6;
  --on-tier: #FFFFFF;
  --on-warn: #FFFFFF;
  --prov-mixed-ink: #04500A;
  --prov-verified-ink: #4B28AB;
  --scrim: rgba(240,240,240,.42);
  --sh-1: 0 2px 8px rgba(0,0,0,.10);
  --sh-2: 0 6px 16px rgba(0,0,0,.14);
  --sh-3: 0 30px 80px rgba(0,0,0,.18);
  /* 🔻 NOT the light theme's #07A608. That value measured 2.65:1 on pond's
     cream ground on `div.lg__card > p.lg__alt > button` -- the "Create an
     account" control, which is the first thing a new member is asked to find.
     A green that fails on this style's OWN ground in BOTH themes is pond's
     palette missing an ink, not a lock leak, so it is fixed here and not by
     the generated pins above. 4.51 on cream, 5.53 on the white card. */
  --success: #067A07;
  --tier-founder-rgb: 162,28,175;
  --tier-manager-rgb: 162,28,175;
  --tier-staff-rgb: 3,105,161;
  --warn: #994D00;
  --warn-ink-on-wash: #7a4700;
  --warn-rgb: 153,77,0;
  --warn-solid: #A85200;
}

/* ============================================================================
   CHROME LOCK — any subtree carrying .h-chrome pins the dark token values,
   whatever the active theme. Put it on the app bar, the bottom nav and the
   desktop sidebar rail. This is what keeps the wordmark + Find circle fixed.
   ========================================================================== */
.h-chrome {
  --bg:     var(--chrome-bg);
  --panel:  var(--chrome-panel);
  --panel2: var(--chrome-panel2);
  --elev:   #242424;
  --line:   var(--chrome-line);
  --line2:  var(--chrome-line2);
  --ink:    var(--chrome-ink);
  --muted:  var(--chrome-muted);
  --faint:  var(--chrome-faint);
  --green:      #07F708;
  --green-rgb:  7,247,8;
  --on-green:   #04120a;
  --green-dim:  rgba(7,247,8,.13);
  --srcGreen:   #07F708;
  --purple:     #7E51FF;
  /* `FND-02`: locked styles pin every token they USE. */
  --purple-solid: #7E51FF;
  --purple-rgb: 126,81,255;
  --on-purple:  #ffffff;
  --purple-dim: rgba(126,81,255,.16);
  color-scheme: dark;
}

/* NOTE: there is no "auto" selector here on purpose. theme.js RESOLVES the
   member's 'auto' preference to a concrete "light"/"dark" value on <html>
   before first paint, so only the two blocks above ever need to exist. */

@media (prefers-reduced-motion: reduce) {
  :root { --t-fast: 0s; --t-med: 0s; --t-sheet: 0s; }
}
