/* Vibe Check — canonical design tokens (web). Apple-grade dark ramp.
   ONE source of truth: JUCE (VibeColours.h) and iOS (colour assets) are generated to match.
   Mirrored verbatim in web/player/tokens.css — keep the two identical. */
:root{
  /* THE BROWSER'S OWN CHROME IS PART OF THE DARK RAMP (Sid, Windows arc). Undeclared, the UA assumes
     a light page and draws its furniture for one: light scrollbars, light form internals, a light
     autofill wash and focus rings picked for white. macOS hid nearly all of it behind overlay
     scrollbars, so it went unnoticed until the studio was opened on Windows and the standing grey
     bar appeared. One declaration fixes the whole class — scrollbars, inputs, focus, autofill —
     without styling a single control, which is what keeps it inside the don't-style-scrollbars
     ruling rather than around it. */
  color-scheme:dark;

  /* surfaces (elevation ramp) */
  --base:#0C0C0E;          /* page / app canvas */
  --surface-1:#1C1C1E;     /* cards, players, list rows */
  --surface-2:#2C2C2E;     /* controls, icon tiles, inputs */
  --surface-3:#3A3A3C;     /* topmost stacked controls (rare) */
  --separator:#38383A;
  --hairline:rgba(255,255,255,.10);

  /* text — translucent white, adapts to any surface */
  --text:#ffffff;
  --text-2:rgba(255,255,255,.60);   /* lift to .70 over a hero image */
  --text-3:rgba(255,255,255,.30);

  /* the approved player redesign's surfaces and inks. The mock uses SOLID greys where we used
     white-at-alpha; solid is what it approved, so these are read straight from it. */
  --surface-card:#1A1B1D;   /* the player card                     (was #1C1C1E) */
  --surface-box:#232427;    /* the sender box                      (was #2C2C2E) */
  --txt-lead:#C3C5C9;       /* "sent via" — the footer's lead line */
  --txt-muted:#8E9196;      /* "sent by", expiry, the footer tagline */
  --ink-invert:#101112;     /* dark ink on an inverted (white) chip */
  --txt-bright:#F3F4F5;     /* the wordmark, card and footer — not pure white in the mock */
  /* EXPIRY IS INFORMATION, NOT AN ALARM (Sid, 2026-08-13). The expiry value's red is its OWN named
     ink, deliberately softer than --red, and the two must never be confused: --ink-expiry is the
     <24h urgency VALUE as text, --red #FF453A is destructive actions ONLY (delete buttons, confirms).
     --red-ink is the former name, kept as an alias so nothing that still says it silently changes. */
  --ink-expiry:#E0403A;
  --red-ink:var(--ink-expiry);

  /* ROW STATE SHADES (fc). Both are rgba of --surface-2 (#2C2C2E) so they sit ON the card rather than
     replacing it, and hover is a FULL STEP below playing — a row being played must never read as a
     row merely pointed at. Tints never stack: a playing row does not also take the hover shade. */
  --row-playing:rgba(44,44,46,.6);
  --row-hover:rgba(44,44,46,.28);

  /* WAVEFORM TREATMENT (fc). The played blue was a hardcoded #3B74F0 pair inside wave.js — the
     pre-split surface blue, and a LIGHTER stroke over a darker fill, which is why the played half
     never quite matched the controls beside it. One blue, from the token: fill at 45%, stroke solid
     at 1.4px. Declared here so the canvas reads the same values the CSS does. */
  --wave-stroke:rgba(255,255,255,.45);
  --wave-fill:rgba(255,255,255,.13);
  --wave-centreline:rgba(255,255,255,.18);
  --wave-played:#E46F40;
  --wave-playhead:rgba(255,255,255,.9);

  /* one tint — everything tappable */
  /* TWO DECLARED BLUES (Sid, 2026-07-31), superseding the one-blue law.
     --blue is the SURFACE blue: fills, buttons, focus borders, the hero. Read from the approved
     mock's download pill (#2F6BFA, was #3B74F0) and unchanged.
     --blue-text is the TEXT blue, and it exists because the references consistently render small
     blue TEXT one step lighter than the surfaces they sit beside. That is not drift: a fill carries
     its own weight of colour and a 13px label does not, so the same hex reads heavier as a surface
     and thinner as type. One blue was the right rule while everything blue was a surface; the
     moment the design uses blue for both, one value cannot serve both jobs.
     The split is by ROLE, not by size: buttons keep --blue even when their label is the blue part
     (the player's outline actions), because a button is a surface whether or not it is filled. */  /* read from the approved mock's download pill: #2F6BFA (was #3B74F0). This is the PRODUCT-WIDE
     tint, so the studio moves with it — one blue, not a player blue and a studio blue. hover and
     pressed are shifted by the same delta so the three keep their relationship. */
  /* THE ACCENT IS ORANGE (Sid, 30 Oct 2026, with the new logo): #E46F40, the logo's triangle. The names stay
     --blue*; the four values below and every tint of them on the site moved together, each step keeping
     its place against the base (the same hue offset, saturation ratio and lightness step, in HSL). */
  --blue:#E46F40;
  --blue-text:#E5714B;      /* small blue TEXT only — never a fill, never a border */
  --blue-hover:#EA7C50;
  --blue-pressed:#CD5F32;

  /* semantic only — small, sparing, never decorative */
  --red:#FF453A;     /* live / record / destructive */
  --green:#30D158;   /* active / connected / ready / copied — status, badges, dots */
  --green-on:#2F8F4E;/* a SWITCH in its on state. Deeper than the status green on purpose: a 29x16
                        pill is a filled surface, and #30D158 at that area reads as an alert rather
                        than a setting. Read off the studio's ruled control. */
  --orange:#FF9F0A;  /* caution — an expiry's final day only */

  /* the player glass card, over the sender's wallpaper */
  --glass:rgba(0,0,0,.72);
  --well:rgba(255,255,255,.07);   /* waveform well */

  /* type */
  --ui:'Inter',system-ui,-apple-system,BlinkMacSystemFont,sans-serif;
  --mono:'Roboto Mono',ui-monospace,SFMono-Regular,monospace;

  /* ---- THE SHARED SCALE (moved here 2026-08-01) ------------------------------------------------
     These lived TWICE — once in studio.css, once in player.css — with a comment in each warning
     that "the two files declare the token separately, so they must be moved together or they drift
     apart silently". That is not a scale the player inherits, it is a scale the player happens to
     agree with, and the only thing holding the two in step was a comment. Measured on live before
     the move: every shared token computed identically on the studio and both players at 1440 and
     390, so consolidating here is inert by construction — and from now on the player cannot drift
     because there is nothing left to drift from.
     What stayed behind in studio.css: the studio's OWN voices (--t-rowtitle/--t-rowdays/--t-subrow
     and the --t-panel* set). Those are surfaces the player does not have. What moved here: the four
     type voices, their tracking, the weights, the five spacing steps, and the panel's surface/ink
     colours — colours belong in this file with the rest of the ramp, and the player's demoted
     download pill now reads three of them. */

  /* SIZES — four voices. --t-micro is a declared FIFTH step for exactly one thing: the auto-play
     label inside a 38px control slot matched across three transports. */
  --t-title:22px; --t-body:14.5px; --t-caption:13px; --t-mono:12.5px; --t-micro:8px;
  /* HELPER / STATUS — the meta size step below a label (redesign tokens: "helper/status 11.5px 400
     #8E9196"). It existed as a literal 11.5 in a dozen places and as nothing in the scale, which is
     how the auto-play label drifted to three different values at once: the shared rule said
     --t-control (12), the studio's filter row said --t-caption (13, and 12 on phones), and the board
     drew 11. Ruled 11.5 (Sid, 2026-08-13). Flat, not stepped: the redesign rules helpers at one
     size on both platforms. */
  --t-helper:11.5px;

  /* RESTING TITLE INK. The redesign names it (design/redesign-2026-08/tokens.css: "--ink-title
     #E8E9EA — resting row titles 15/600; sub-rows 15/400"), and the sheet's track rows and its
     "delete this track" were already written against var(--ink-title) — against a name that existed
     nowhere. An undefined custom property does not fall back to a sensible default; it makes the
     declaration invalid at computed-value time, so both inherited plain white and the ruled ink was
     never once rendered. Caught by verify-collection the first time it was pointed at the board. */
  --ink-title:#E8E9EA;
  /* TRACKING. -0.015em is a METRIC COMPENSATION, NOT a value copied from a reference — do not
     "correct" it back to -0.005em in a later match-the-reference pass. The approved references
     render in Helvetica Neue; this product is ruled to Inter, which is measurably wider at the same
     size and the same declared tracking. Measured 2026-07-30 on identical strings: +6.6% at weight
     400, +4.3% at 500, +2.1% at 600, +3.5% at 700. Every role's DECLARED tracking already matched,
     so the extra width is the family's metrics and tracking is the only lever; Sid ruled ONE global
     step over per-role hacks. -0.015em because it is already a declared step (--t-title-ls), not a
     new number. It lands the 600/700 roles within ~1.7% and narrows the 400-weight roles without
     closing them: one value cannot do better, because the gap varies with weight. */
  --t-tracking:-0.015em;
  --t-title-ls:-0.015em;           /* the one large voice tightens as it grows */
  --t-mono-ls:var(--t-tracking);   /* ONE mono treatment, product-wide */
  --t-logo-ls:0.34em;              /* the card wordmark */
  --t-logo-foot-ls:0.26em;         /* the footer wordmark */
  --t-w-reg:400; --t-w-med:500; --t-w-semi:600; --t-w-bold:700;

  /* the tracklist row title, on every surface that lists a collection's tracks: the studio list's
     expanded sub-rows and, as of Sid's inheritance ruling (2026-08-01), both players' tracklists.
     Held FLAT at 390 — Sid's earlier ruling that phones do not step this voice down still stands,
     and it is why it is its own token rather than --t-body. */
  --t-subrow:14.5px;

  /* the label under a control (auto-play). Flat at both widths, like --t-subrow: it sits in a
     fixed-size control slot matched across three transports, so stepping it down would break the
     geometry rather than the reading. Declared because no other step is 12px flat. */
  --t-control:12px;

  /* THE WAVEFORM WELL. --well-h is the ruled height (Sid, 2026-08-01); the tiers below it are the
     SHORT-VIEWPORT compaction, a different axis from the ruling — the card is capped to the window
     and something has to give. The values are the player's measured tiers, promoted to tokens so
     the studio can compact identically instead of not at all. */
  --well-h:120px;
  /* THE COMPACT STATE (Sid, 2026-08-02) — a NAMED state of the same shared component, not a
     player-only override. It applies wherever a collection tracklist shares the card, because
     there the list is the most important thing on it and the ceremony is what gives. 84 is not a
     new number: it is the tier the short-viewport scale already declares. See .st-card.has-list in
     sendbox.css, which takes the SMALLER of this and whatever tier the window is on. */
  --well-h-tight:84px;

  /* THE TRANSPORT, both scales declared side by side (Sid, 2026-08-02) — a collection's compact
     scale is a VALUE here, not a second hand-kept copy of the rules. verify-shared-controls
     asserts both, so neither can drift the way the two auto-play switches once did. */
  --hero:88px;   --hero-tight:48px;
  --skip:44px;   --skip-tight:32px;
  --heroGlyph:34px; --heroGlyph-tight:20px;
  --skipGlyph:19px; --skipGlyph-tight:14px;

  /* SPACING — one 6-rhythm, five steps. Nothing sits between steps. */
  --sp-tight:6px; --sp-close:12px; --sp-block:18px; --sp-air:24px; --sp-wide:30px;

  /* the panel's surfaces and inks, read headless off Acetate_Collection.html (2026-07-31) */
  --surface-inset:#202124;     /* inset ground: track panel, fields, url row, quiet buttons */
  --line-inset:#2C2E31;        /* its 1px edge, and the separator between track rows */
  --line-well:#2A2C2F;         /* the well's own window edge */
  /* THE WELL'S GROUND (Sid, 2026-08-15). The well used to have no ground: it was a blurred COPY of
     the wallpaper, positioned by JS to line up with the real one behind the card. It is now painted,
     flat and opaque, referencing nothing outside itself.
     THIS VALUE IS NOT A NEW PICK. It is rgb(10,11,14) — the exact blue-black the ≤480 in-well scrim
     was already using to drag the well "near-neutral dark over ANY wallpaper". The old construction
     spent a JS alignment pass, a 12px blur, a 40px overscan and a compositing layer arriving at an
     approximation of this colour; painting it is the same destination without the machinery.
     Why it had to go rather than be tuned: the copy only misaligns on WebKit, so three passes fixed
     it by eye on a phone and every harness since agreed it was fine, because every harness was
     Chrome. Reproduced on real Mobile Safari 2026-08-15 and clean in Chrome on the same page,
     minutes apart. An opaque ground has no alignment to lose. */
  --well-ground:#0A0B0E;
  --line-card:#26282B;         /* the card's edge */
  --ink-value:#F3F4F5;         /* a value the producer typed or chose */
  --ink-quiet:#C3C5C9;         /* placeholders, durations, the url, time chips */
  --red-del:#E0403A;           /* delete link — the reference's red, softer than the system #FF453A */
  --row-line:#2A2C2F;          /* the studio list's divider ink: an OPAQUE rgb, not --hairline,
                                  which composites to ~rgb(51,51,53) and reads visibly lighter */

/* ================================================================================================
   THE FIVE CURATED WALLPAPER ACCENTS — ruled constants, ONE declaration each.
   ================================================================================================
   These are (fi)'s measured values and they are NOT to be recomputed, re-extracted or nudged: each
   keeps its picture's measured hue and saturation, raised in LIGHTNESS only to 3.73:1 on #1C1C1E,
   which is parity with the #2F6BFA they replaced and deliberately NOT a WCAG floor, because a higher
   floor would make every accent louder than what shipped before.

   WHY THEY MOVED HERE from player.css (2026-08-22): the settings screen has to paint all five at
   once, on its swatch dots, and the studio does not load player.css. The alternative was a second
   copy of five ruled constants in studio.css, which is the exact shape this codebase has paid for
   again and again. tokens.css is the one stylesheet BOTH the listener and the studio load, so it is
   the only place they can be declared once. player.css still owns which class takes which accent and
   still carries the reasoning per picture; it now reads these names instead of repeating the hex.

   THE ACCENT IS LISTENER-PAGE ONLY. The studio uses these five in exactly one role, the swatch dot
   that says which picture a tile is, and nowhere else — no button, no text, no border. The studio
   keeps --blue and --blue-text. */
  --accent-turntable:#D43E55;   /* the neon behind the deck · hue 351 · THE DEFAULT */
  --accent-moog:#D44135;        /* the patch cables · hue 4 */
  --accent-ssl:#A46742;         /* the desk and its backlights · hue 23 */
  --accent-blacksand:#767676;   /* a MONOCHROME accent for a monochrome file (R=G=B in every pixel) */
  --accent-whitesand:#8C7154;   /* the warm cast of the frame · hue 32 */
}

/* ONE SCALE EVERYWHERE (Sid, 2026-08-14). The phone step-down is GONE — it used to read:
     @media (max-width:480px){ :root{ --t-title:20px; --t-body:13.5px; --t-caption:12px;
       --t-mono:11.5px; --sp-tight:5px; --sp-close:9px; --sp-block:14px; --sp-air:18px;
       --sp-wide:23px; } }
   and it was the squeezed desktop the phone layout exists to avoid. The redesign draws its phone
   frames on the DESKTOP scale and says so on the board itself — "390pt, first-class, never a
   squeezed desktop: same tokens, voices and measured sizes as the signed-off desktop board" — and
   (fd) restates it: the desktop set is law, and only TOUCH-DRIVEN adaptations differ. A type ramp
   that shrinks because the screen is small is not a touch adaptation.
   MEASURED, before the removal, at 390: the sheet's block rhythm came out 18/5 against the 24/6
   both boards gauge, and the filter voice 12 against the drawn 13. Nothing here is a new number —
   the step-down's five sizes and five spaces simply stop being declared, so every surface reads the
   one ramp above, which is where each value was already ruled.
   --t-helper is the precedent: it was ruled flat rather than stepped on 2026-08-13 for exactly this
   reason, and this finishes what that started. */
