/* ==========================================================================
   tokens.css — the single source of colour truth.

   Governed by Design.md. Read it before changing anything here.

   RULES
   1. This is the ONLY file in web/ that may contain a colour literal.
   2. Never add a second :root block that redefines a token name. Three
      stacked palettes are what made colours conflict in the first place.
   3. Tokens are named for their job, never their hue.
   4. After changing any value, run: node scripts/check-contrast.mjs
   ========================================================================== */

:root {
  color-scheme: dark;

  /* --- Surfaces ------------------------------------------------------- */
  --surface-base: #080b14;      /* page canvas */
  --surface-raised: #101522;    /* cards, masthead, footer */
  --surface-sunken: #171d2c;    /* table headers, wells, insets */
  --surface-overlay: #20283a;   /* hover, active tab, chips */
  --surface-inverse: #f3f5fa;   /* light plates, logo lockups, print */

  /* --- Text ----------------------------------------------------------- */
  --text-primary: #f3f5fa;
  --text-secondary: #c3cad8;
  --text-muted: #9aa5b7;
  --text-on-inverse: #101522;
  --text-on-accent: #ffffff;
  --warning-ink: #17120a;       /* the only ink permitted on amber */

  /* --- Brand ---------------------------------------------------------- */
  /* Red splits in two: it cannot pass both as text on dark and as a
     surface under white. See Design.md 1.3. */
  --brand-primary: #e91444;     /* red as a SURFACE, white text on it */
  --brand-secondary: #244aa7;   /* Basel blue as a surface */
  --brand-accent: #ff3c68;      /* red as TEXT/graphic on dark */

  /* --- Interactive ---------------------------------------------------- */
  --interactive-default: #f3f5fa;
  --interactive-hover: #ff3c68;
  --interactive-active: #ff6b8d;
  --interactive-disabled: #566075;
  --focus-ring: #75a0ff;

  /* --- Feedback ------------------------------------------------------- */
  --success-surface: #1d8664;   /* white text: 4.52:1 */
  --success-text: #27b486;      /* on dark surfaces */
  --warning-surface: #e1b657;   /* warning-ink only: 9.78:1 */
  --warning-text: #e1b657;
  --danger-surface: #e91444;
  --danger-text: #ff3c68;
  --info-surface: #244c9c;
  --info-text: #678de0;

  /* Dark-tinted feedback chips: coverage tiers, badges, notes.
     Each pair is verified together — see scripts/check-contrast.mjs. */
  --success-subtle: #123e35;
  --success-on-subtle: #adf3dd;
  --warning-subtle: #543d18;
  --warning-on-subtle: #ffe0a7;
  --danger-subtle: #4a2934;
  --danger-on-subtle: #f0c7d3;
  --info-subtle: #1e356b;
  --info-on-subtle: #c4d5ff;
  --neutral-subtle: #171b24;
  --neutral-on-subtle: #9aa5b7;

  /* --- Lines ---------------------------------------------------------- */
  --border-subtle: #30394b;     /* decorative hairlines, card edges */
  --border-strong: #5f7195;     /* boundary of anything operable, >=3:1 */
  --divider: #30394b;           /* row rules inside a card or table */

  /* --- Data colours (allow-listed, not UI tokens) ---------------------- */
  /* These encode data, not meaning. See Design.md 1.7. */
  --pitch-surface: #173d35;
  --pitch-surface-alt: #194f42;
  --pitch-border: #285a50;
  --pitch-line: #ffffff;
  --tier-full: #126c57;
  --tier-advanced: #244c9c;
  --tier-standard: #a46715;
  --tier-basic: #744454;
  --goal-marker: #f3cf51;       /* scored shot */
  --shot-blocked: #7b4860;
  --shot-blocked-mark: #a85a7b; /* lighter, for the goal-view marker */
  --shot-woodwork: #e1b657;

  /* Team sides in data visualisations. */
  --team-home: #e91444;
  --team-away: #3567d6;

  /* Categorical player palette for the spatial lab. Ordered, not ranked. */
  --player-1: #e91444;
  --player-2: #4c7ee8;
  --player-3: #27b486;
  --player-4: #e1b657;

  /* Positional lines on the action map, in pitch order. Categorical: identity,
     not rank. Generated at OKLCH L .65 / C .15 on 90-degree hue steps and
     verified with the data-viz validator against --pitch-surface over ALL
     pairs, because these marks overlap anywhere on the pitch: lightness band,
     chroma floor, normal-vision separation and 3:1 contrast all pass, with
     colour-blind separation at the 8.0 target. Four is not a stylistic choice —
     it is the measured ceiling. No 5, 6 or 7 hue set clears all-pairs
     separation on this surface at any spacing, which is why the finer roles
     (CB, FB, CAM, winger) are a filter and never a colour. The green hue step
     is deliberately skipped: it is the pitch. */
  --line-gk: #db6371;
  --line-def: #9e9300;
  --line-mid: #00aaab;
  --line-att: #877fe6;

  /* Territory ramps. Magnitude, not identity, so each is ONE hue running
     light to dark — never a rainbow. Verified with the data-viz validator in
     ordinal mode: monotone lightness, every adjacent gap >= .06 L, and the
     dark end clear of the page surface.

     The two ramps are the halves of one diverging scale whose midpoint is
     --territory-even, a true neutral: on the dominance map a cell owned by
     nobody must not lean red or blue, and a hue at the midpoint would invent a
     winner where the count is level. Each half doubles as that side's
     sequential ramp on the per-team and per-player maps, so a shade means the
     same thing on every pitch on the page. */
  --territory-basel-1: #f7cbcf;
  --territory-basel-2: #eca7ae;
  --territory-basel-3: #df838f;
  --territory-basel-4: #d15e71;
  --territory-basel-5: #c03254;
  --territory-basel-6: #ae0037;
  --territory-opponent-1: #c2daf9;
  --territory-opponent-2: #99bff1;
  --territory-opponent-3: #70a5e8;
  --territory-opponent-4: #458ade;
  --territory-opponent-5: #026dd4;
  --territory-opponent-6: #004fc8;
  --territory-even: #81878d;

  /* Sequential heat scale — must stay perceptually ordered. */
  --heat-low: #fff7a8;
  --heat-mid: #ffba33;
  --heat-high: #e91444;

  /* Column comparison in the player tables: where one man's number sits inside
     the spread of his own column. Diverging, and the middle band is deliberately
     left unpainted — a min/max ramp always puts something at each end, so
     painting the middle would give a column that is flat a gradient it does not
     have. Two steps a side is all a 66-column table can carry.

     Metrics whose registry direction is `neutral` (minutes, booleans) get the
     one-hue `flat` wash instead: green on more minutes would claim more is
     better, which the registry explicitly declines to say.

     Composited over --surface-raised and --surface-sunken, every step keeps
     text-primary and text-secondary above 4.5:1 (worst case: good-2 carrying
     text-secondary on sunken, 4.62:1). text-muted is NOT safe on good-2 — but
     it only ever styles .metric-missing and .metric-na, which have no value to
     compare and so are never washed. */
  --compare-good-1: color-mix(in srgb, var(--success-surface) 34%, transparent);
  --compare-good-2: color-mix(in srgb, var(--success-surface) 62%, transparent);
  --compare-poor-1: color-mix(in srgb, var(--danger-surface) 30%, transparent);
  --compare-poor-2: color-mix(in srgb, var(--danger-surface) 52%, transparent);
  --compare-flat-1: color-mix(in srgb, var(--info-surface) 40%, transparent);
  --compare-flat-2: color-mix(in srgb, var(--info-surface) 70%, transparent);

  /* --- Alpha overlays --------------------------------------------------- */
  /* Derived from tokens with color-mix so they can never drift from the
     colour they tint. Four steps only — the audit found 17. */
  --overlay-weak: color-mix(in srgb, var(--text-on-accent) 6%, transparent);
  --overlay-line: color-mix(in srgb, var(--text-on-accent) 14%, transparent);
  --overlay-medium: color-mix(in srgb, var(--text-on-accent) 35%, transparent);
  --overlay-strong: color-mix(in srgb, var(--text-on-accent) 60%, transparent);
  --focus-glow: color-mix(in srgb, var(--focus-ring) 22%, transparent);
  --brand-wash: color-mix(in srgb, var(--brand-primary) 13%, transparent);

  /* --- Brand gradients -------------------------------------------------- */
  /* Defined once, here, so the stops cannot drift. Any transparent mark placed
     over these needs the lockup plate of Design.md 3.5b — surface-inverse, not
     a scrim. A scrim over these stops reads as a smudge under the mark. */
  --gradient-match-masthead:
    linear-gradient(90deg, #86102e 0 32%, #0d142b 32% 68%, #1d3979 68%);
  --gradient-feature:
    linear-gradient(135deg, #a81138 0 49.8%, #121c40 50.2% 100%);
  --gradient-page-glow:
    radial-gradient(circle at 78% -10%, rgba(53, 103, 214, .10), transparent 30rem);

  /* --- Scrim ---------------------------------------------------------- */
  /* Never place a transparent mark on a gradient or photo without this.
     Verified against every gradient stop in use: mono-white 17.92:1
     worst case, full-colour mark 3.97:1. See Design.md 3.5. */
  --scrim-strong: rgba(8, 11, 20, .78);
  --scrim-soft: rgba(8, 11, 20, .55);
  --backdrop: rgba(8, 11, 20, .68);

  /* --- Spacing (4px base) ---------------------------------------------- */
  --space-1: 4px;
  --space-2: 8px;
  --space-3: 12px;
  --space-4: 16px;
  --space-5: 20px;
  --space-6: 24px;
  --space-8: 32px;
  --space-10: 40px;
  --space-12: 48px;
  --space-16: 64px;
  --space-20: 80px;

  /* --- Layout ----------------------------------------------------------- */
  /* The sticky masthead's height, published so anything that has to stick
     beneath it can say where "beneath" is. This is the desktop value it starts
     from; `trackMastheadHeight()` in app.js measures the real bar and keeps
     this true when it reflows to two rows on a narrow screen. */
  --masthead-height: 88px;
  /* Where the page's sticky chrome ends: the masthead plus the match tab bar
     that sticks under it (a 44px button and its rule), plus air. Anything that
     sticks below both, or is scrolled to from an index, measures from here. */
  --sticky-top: calc(var(--masthead-height) + 56px);

  /* --- Radius ---------------------------------------------------------- */
  --radius-none: 0;
  --radius-control: 6px;
  --radius-round: 999px;

  /* --- Elevation -------------------------------------------------------- */
  --elevation-none: none;
  --elevation-card: 0 14px 40px rgba(0, 0, 0, .16);
  --elevation-chip: 0 3px 12px rgba(0, 0, 0, .25);
  --elevation-sticky: 0 12px 30px rgba(0, 0, 0, .25);
  --elevation-sticky-column: 5px 0 12px rgba(0, 0, 0, .2);
  --elevation-popover: 0 18px 40px rgba(0, 0, 0, .5);
  --elevation-drawer: -25px 0 70px rgba(0, 0, 0, .45);

  /* --- Motion ----------------------------------------------------------- */
  --motion-fast: 120ms;
  --motion-base: 160ms;
  --motion-slow: 240ms;
  --motion-ease: ease;

  /* --- Typography ------------------------------------------------------- */
  /* No webfonts are loaded and none are linked in index.html. The stylesheets
     used to name "DM Sans" and "Manrope", which never resolved and always fell
     through to the next entry. Those names are gone; these three stacks are
     exactly what the browser was already rendering. Adding a webfont here
     would change every line of copy — see Design.md 5.1. */
  --font-body: system-ui, sans-serif;                                /* was "DM Sans", … */
  --font-display: "Arial Narrow", "Helvetica Neue", Arial, sans-serif;
  --font-figure: sans-serif;                                         /* was "Manrope", … */
}

/* Motion is opt-in. Every animated rule in the app is covered here. */
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
    scroll-behavior: auto !important;
  }
}
