/* ==========================================================================
   Design tokens — Newsprint
   --------------------------------------------------------------------------
   THEMING, in one paragraph:

   Every colour token below is declared with `light-dark(<light>, <dark>)`,
   so a colour's two values live on ONE line and cannot drift apart. Which
   half is used is decided by `color-scheme`:

     :root                      color-scheme: light dark   -> follows the OS
     :root[data-theme="light"]  color-scheme: light        -> pinned light
     :root[data-theme="dark"]   color-scheme: dark         -> pinned dark

   So "auto" is simply the ABSENCE of a data-theme attribute, and the toggle
   only ever has to add, change, or remove that one attribute. Setting
   `color-scheme` also themes form controls and scrollbars for free.

   Each token has a plain light-only declaration immediately above the
   light-dark() one. Browsers too old for light-dark() ignore the second
   declaration and get the light theme — the same look v1 shipped with.

   Every ratio quoted in the comments was MEASURED, not estimated: oklch was
   converted to sRGB, then to WCAG relative luminance. The status marks were
   additionally checked under simulated protanopia, deuteranopia and
   tritanopia. Gate script and method:
   scratchpad/contrast.py + verify_final.py (2026-08-16).
   ========================================================================== */

/* The one served typeface. Variable: optical size 6-72 (the masthead and the
   body text get different drawings from one file) and weights 400 600. Latin
   subset only -- the site is written in English. swap: text paints in the
   fallback serif at once and re-flows into Newsreader when the file lands. */
@font-face {
  font-family: "Newsreader";
  font-style: normal;
  font-weight: 400 600;
  font-display: swap;
  src: url("/fonts/newsreader-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

:root {
  color-scheme: light dark;
}

:root[data-theme="light"] { color-scheme: light; }
:root[data-theme="dark"]  { color-scheme: dark;  }

:root {
  /* === Colour — Newsprint ================================================
     Light: warm cream paper, warm near-black ink, rust accent.
     Dark:  the same press at night — warm near-black paper, warm off-white
            ink, the rust lifted to stay legible. Not an inversion; the dark
            values were chosen and measured independently.
     ====================================================================== */

  /* Page and surfaces */
  --color-paper: oklch(97% 0.011 75);
  --color-paper: light-dark(oklch(97% 0.011 75), oklch(17% 0.010 62));

  --color-paper-2: oklch(94% 0.013 75);          /* footer band */
  --color-paper-2: light-dark(oklch(94% 0.013 75), oklch(21% 0.011 62));

  --color-surface: oklch(99.5% 0.006 75);        /* cards, panels */
  --color-surface: light-dark(oklch(99.5% 0.006 75), oklch(22.5% 0.012 62));

  /* Rules — decorative dividers, deliberately below 3:1. WCAG 1.4.11 scopes
     non-text contrast to UI components and MEANINGFUL graphics; a hairline
     is neither, and darkening it would destroy the newsprint texture. */
  --color-rule: oklch(80% 0.015 70);
  --color-rule: light-dark(oklch(80% 0.015 70), oklch(32% 0.012 60));

  --color-rule-strong: oklch(70% 0.018 65);      /* double rule under masthead */
  --color-rule-strong: light-dark(oklch(70% 0.018 65), oklch(42% 0.014 58));

  /* Text tiers — all measured >= 4.5:1 on paper, paper-2 and surface */
  --color-neutral: oklch(45% 0.014 65);          /* meta, axis labels  6.8:1 / 7.7:1 */
  --color-neutral: light-dark(oklch(45% 0.014 65), oklch(72% 0.014 68));

  --color-muted: oklch(40% 0.013 60);            /* nav links          8.5:1 / 9.5:1 */
  --color-muted: light-dark(oklch(40% 0.013 60), oklch(78% 0.012 66));

  --color-ink: oklch(21% 0.014 55);              /* body              16.3:1 / 14.6:1 */
  --color-ink: light-dark(oklch(21% 0.014 55), oklch(91% 0.010 75));

  --color-ink-2: oklch(15% 0.012 50);            /* headings          18.0:1 / 17.5:1 */
  --color-ink-2: light-dark(oklch(15% 0.012 50), oklch(97% 0.009 78));

  /* Accent — chroma sits just inside the sRGB gamut at each lightness, so the
     browser renders what is declared instead of silently clipping it. */
  --color-accent: oklch(37% 0.128 32);           /* links             10.2:1 / 8.2:1 */
  --color-accent: light-dark(oklch(37% 0.128 32), oklch(75% 0.130 45));

  --color-accent-2: oklch(30% 0.108 30);         /* hover             13.1:1 / 11.4:1 */
  --color-accent-2: light-dark(oklch(30% 0.108 30), oklch(84% 0.098 52));

  --color-focus: oklch(37% 0.128 32);
  --color-focus: light-dark(oklch(37% 0.128 32), oklch(75% 0.130 45));

  --color-selection: oklch(88% 0.040 45);
  --color-selection: light-dark(oklch(88% 0.040 45), oklch(38% 0.070 40));

  /* === Data colour =======================================================
     Two series only. A sales chart compares THIS period against the LAST
     one, so a five-colour categorical ramp would be decoration pretending
     to be information. Current period takes the accent; the prior period
     recedes to a cool grey-blue.  Separation: dE 0.30 light / 0.23 dark,
     worst-case under simulated CVD 0.40 / 0.26.
     ====================================================================== */
  --series-current: oklch(37% 0.128 32);
  --series-current: light-dark(oklch(37% 0.128 32), oklch(75% 0.130 45));

  --series-prior: oklch(62% 0.036 245);
  --series-prior: light-dark(oklch(62% 0.036 245), oklch(60% 0.040 245));

  /* Status MARKS — dots and fills. Non-text, so 3:1 is the bar, which buys
     the lightness range these hues need to stay apart for red-green colour
     blindness. Worst-case CVD separation 0.21 light / 0.20 dark.
     They are never the only signal: every status also carries a WORD and a
     shape (filled / half / open), so the colour is the fast-scan layer, not
     the meaning. That is WCAG 1.4.1, and it is also just better design. */
  --mark-good: oklch(62% 0.120 170);
  --mark-good: light-dark(oklch(62% 0.120 170), oklch(78% 0.130 158));

  --mark-warn: oklch(56% 0.110 80);
  --mark-warn: light-dark(oklch(56% 0.110 80), oklch(82% 0.130 80));

  /* Light "bad" moved from brick (15°) to wine (345°) on 2026-08-16: the brick sat
     17° from the rust accent, so "declining" and "this year" read as one colour
     two panels apart. Measured after the move: 10.7:1 / 9.8:1 / 11.5:1 on the
     three surfaces; separation from the accent 0.047 -> 0.108; worst-case CVD
     separation from good/warn 0.214 (was 0.216). Dark left as measured. */
  --mark-bad: oklch(36% 0.140 345);
  --mark-bad: light-dark(oklch(36% 0.140 345), oklch(72% 0.140 28));

  /* Status TEXT — deltas. Needs the full 4.5:1, and the +/- sign already
     provides the redundant encoding, so these are tuned for legibility
     rather than for CVD separation. */
  --text-good: oklch(48% 0.094 170);             /* 5.5:1 / 10.1:1 */
  --text-good: light-dark(oklch(48% 0.094 170), oklch(78% 0.130 158));

  --text-bad: oklch(40% 0.160 0);                /* 9.2:1 / 7.3:1 — light moved with the mark, see above */
  --text-bad: light-dark(oklch(40% 0.160 0), oklch(72% 0.140 28));

  /* === Typography ========================================================
     ONE typeface is served, from this domain: Newsreader (SIL Open Font
     License, licence text in /fonts/OFL.txt), as a single latin variable
     file carrying optical size 6-72 and weights 400-600, declared in the
     @font-face block above. Before 2026-08-16 nothing was served and the
     stack fell straight through to the reader's system serif -- the
     personality existed only on paper. The site still calls no third party.
     The mono stack is deliberately left to the system.
     ====================================================================== */
  --font-display: "Newsreader", ui-serif, Georgia, "Times New Roman", serif;
  --font-body:    "Newsreader", ui-serif, Georgia, "Times New Roman", serif;
  --font-outlier: "JetBrains Mono", ui-monospace, "SFMono-Regular", Menlo, Consolas, monospace;

  --text-xs:   0.75rem;    /* 12px  */
  --text-sm:   0.875rem;   /* 14px  */
  --text-base: 1rem;       /* 16px  */
  --text-md:   1.25rem;    /* 20px  */
  --text-lg:   1.5625rem;  /* 25px  */
  --text-xl:   1.9531rem;  /* 31.25px */
  --text-2xl:  2.4414rem;  /* 39px  */
  --text-mast:  clamp(2.25rem, 5vw + 1rem, 3.625rem);
  --text-title: clamp(1.9531rem, 3vw + 1rem, 2.4414rem);
  --text-kpi:   clamp(1.5625rem, 2.5vw + 0.75rem, 1.9531rem);

  --leading-tight: 1.15;
  --leading-snug:  1.35;
  --leading-body:  1.68;

  /* === Spacing — 4pt scale === */
  --space-3xs: 0.125rem;  /*  2px */
  --space-2xs: 0.25rem;   /*  4px */
  --space-xs:  0.5rem;    /*  8px */
  --space-sm:  0.75rem;   /* 12px */
  --space-md:  1rem;      /* 16px */
  --space-lg:  1.5rem;    /* 24px */
  --space-xl:  2.5rem;    /* 40px */
  --space-2xl: 4rem;      /* 64px */
  --space-3xl: 6rem;      /* 96px */
  --space-4xl: 9rem;      /* 144px */

  /* === Measure / page === */
  --measure: 65ch;
  --measure-wide: 78rem;        /* dashboards break the prose column */
  --page-gutter: clamp(1.25rem, 5vw, 3rem);

  /* === Rules === */
  --rule-hair: 1px;
  --rule-thick: 2px;

  /* === Radius — print metaphor, square by default === */
  --radius-none: 0;
  --radius-sm: 0.125rem;
  --radius-pill: 999px;

  /* === Motion — Newsprint reads as static print. The motion budget is one
     colour shift and one chart draw-in, both zero under reduced motion. === */
  --ease-out:    cubic-bezier(0.16, 1, 0.3, 1);
  --ease-in:     cubic-bezier(0.7, 0, 0.84, 0);
  --ease-in-out: cubic-bezier(0.65, 0, 0.35, 1);
  --dur-short: 120ms;
  --dur-med:   180ms;
  --dur-long:  420ms;

  /* === Z-index scale === */
  --z-base:     1;
  --z-raised:   10;
  --z-dropdown: 100;
  --z-sticky:   200;
  --z-modal:    400;
  --z-toast:    500;
  --z-tooltip:  600;
}

@media (prefers-reduced-motion: reduce) {
  :root {
    --dur-short: 0ms;
    --dur-med:   0ms;
    --dur-long:  0ms;
  }
}
