/* ═══════════════════════════════════════════════════════════════════════════════
   RUTHIE UI — TOKEN LAYER  (design plan v1 · docs/design/ruthie-ui-v1.md §2)

   DARK IS THE ONE FACE. Not the default and not the preference — the only face the
   product ships. Operator ruling Q-S242-c4-1 = (iii), "the product says DARK-ONLY in
   the browser's language"; and Q-S242-c3-1 = C, which PARKS light rather than deleting
   it. Sitting W22 · docs/design/ruthie-ui-v1-s22-dark-only.md.

   The declaration is made three times, in three languages, and they agree:
     :root, below, UNCONDITIONAL   no media query and no attribute, so nothing a
                                   viewer's OS or localStorage carries can land them
                                   on a face this product does not ship
     color-scheme: dark            the UA's OWN language — scrollbars, form controls,
                                   date pickers and every widget the page does not
                                   draw itself stop rendering light under dark tokens
     <meta name="color-scheme">    the same sentence in the document, read before CSS

   ★ THE LIGHT BLOCK BELOW IS PARKED, NOT DEAD, AND IT IS NEVER TO BE DELETED. Its
   values are several sittings of measured work and the release is NAMED: a DESIGNED
   light face. To un-park it — give the light block its own [data-theme="light"]
   selector and `color-scheme: light`, restore the theme group in js/header-menu.js,
   restore js/theme-boot.js's storage read. Nothing else. (Sitting W20 measured nine
   sub-AA cells on that face; they are parked WITH it and are what the release fixes
   first.) Until then the same block carries the THEME-INVARIANT tokens — the mark
   pair, the families, the type scale, the radii — so it is the BASE as well as the
   park, and a token the dark set below does not restate resolves there on purpose.

   The palette is derived from Ruthie's own canon, not from a generator:
     --r-accent     her green dress          (assets/ruthie/ruthie-glam-560.png)
     --r-attention  the sunrise key light    (assets/ruthie/candid/rooftop-sunrise.png)
     neutral ramp   the warm haze + the warm-black leather of that same still

   THE RULE THAT MAKES ONE GREEN CARRY TWO JOBS (plan §2.2, law L-UI-4):
     --r-accent  owns FILLS, RULES, FOCUS RINGS and the ACTIVE state — never a number.
     --r-up/down own SIGNED NUMERALS — never a fill.
   Colour is punctuation, not decoration. (voice-ruthie.md, applied to the screen.)
   ═══════════════════════════════════════════════════════════════════════════════ */

/* ── 1. Primitives — the BASE, and the PARKED light face ─────────────────────
   Read the ★ above before touching a value here: this block is not the shipped
   face, and it is not spare bytes either. ─────────────────────────────────── */
:root {
  /* Neutral ramp — warm ON PURPOSE. An undecided grey is the tell of a default
     theme; Linear's 2025 refresh moved "toward a warmer gray that still feels
     crisp, but less saturated" for exactly this reason. (plan §1.3, law L-UI-6) */
  --r-paper:    #FAF8F4;   /* page ground        */
  --r-surface:  #FFFFFF;   /* raised: rows, head */
  /* THE FOURTH GROUND, AND ON THIS FACE IT IS A KNOWN COLLAPSE (sitting 25, §2).
     --r-raised is the step a surface takes when it lifts ABOVE its neighbours. On the
     dark face that is a real slot. Here it is not: --r-surface is already #FFFFFF, the
     top of the ramp, so there is nothing above it — and every value BELOW it (the only
     direction left) is either --r-paper or --r-sunk, both of which mean something else.
     So this face gets the surface's own value, deliberately and named: it is two names
     for one value, which L-UI-72 calls one tier, and it is inert because this face does
     not ship (W22, ruling Q-S242-c4-1 = iii). It is DEFINED rather than omitted because
     the parked face is released by re-parking the block below, and an undefined
     var(--r-raised) is not a fallback — it is an invalid declaration, and four hovers
     would lose their ground entirely. Releasing the light face means re-ramping it
     (surface off white) before this token can mean anything here. */
  --r-raised:   #FFFFFF;   /* = --r-surface. See above: the light ramp has no room. */
  --r-sunk:     #F3F0EA;   /* wells, hover       */
  --r-line:     #E6E1D8;   /* the one hairline   */
  --r-line-2:   #D6CFC2;   /* a rule that must be seen */
  --r-ink:      #17140F;   /* primary text       */
  --r-ink-2:    #56514A;   /* secondary          */
  /* NOT RAISED, and that is a decision (sitting 17). #77716A measures 4.55:1 on
     paper and 4.24:1 on --r-sunk, and the ledger suite asserts that second number
     is BELOW 4.5 as the measured reason the blank's next-line was promoted to
     ink-2. Lifting this grey clears the bar and silently deletes another
     sitting's negative control — the muted register's job in THIS sitting is
     done by the accents, not by moving the neutral ramp. */
  --r-ink-3:    #77716A;   /* muted / labels — 4.8:1 on white (harness-measured) */

  /* Chromatics — THREE roles + two semantics (sitting 17, plan §S17-2).
     Each has ONE job, so a screen can carry all three without competing:
       accent  = STRUCTURE — active/selected state, rules, focus ring
       mark    = IDENTITY  — the venue pill, and EXHAUSTED. Ruthie's sunrise gold.
                 (S242: EXHAUSTED STAYS here. The category ladder below takes the
                  Settings register, and the one hue it may not take is this one —
                  see the dE 6.3 refusal in §3.)
       commit  = THE ACT   — the ONE filled primary action per surface. */
  /* CAUGHT BY THE HARNESS, NOT BY READING IT: the first pass deepened this to
     #0A5233 for chroma and drove the CIELAB distance to --r-up down to dE 16.0 —
     under the 25 the Scanner harness has enforced since sitting 1. A structural
     green and a P&L green that a user can confuse is exactly the failure law
     L-UI-4 exists to prevent, and "more saturated" is not worth it.
     #0E4630: chroma 25.3 (up from 20.2), 10.22:1 on paper, dE 28.8 from --r-up. */
  --r-accent:      #0E4630;   /* Ruthie pine, deepened — chroma 20.2 → 25.3 at 10.22:1 */
  /* In LIGHT the hi tier COLLAPSES onto the accent, deliberately. "Brighter" is
     how a dark theme says "more present"; on paper it would mean LESS contrast,
     which is the opposite of the token's job. One value, one behaviour, and the
     dE guarantee above is inherited rather than re-argued. */
  --r-accent-hi:   #0E4630;
  --r-accent-ink:  #FFFFFF;   /* text ON the accent — 10.84:1 on #0E4630 */
  --r-accent-soft: #E3EDE7;   /* the accent at ~8% over paper, precomputed    */

  /* The MARK. `--r-attention` keeps its name (≈40 call sites read it) and gains a
     second, louder job. The FILL pair is theme-INVARIANT on purpose: an identity
     mark that changes colour when you flip the theme is not an identity mark. */
  --r-attention:   #8A5A08;   /* sunrise gold as INK on light — 5.58:1 (was 4.71) */
  --r-attention-soft: #FBF1DE;
  --r-mark-fill:   #F0B429;   /* the feather. Same on both themes.             */
  --r-mark-ink:    #14120F;   /* on the feather — 10.03:1. Same on both themes. */

  /* THE TELEGRAM TWIN (S243 W22, F-TELEGRAM-INK-SUB-AA-ON-THE-GREY-DARK-GROUND-S242).
     A third-party brand mark is the same shape of thing as the feather: an IDENTITY,
     so the FILL is theme-invariant and brand-true, and the INK is a separate token
     because a brand hex is chosen by someone else's designer and owes this product
     nothing. Two names, because they have two jobs.

     MEASURED ON THE RENDERED DRAWER, against the ground AS PAINTED — the control's own
     12%/8% wash composited onto the drawer's --r-paper, which is what the ink actually
     sits on and is LIGHTER than the paper:
                              link btn (#122026)   code (#121B1E)   black ground
       #0088CC  (shipped)          4.28:1  ✗           4.47:1  ✗      4.95 / 5.11
       #2E7FA6  (the §8 PROPOSAL) 3.73:1  ✗           3.90:1  ✗      4.32 / 4.46
       #3B9FD4  (this)             5.63:1              5.88:1         6.51 / 6.73
     THE PROPOSAL WAS WORSE THAN THE DEFECT, and the reason is worth keeping: it was
     computed against --r-paper instead of the painted ground, so it went DARKER on a
     ground the wash had LIGHTENED. docs/design/ruthie-ui-v1-s21-one-place.md §8 said
     its own numbers were proposals; the browser was asked and it disagreed.

     dE, since a blue near two other blues is a fair question: #3B9FD4 is 25.6 from
     --r-tick-cyan (clears the file's own 25 bar, and that is the one it sits in the
     same drawer as) and 23.1 from --r-commit — under the bar, and accepted with the
     reason: --r-commit is only ever a FILL with dark ink on it and never appears in
     this panel, so the two are never a text colour a reader compares. The shipped
     #0088CC sat at 19.4 from --r-commit, so this MOVES AWAY from that neighbour.

     PARKED-LIGHT CAVEAT: these are invariant, so the parked light face inherits this
     ink and it measures 2.13:1 on the light composite (#DCEAEF) — unusable. That is a
     PARKED number, not a shipped one; the light release owes this pair its own tier
     and must measure it rather than adopt §8's unmeasured #0B5F87. */
  --r-mark-telegram:     #0088CC;   /* the brand mark: fills, washes, borders */
  --r-mark-telegram-ink: #3B9FD4;   /* the same identity, as READABLE INK      */

  /* The COMMIT. Operator-directed (S241 EOD, the marked-up Axiom Deposit button);
     bound to ONE role so it cannot spread. NOT wired into the legacy --blue alias
     below — a legacy name would spray it across forty modules. */
  --r-commit:      #4361EE;   /* the filled primary action. Same on both themes. */
  --r-commit-ink:  #FFFFFF;   /* label on it — 5.02:1                            */
  --r-commit-soft: #E9ECFD;
  --r-commit-line: #C3CBFA;

  --r-up:          #0F7A45;   /* semantic — SIGNED NUMERALS ONLY. 5.09:1 (was
                                 #17864F = 4.34, BELOW the 4.5 bar for 13px text
                                 — a real defect the sitting-17 harness found) */
  --r-down:        #B3261E;   /* semantic — SIGNED NUMERALS ONLY */

  /* THE FAULT ROLE (S242, sitting 20). "We could not READ this" is a third thing:
     it is not a LOSS (--r-down's job, on a signed numeral) and it is not an ordinary
     BLANK (--r-ink-3's em-dash, which means "this position has no take-profit").
     Before this token the two blanks were the SAME INK — measured 4.82:1 on light
     and 5.76:1 on dark for both, i.e. one number for two different truths.
     It carries --r-down's VALUE today and that is deliberate: the deliverable is the
     ROLE, so the day a fault needs its own hue there is one name to move instead of
     every call site. Written as a LITERAL, repeated per theme, never `var(--r-down)`
     — HARNESS-1: a token behind a var() is a token this repo's own auditors, which
     scan tokens.css as TEXT, cannot read. */
  --r-fault:       #B3261E;   /* UNREADABLE — never a signed numeral, never a blank */

  /* ── THE SETTINGS REGISTER — the panel's own palette, lifted (S242, seat W19) ──
     THE OPERATOR, THIS HOUR: "I didn't want settings muted. I wanted the main UI to
     pop in colors like the settings panels was." The Settings drawer is therefore the
     REFERENCE, not a candidate for muting — and its colours were never in the token
     layer at all: eight cluster ticks live as hex literals in js/settings.js and five
     tier pills as hex literals in css/settings.css, which is precisely why the rest of
     the product could drift achromatic while that one panel stayed loud. A palette
     that only one screen can reach is not a palette.

     THE DARK VALUES ARE THE SETTINGS VALUES, VERBATIM — the panel the operator points
     at is byte-identical after this change on the face he uses. The LIGHT values are
     the deepened siblings of the same hues, because every one of the originals
     measures 1.3–2.7:1 as ink on white (harness-measured, this sitting) and a token
     that carries one theme's value into the other is a constant with a variable's
     name. That is the one place Settings moves, and it moves from failing to passing.

     Two families, because they are two vocabularies and the values genuinely differ
     (the tier greens are #00e676; the cluster green is #4ade80):
       --r-tick-*  the cluster hues — the ordered CATEGORY ladder is built from these
       --r-tier-*  the tier hues — every surface that NAMES a tier reads these        */
  --r-tick-cyan:    #0E7490;   /* Entry Gate       · dark #38D2F0 */
  --r-tick-cyan-2:  #0B7285;   /* Trailing Stop    · dark #22D3EE */
  --r-tick-green:   #166534;   /* Risk Control     · dark #4ADE80 */
  --r-tick-amber:   #854D0E;   /* Exits & TP       · dark #FBBF24 */
  --r-tick-teal:    #0F766E;   /* DCA              · dark #34D399 */
  --r-tick-violet:  #6D28D9;   /* Penalty & Bonus  · dark #A78BFA */
  --r-tick-pink:    #BE185D;   /* Market Filters   · dark #F472B6 */
  /* THE ONE SETTINGS VALUE THIS SITTING CHANGES, and it is the one the brief opened.
     Measured on the RENDERED drawer (the tick's own ground), both dark grounds:
       #64748b   3.93:1 grey · 4.41:1 black   — below AA on the face the operator uses
       #94A3B8   7.29:1 grey · 8.19:1 black
     It is the leftover cluster's tick, so it must keep READING as neutral; it is
     lifted one step inside its own family (slate-400 dark, slate-600 light) rather
     than given a hue, and it now clears 4.5:1 on all six grounds. Nothing else in
     Settings changes value — proven site-by-site, not asserted. */
  --r-tick-neutral: #5B6473;   /* More Controls    · dark #94A3B8 (was #64748b) */

  --r-tier-pro:        #1D4ED8;   /* dark #6EA8FF */
  --r-tier-premium:    #047857;   /* dark #00E676 */
  --r-tier-perps:      #9A3412;   /* dark #FFB84A */
  --r-tier-strategy:   #6D28D9;   /* dark #C084FC */
  --r-tier-enterprise: #78600A;   /* dark #FFD700 */

  /* Focus — one ring, everywhere, visible on both themes. */
  --r-focus: var(--r-accent);

  /* Elevation. Warm shadows; a cool shadow on warm paper reads as dirt. */
  --r-shadow-1: 0 1px 2px rgba(23, 20, 15, 0.06);
  --r-shadow-2: 0 4px 16px rgba(23, 20, 15, 0.10), 0 1px 3px rgba(23, 20, 15, 0.06);

  /* Type — one family for words, one for figures. */
  --r-font-ui:  'Space Grotesk', ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
  --r-font-num: 'JetBrains Mono', ui-monospace, SFMono-Regular, 'SF Mono', Menlo, Consolas, 'Liberation Mono', monospace;

  /* Scale — seven steps; the two largest are rare by construction. */
  --r-t-11: 11px;  --r-t-12: 12px;  --r-t-13: 13px;  --r-t-15: 15px;
  --r-t-18: 18px;  --r-t-22: 22px;  --r-t-28: 28px;

  --r-track-label: 0.08em;   /* uppercase utility tracking */

  /* ── THE NAMING REGISTER — capitals for the EYE, never for the EAR ──────────
     Sitting 33 measured this in Chrome's own accessibility tree, four candidates,
     one page: `text-transform: uppercase` changes the CHARACTERS the engine
     speaks (a label typed `Collateral` is NAMED `COLLATERAL`, and VoiceOver reads
     a short all-caps string as an initialism — `ADD` becomes "A.D.D."), while
     `font-variant-caps` changes only the GLYPHS and leaves the name `Collateral`.
     AccName 1.2 is silent on which happens, so it is measured, not reasoned.

     Every element that IS an accessible name — the label a `role="group"` reading
     points `aria-labelledby` at — wears THIS register and never `text-transform`.
     A label that only decorates may keep the transform; the register is about the
     ear, and an element nothing points at has no name to protect. (L-UI-108.)

     THE SIZE IS PART OF THE TOKEN. Small caps are drawn at x-height, so 11px
     small caps read visibly smaller than 11px full capitals; 12px was measured
     against the shipped face as the step that holds the label's line box while
     the tracking comes down a notch because small caps are already open. */
  --r-t-name:     12px;
  --r-track-name: 0.06em;
  --r-radius:   6px;
  --r-radius-s: 4px;

}

/* ── 2. Primitives — DARK. THE ONE FACE, UNCONDITIONALLY ─────────────────────
   A warm charcoal, NOT a blue-black. That one decision is what stops the dark theme
   reverting to the "AI trading dashboard" look the operator rejected.

   THIS BLOCK USED TO SIT BEHIND `@media (prefers-color-scheme: dark)` AND A
   `:not([data-theme="light"])` GUARD, AND THAT PAIR WAS THE DEFECT
   (F-SYSTEM-THEME-RESOLVES-TO-THE-UNSUPPORTED-LIGHT-FACE-S242). Measured in a real
   browser at W22, BEFORE the change: a light-preferring OS resolved --r-paper to
   #FAF8F4 on app.html, index.html AND 404.html — every viewer whose machine says
   "light" was handed the face the operator ruled unsupported — and a stale 'light'
   in localStorage did the same on a dark machine. Two conditions, two doors onto a
   face that does not exist. Both are gone: this block is (0,1,0) and follows the
   parked one, so it wins on ORDER — which is load-bearing here, and said out loud
   rather than assumed. */
:root {
  /* THE UA'S OWN LANGUAGE. Without this the engine paints its scrollbars, its
     <select> popups, the focus ring on a form control and the autofill wash from the
     LIGHT system palette while every token around them is dark. Measured before the
     change: `color-scheme: normal` on all three documents. A page cannot declare
     itself dark in CSS and stay silent to the UA. */
  color-scheme: dark;

  --r-paper:    #14120F;
  --r-surface:  #1C1917;
  /* THE FOURTH GROUND — minted in prose at sitting 24, SIZED here (sitting 25, §2).
     The ramp shipped three grounds and the bridge wanted four: `--bg-elevated` had to
     serve both eighteen genuinely-raised rest surfaces (correctly --r-surface) and a
     handful of hovers that needed a STEP, and one alias cannot be two tiers.

     Sized against the product's OWN steps, not by eye. Measured in Lab (CIE76) on this
     face: paper->surface 3.55, surface->sunk 2.26, sunk->line 4.98, surface->line 7.14.
     Three separations have to hold at once — the lift must be seen, the fourth name must
     not collapse into --r-sunk (L-UI-72), and a hairline drawn on it must survive — and
     on the ramp's own hue geometry (R-G 3..5, G-B 2..3, the warm charcoal) #262220 is
     the point that MAXIMISES the smallest of the three:
         surface -> raised   4.59   (louder than page->card, quieter than the hairline)
         raised  vs sunk     2.60   (> 2.26, the smallest step this product ships)
         raised  vs line     2.61   (the hairline lives)
         raised  vs line-2   9.18 · ink on raised 13.63:1 · ink-3 on raised 5.19:1
     Every neighbouring candidate trades one of those below 2.26. The interval between
     --r-surface and --r-line is only 7.14 dE long and --r-sunk already sits in it, so
     this is a tight ramp, not a generous one — which is the reason the mint had to be a
     measurement and not a hex somebody liked. Harness: TheFloor/harnesses/verify-ruthie-raised.js. */
  --r-raised:   #262220;
  --r-sunk:     #211D1A;
  --r-line:     #2C2724;
  --r-line-2:   #3B3531;
  --r-ink:      #F2EEE7;
  --r-ink-2:    #A9A29A;
  --r-ink-3:    #9A938A;

  /* F-ACCENT-INK-FAILS-CONTRAST-IN-DARK-S236, fixed at sitting 4.
     The dark accent is a MID-LIGHT green, so the ink on it must go DARK — the
     light theme's answer inverted, not reused.
     Sitting 17 then RAISED it: #3F9C74 measured 5.54:1 on paper / 5.18:1 on a
     row, against a Settings modal whose own accent marks run 9–13:1. That gap
     is the "muted and flat" the operator named, and it was measurable.
       #3F9C74  5.54 paper · 5.18 row   ← three sittings
       #4FB88A  7.62 paper · 7.12 row   ← this, and dE 29.6 from --r-up so the
                                          two greens still cannot be confused
     A token that carries one theme's value into the other is not a token, it is a
     constant with a variable's name. */
  --r-accent:      #4FB88A;
  --r-accent-hi:   #6BCFA6;   /* 9.88:1 — the bright tier, where the neon was */
  --r-accent-ink:  #14120F;   /* 7.62:1 on the accent, 9.88:1 on the hi tier   */
  --r-accent-soft: #16332A;

  --r-attention:   #F0B429;   /* the feather as ink — 10.03:1 (was #D9A441, 8.31) */
  --r-attention-soft: #2A2114;
  --r-commit-soft: #1A1E3C;
  --r-commit-line: #2E3670;

  --r-up:          #4ADE80;
  --r-down:        #F87171;
  --r-fault:       #F87171;   /* the fault ROLE — see the note on :root */

  /* THE SETTINGS REGISTER, dark — VERBATIM the values js/settings.js and
     css/settings.css ship today. The reference panel does not move on this face. */
  --r-tick-cyan:    #38D2F0;
  --r-tick-cyan-2:  #22D3EE;
  --r-tick-green:   #4ADE80;
  --r-tick-amber:   #FBBF24;
  --r-tick-teal:    #34D399;
  --r-tick-violet:  #A78BFA;
  --r-tick-pink:    #F472B6;
  --r-tick-neutral: #94A3B8;   /* the one lift — #64748b measured 3.67:1 on a row */

  --r-tier-pro:        #6EA8FF;
  --r-tier-premium:    #00E676;
  --r-tier-perps:      #FFB84A;
  --r-tier-strategy:   #C084FC;
  --r-tier-enterprise: #FFD700;

  /* THE COMMIT, RE-VALUED FOR THIS FACE. `#4361EE` is a light-theme blue: on the
     black ground its own fill measures 4.18:1 against paper and the button reads as
     a dark rectangle — the "muted" the operator named, on the one control that is
     supposed to be the loudest thing on the surface. The dark face takes Settings'
     own blue and inverts the ink, exactly as --r-accent-ink already does:
       #4361EE fill / #FFFFFF ink   5.02:1   ← light, unchanged
       #6EA8FF fill / #14120F ink   8.71:1   ← here
     The role is untouched: ONE filled primary action per surface. */
  --r-commit:      #6EA8FF;
  --r-commit-ink:  #14120F;

  --r-shadow-1: 0 1px 2px rgba(0, 0, 0, 0.40);
  --r-shadow-2: 0 4px 16px rgba(0, 0, 0, 0.50), 0 1px 3px rgba(0, 0, 0, 0.40);
}

/* The EXPLICIT block. It no longer 'beats the system preference' — there is no
   system preference left to beat, and no light face for it to beat it TO. It is kept,
   byte-identical, for two live reasons: js/theme-boot.js still stamps
   data-theme="dark" so that every JS reader of the attribute resolves the SHIPPED
   face rather than falling through to prefers-color-scheme; and this block is half of
   the mechanism the parked light face is released through. Its values are the same
   values as the face above, deliberately — a duplicate that DISAGREED would be a
   second face nobody chose. */
:root[data-theme="dark"] {
  --r-paper:    #14120F;
  --r-surface:  #1C1917;
  --r-raised:   #262220;   /* see the sizing note in the face above */
  --r-sunk:     #211D1A;
  --r-line:     #2C2724;
  --r-line-2:   #3B3531;
  --r-ink:      #F2EEE7;
  --r-ink-2:    #A9A29A;
  --r-ink-3:    #9A938A;

  --r-accent:      #4FB88A;   /* see the note in the media-query block above */
  --r-accent-hi:   #6BCFA6;
  --r-accent-ink:  #14120F;
  --r-accent-soft: #16332A;

  --r-attention:   #F0B429;
  --r-attention-soft: #2A2114;
  --r-commit-soft: #1A1E3C;
  --r-commit-line: #2E3670;

  --r-up:          #4ADE80;
  --r-down:        #F87171;
  --r-fault:       #F87171;   /* the fault ROLE — see the note on :root */

  /* THE SETTINGS REGISTER, dark — VERBATIM the values js/settings.js and
     css/settings.css ship today. The reference panel does not move on this face. */
  --r-tick-cyan:    #38D2F0;
  --r-tick-cyan-2:  #22D3EE;
  --r-tick-green:   #4ADE80;
  --r-tick-amber:   #FBBF24;
  --r-tick-teal:    #34D399;
  --r-tick-violet:  #A78BFA;
  --r-tick-pink:    #F472B6;
  --r-tick-neutral: #94A3B8;   /* the one lift — #64748b measured 3.67:1 on a row */

  --r-tier-pro:        #6EA8FF;
  --r-tier-premium:    #00E676;
  --r-tier-perps:      #FFB84A;
  --r-tier-strategy:   #C084FC;
  --r-tier-enterprise: #FFD700;

  /* THE COMMIT, RE-VALUED FOR THIS FACE. `#4361EE` is a light-theme blue: on the
     black ground its own fill measures 4.18:1 against paper and the button reads as
     a dark rectangle — the "muted" the operator named, on the one control that is
     supposed to be the loudest thing on the surface. The dark face takes Settings'
     own blue and inverts the ink, exactly as --r-accent-ink already does:
       #4361EE fill / #FFFFFF ink   5.02:1   ← light, unchanged
       #6EA8FF fill / #14120F ink   8.71:1   ← here
     The role is untouched: ONE filled primary action per surface. */
  --r-commit:      #6EA8FF;
  --r-commit-ink:  #14120F;

  --r-shadow-1: 0 1px 2px rgba(0, 0, 0, 0.40);
  --r-shadow-2: 0 4px 16px rgba(0, 0, 0, 0.50), 0 1px 3px rgba(0, 0, 0, 0.40);
}

/* ── 2c. THE GROUND — the operator's open question, shipped as two options ─────
   ONE attribute on <html>. Nothing but the ground and its ground-relative tints
   move; every ink, accent, mark and semantic value is shared, so the review
   compares exactly one variable and the contrast table below stays true for both.

     no attribute        → the grey ground (the committed default, Q-S242-c1-1)
     data-ground="grey"  → the same, stated explicitly so T0 can flip BACK
     data-ground="black" → a true black ground, Axiom's

   SPECIFICITY, RE-DERIVED AT W22 BECAUSE IT MOVED. These two used to be (0,3,0):
   `:root:not([data-theme="light"])[data-ground]` — :root (0,1,0) + the :not()
   argument's own (0,1,0) + the attribute (0,1,0). With the light guard gone they
   are (0,2,0), which still beats the (0,1,0) face above but now merely TIES with
   `:root[data-theme="dark"]`. They therefore have to sit AFTER it, and they do.
   That is an ORDER dependency where there was none, and it is written down here
   rather than left for someone to rediscover by moving a block. */
:root[data-ground="grey"] {
  --r-paper:   #14120F;   --r-surface: #1C1917;
  --r-raised:  #262220;   --r-sunk:    #211D1A;
  --r-line:    #2C2724;   --r-line-2:  #3B3531;
  --r-accent-soft: #16332A;
  --r-attention-soft: #2A2114;
  --r-commit-soft: #1A1E3C;
}
:root[data-ground="black"] {
  --r-paper:   #000000;   --r-surface: #0C0B0A;
  /* The black ground is a DIFFERENT ramp, so it gets its own fourth value: one hex over
     two surfaces is two different lifts. Sized to the SAME perceived step as the grey
     ground's (4.65 dE vs its 4.59) so a hover does not feel twice as strong here —
     surface->raised 4.65, raised vs sunk 2.44 (> its 2.21 smallest step), raised vs
     line 8.70. The black ramp reuses grey's --r-line, which is why it has more room. */
  --r-raised:  #191615;   --r-sunk:    #131110;
  --r-line:    #2C2724;   --r-line-2:  #3B3531;
  --r-accent-soft: #0E241D;
  --r-attention-soft: #1E1809;
  --r-commit-soft: #12152E;
}
:root[data-theme="dark"][data-ground="grey"] {
  --r-paper:   #14120F;   --r-surface: #1C1917;
  --r-raised:  #262220;   --r-sunk:    #211D1A;
  --r-line:    #2C2724;   --r-line-2:  #3B3531;
  --r-accent-soft: #16332A;
  --r-attention-soft: #2A2114;
  --r-commit-soft: #1A1E3C;
}
:root[data-theme="dark"][data-ground="black"] {
  --r-paper:   #000000;   --r-surface: #0C0B0A;
  /* The black ground is a DIFFERENT ramp, so it gets its own fourth value: one hex over
     two surfaces is two different lifts. Sized to the SAME perceived step as the grey
     ground's (4.65 dE vs its 4.59) so a hover does not feel twice as strong here —
     surface->raised 4.65, raised vs sunk 2.44 (> its 2.21 smallest step), raised vs
     line 8.70. The black ramp reuses grey's --r-line, which is why it has more room. */
  --r-raised:  #191615;   --r-sunk:    #131110;
  --r-line:    #2C2724;   --r-line-2:  #3B3531;
  --r-accent-soft: #0E241D;
  --r-attention-soft: #1E1809;
  --r-commit-soft: #12152E;
}

/* ── 3. The bridge — legacy names re-pointed at the new tokens ────────────────
   ~40 JS modules and ~200KB of CSS read the old variable names. Re-pointing them
   here means the whole application inherits the new palette immediately, and each
   surface can then be restyled properly on its own sitting. This is a bridge with
   an end date, not an architecture: as each screen is rebuilt it stops reading
   these and reads --r-* directly.

   `body[data-skin]` (0,1,1) is carried alongside `:root` so this beats
   skins/themes.css's [data-skin="scanner"] (0,1,0) on SPECIFICITY, not just order. */
:root,
body[data-skin] {
  --bg-base:       var(--r-paper);
  --bg-surface:    var(--r-surface);
  --bg-elevated:   var(--r-surface);
  --bg-input:      var(--r-sunk);
  --bg-primary:    var(--r-paper);
  --bg-secondary:  var(--r-surface);
  --bg-card:       var(--r-surface);
  --bg-card-hover: var(--r-sunk);
  --bg-sidebar:    var(--r-paper);
  --bg-header:     var(--r-surface);

  --border-subtle:  var(--r-line);
  --border-default: var(--r-line);
  --border-strong:  var(--r-line-2);
  --border:         var(--r-line);
  --border-light:   var(--r-line-2);

  --text-primary:   var(--r-ink);
  --text-secondary: var(--r-ink-2);
  --text-muted:     var(--r-ink-3);

  /* One accent. The legacy aliases collapse onto it — this is where three of the
     four old accent hues (cyan, violet, neon-green chrome) stop existing.

     ★ `--blue` STAYS pointed at the accent. Sitting 17 introduced a real blue
     (`--r-commit`) and the tempting one-liner was `--blue: var(--r-commit)` — which
     would have sprayed a money-coloured button across every legacy call site in
     forty modules in a single character. An accent is spent like money; a bridge
     alias is not a place to spend it. `--r-commit` is written out by hand at each
     of its (currently three) sites and nowhere else. */
  --accent:      var(--r-accent);
  --accent-glow: color-mix(in srgb, var(--r-accent) 26%, transparent);
  --accent-dim:  var(--r-accent-soft);
  --accent-sol:   var(--r-accent);
  --accent-pulse: var(--r-accent);
  --blue:         var(--r-accent);
  --blue-laser:   var(--r-accent);
  --purple:       var(--r-accent);
  --cyan:         var(--r-accent);

  /* Semantics stay semantic. */
  --green:  var(--r-up);
  --red:    var(--r-down);
  --yellow: var(--r-attention);

  /* ── THE CATEGORY LADDER (S242, seat W19) ────────────────────────────────
     L-UI-5 read "an ordered scale is one column encoded in WEIGHT, not five fills",
     and the ladder was collapsed onto the ink ramp. The operator has now looked at
     the result beside the Settings drawer and called it what it is. The ladder comes
     back as colour — but as ORDERED colour, taken from the register above, so it is
     the same seven hues the Settings panel already speaks rather than a new palette.

     WHICH HUE GOES WHERE IS A MEASUREMENT, NOT A PREFERENCE. tokens.css:55 records
     the Scanner harness's bar: two roles a reader can confuse must sit >= dE 25
     apart in CIELAB. Run over the candidate ladder (see the sitting's dE matrix) it
     REFUSED the obvious assignment twice:
       WATCH=#fbbf24 vs EXHAUSTED=#f0b429 -> dE 6.3.  Two states that mean OPPOSITE
         things (keep looking / the agent's own ceiling said no) cannot be one amber.
         EXHAUSTED keeps the canon gold, so WATCH moved — to the ENTRY GATE cyan,
         which is what "watching at the gate" is, and lands dE 106 away.
       STRONG_BUY=#4ade80 vs BUY=#34d399 -> dE 18.8, and BUY vs --r-accent -> dE 15.0.
         Kept anyway, and this is the deliberate half: those two mean the SAME thing
         one rung apart, and .sc-read already separates them by weight (700 vs 600).
         The bar exists to stop a reader confusing roles that DISAGREE.
     NEUTRAL takes no hue at all. "No call" is the absence of a signal, and painting
     it would make the quiet rows the loudest thing in a full table. */
  --r-cat-strong:    var(--r-tick-green);    /* STRONG_BUY */
  --r-cat-buy:       var(--r-tick-teal);     /* BUY — the same family, one rung down */
  --r-cat-watch:     var(--r-tick-cyan);     /* WATCH — the Entry Gate hue */
  --r-cat-neutral:   var(--r-ink-3);         /* NEUTRAL — deliberately not a hue */
  --r-cat-avoid:     var(--r-down);          /* AVOID — the semantic, not a tick */
  --r-cat-exhausted: var(--r-attention);     /* EXHAUSTED — the mark keeps its job */
  --r-cat-pinned:    var(--r-tick-violet);   /* watchlist — CLAUDE.md's "purple" */

  /* The legacy names survive for legacy call sites and now resolve to the ladder,
     so a 2016-era `--color-avoid` and a 2026 `.sc-read.cat-AVOID` are one value. */
  --color-strong-buy: var(--r-cat-strong);
  --color-buy:        var(--r-cat-buy);
  --color-watch:      var(--r-cat-watch);
  --color-neutral:    var(--r-cat-neutral);
  --color-avoid:      var(--r-cat-avoid);
  --color-exhausted:  var(--r-cat-exhausted);

  --card-radius: var(--r-radius);
}

/* ── 4. Ground + type defaults ───────────────────────────────────────────────── */
html, body {
  background: var(--r-paper);
  color: var(--r-ink);
  font-family: var(--r-font-ui);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

/* Every figure that can sit in a column is tabular. Declared here AND measured in
   the harness — `font-variant-numeric` is a request to the font, and a family
   without `tnum` fails it silently (plan §1.4, law L-UI-3). */
.mono,
.r-num,
[class*="-val"],
[class*="-price"],
input[type="number"] {
  font-variant-numeric: tabular-nums lining-nums;
}

/* Visually hidden, still read by assistive tech. */
.sr-only {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* ── 5. Focus — one visible ring on everything interactive ───────────────────── */
:where(a, button, input, select, textarea, summary, [tabindex]):focus-visible {
  outline: 2px solid var(--r-focus);
  outline-offset: 2px;
  border-radius: var(--r-radius-s);
}

/* ── 6. Motion ───────────────────────────────────────────────────────────────
   Degrade-to-SHOWN: an unsupported/opted-out engine must land on "everything
   visible", never on "everything hidden". A media query adds no specificity, so
   any state selector this must override is repeated, not assumed. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
    scroll-behavior: auto !important;
  }
  .token-card,
  .token-card.STRONG_BUY,
  [data-skin="scanner"] .token-card.STRONG_BUY {
    animation: none !important;
    opacity: 1 !important;
  }
}

/* ── 7. Retired decoration ───────────────────────────────────────────────────
   The radar-ping and the glow-pulse were decoration-motion on a data element
   (Floor laws 1 and 5) and the loudest thing on the Scanner. Killed at the source
   rather than overridden per-rule, so nothing can re-inherit them. */
[data-skin="scanner"] .token-card.STRONG_BUY,
.token-card.STRONG_BUY {
  animation: none;
  box-shadow: none;
}
[data-skin="scanner"] .token-card.STRONG_BUY::after { content: none; }
