/* ==========================================================================
   Design tokens — the only file that defines colors.
   Origin: stembot-runeworks:web/src/styles/tokens.css,
           tombio:static/style.css, MerlinStudio:frontend/app/globals.css (adapted)

   Layers:
     1. Brand hooks     — what a theme preset overrides (see design/README.md).
     2. Raw scales      — a neutral ramp computed in OKLCH from two hooks, so a
                          preset can tint every grey (cream paper, slate…) in one line.
     3. Semantic tokens — the only names components are allowed to use.
   Dark mode is `:root[data-theme="dark"]`, set by libs/web/theme.js (never by
   a media query here: the user's explicit choice must win over the OS).
   ========================================================================== */

:root {
  color-scheme: light;

  /* --- 1. Brand hooks (contract) ------------------------------------------ */
  --brand: #4f46e5;
  --brand-contrast: #ffffff;
  --font-ui: "Inter", ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  --font-display: var(--font-ui);
  --font-mono: "JetBrains Mono", ui-monospace, "SFMono-Regular", Consolas, "Liberation Mono", monospace;
  --radius-base: 8px;
  --density: 1;

  /* --- 1b. Optional extension hooks --------------------------------------
     Not required by the contract; each has a safe default. They exist because
     the four source apps differ in ways the seven contract hooks cannot express
     (tinted paper, a dark navy sidebar, a 13px tool UI, pill buttons). */
  --neutral-hue: 262;          /* hue of every grey (OKLCH degrees) */
  --neutral-chroma: 0.012;     /* 0 = pure grey, .025 = visibly tinted paper */
  --fs-base: 15px;             /* body size; the type scale derives from it */
  --btn-radius: var(--radius);
  --btn-shadow: none;
  /* --brand-dark / --brand-dark-contrast: the accent used in dark mode.
     Left undefined on purpose: when absent it is derived from --brand. */

  /* --- 2. Raw neutral scale ---------------------------------------------- */
  --n-0:    oklch(1     calc(var(--neutral-chroma) * .3) var(--neutral-hue));
  --n-25:   oklch(0.985 var(--neutral-chroma) var(--neutral-hue));
  --n-50:   oklch(0.968 var(--neutral-chroma) var(--neutral-hue));
  --n-100:  oklch(0.945 var(--neutral-chroma) var(--neutral-hue));
  --n-150:  oklch(0.915 var(--neutral-chroma) var(--neutral-hue));
  --n-200:  oklch(0.885 var(--neutral-chroma) var(--neutral-hue));
  --n-300:  oklch(0.82  var(--neutral-chroma) var(--neutral-hue));
  --n-400:  oklch(0.70  var(--neutral-chroma) var(--neutral-hue));
  --n-500:  oklch(0.53  var(--neutral-chroma) var(--neutral-hue));
  --n-600:  oklch(0.45  var(--neutral-chroma) var(--neutral-hue));
  --n-700:  oklch(0.37  var(--neutral-chroma) var(--neutral-hue));
  --n-800:  oklch(0.30  var(--neutral-chroma) var(--neutral-hue));
  --n-850:  oklch(0.255 var(--neutral-chroma) var(--neutral-hue));
  --n-900:  oklch(0.215 var(--neutral-chroma) var(--neutral-hue));
  --n-950:  oklch(0.18  var(--neutral-chroma) var(--neutral-hue));
  --n-1000: oklch(0.145 var(--neutral-chroma) var(--neutral-hue));

  /* Status hues, text-safe on light surfaces (checked >= 4.5:1 on --*-soft). */
  --green-600: oklch(0.50 0.13 150);
  --amber-600: oklch(0.52 0.12 65);
  --red-600:   oklch(0.52 0.19 27);
  --blue-600:  oklch(0.50 0.14 250);
  --green-300: oklch(0.80 0.14 152);
  --amber-300: oklch(0.83 0.13 80);
  --red-300:   oklch(0.75 0.14 25);
  --blue-300:  oklch(0.78 0.11 245);

  /* --- 3. Semantic tokens (light) ---------------------------------------- */
  --bg: var(--n-50);
  --bg-sunken: var(--n-100);
  --surface: var(--n-0);
  --surface-2: var(--n-25);
  --border: var(--n-150);
  --border-strong: var(--n-300);

  --text: var(--n-900);
  --text-muted: var(--n-600);
  --text-faint: var(--n-500);

  --accent: var(--brand);
  --accent-contrast: var(--brand-contrast);
  --accent-hover: color-mix(in oklab, var(--accent) 84%, black);
  --accent-soft: color-mix(in oklab, var(--accent) 12%, var(--surface));
  /* Accent used as *text* (links, selected nav). A bright brand such as an
     orange fails 4.5:1 on paper, so it is pulled toward the text color. */
  --accent-text: color-mix(in oklab, var(--accent) 60%, var(--text));
  --focus: var(--accent-text);

  --ok: var(--green-600);
  --warn: var(--amber-600);
  --danger: var(--red-600);
  --info: var(--blue-600);
  --ok-soft: color-mix(in oklab, var(--ok) 13%, var(--surface));
  --warn-soft: color-mix(in oklab, var(--warn) 15%, var(--surface));
  --danger-soft: color-mix(in oklab, var(--danger) 12%, var(--surface));
  --info-soft: color-mix(in oklab, var(--info) 12%, var(--surface));
  --danger-contrast: #ffffff;   /* text on a solid --danger button */

  /* Sidebar / topbar chrome. Defaults to the page surface; the navy preset
     turns it into a dark rail. */
  --chrome-bg: var(--surface);
  --chrome-text: var(--text);
  --chrome-accent: var(--accent-text);
  --chrome-muted: color-mix(in oklab, var(--chrome-text) 72%, var(--chrome-bg));
  --chrome-hover: color-mix(in oklab, var(--chrome-text) 7%, var(--chrome-bg));
  --chrome-active: color-mix(in oklab, var(--chrome-accent) 14%, var(--chrome-bg));
  --chrome-border: color-mix(in oklab, var(--chrome-text) 12%, var(--chrome-bg));

  --overlay: rgb(10 12 20 / 0.45);
  --shadow-1: 0 1px 2px rgb(16 24 40 / 0.05), 0 1px 3px rgb(16 24 40 / 0.08);
  --shadow-2: 0 10px 30px -8px rgb(16 24 40 / 0.22), 0 2px 6px rgb(16 24 40 / 0.06);
  --shadow-3: 0 24px 60px -12px rgb(16 24 40 / 0.35), 0 4px 12px rgb(16 24 40 / 0.08);

  /* --- Shape ------------------------------------------------------------- */
  --radius-sm: calc(var(--radius-base) * 0.6);
  --radius: var(--radius-base);
  --radius-lg: calc(var(--radius-base) * 1.5);
  --radius-xl: calc(var(--radius-base) * 2);
  --radius-pill: 999px;

  /* --- Space (4px grid) -------------------------------------------------- */
  --space-1: 4px;
  --space-2: 8px;
  --space-3: 12px;
  --space-4: 16px;
  --space-5: 24px;
  --space-6: 32px;
  --space-7: 48px;
  --space-8: 64px;

  /* --- Type -------------------------------------------------------------- */
  --fs-xs: calc(var(--fs-base) * 0.8);
  --fs-sm: calc(var(--fs-base) * 0.9);
  --fs-md: var(--fs-base);
  --fs-lg: calc(var(--fs-base) * 1.2);
  --fs-xl: calc(var(--fs-base) * 1.45);
  --fs-2xl: calc(var(--fs-base) * 1.8);
  --fs-3xl: calc(var(--fs-base) * 2.3);

  /* --- Controls ---------------------------------------------------------- */
  --control-h: calc(40px * var(--density));
  --control-h-sm: calc(32px * var(--density));
  --control-h-lg: calc(48px * var(--density));

  /* --- Motion ------------------------------------------------------------ */
  --dur-fast: 120ms;
  --dur: 200ms;
  --dur-slow: 360ms;
  --ease-out: cubic-bezier(0.22, 1, 0.36, 1);
  --ease-spring: cubic-bezier(0.34, 1.56, 0.64, 1);

  /* --- Layers & layout --------------------------------------------------- */
  --z-topbar: 40;
  --z-drawer: 60;
  --z-modal: 80;
  --z-toast: 100;
  --topbar-h: 56px;
  --sidebar-w: 240px;
  --content-max: 1100px;
}

/* ==========================================================================
   Dark mode. Only semantic tokens move; the raw neutral ramp is shared, so a
   tinted preset stays tinted at night.
   ========================================================================== */
:root[data-theme="dark"] {
  color-scheme: dark;

  --bg: var(--n-950);
  --bg-sunken: var(--n-1000);
  --surface: var(--n-900);
  --surface-2: var(--n-850);
  --border: var(--n-800);
  --border-strong: var(--n-700);

  --text: var(--n-50);
  --text-muted: var(--n-300);
  --text-faint: var(--n-400);

  /* A dark brand (navy, ink, teal) would vanish on a dark page, so the dark
     accent is the brand lifted toward white, with dark text on it. A preset
     can set --brand-dark to pick an exact color instead. */
  --accent: var(--brand-dark, color-mix(in oklab, var(--brand) 55%, white));
  --accent-contrast: var(--brand-dark-contrast, var(--n-1000));
  --accent-hover: color-mix(in oklab, var(--accent) 85%, white);
  --accent-soft: color-mix(in oklab, var(--accent) 16%, var(--surface));
  --accent-text: color-mix(in oklab, var(--accent) 80%, var(--text));

  --ok: var(--green-300);
  --warn: var(--amber-300);
  --danger: var(--red-300);
  --info: var(--blue-300);
  --ok-soft: color-mix(in oklab, var(--ok) 16%, var(--surface));
  --warn-soft: color-mix(in oklab, var(--warn) 16%, var(--surface));
  --danger-soft: color-mix(in oklab, var(--danger) 16%, var(--surface));
  --info-soft: color-mix(in oklab, var(--info) 16%, var(--surface));
  --danger-contrast: var(--n-1000);

  --overlay: rgb(0 0 0 / 0.6);
  --shadow-1: 0 1px 2px rgb(0 0 0 / 0.4);
  --shadow-2: 0 10px 30px -8px rgb(0 0 0 / 0.6), 0 2px 6px rgb(0 0 0 / 0.3);
  --shadow-3: 0 24px 60px -12px rgb(0 0 0 / 0.75), 0 4px 12px rgb(0 0 0 / 0.4);
}

@media (prefers-reduced-motion: reduce) {
  :root {
    --dur-fast: 1ms;
    --dur: 1ms;
    --dur-slow: 1ms;
  }
}

/* ------------------------------------------------------------------ data-viz palette
   Used by the charts module (and any chart). The ORDER is the colour-blind safety mechanism,
   validated as a set (adjacent CVD ΔE ≥ 8, normal-vision ΔE ≥ 15) against the light and dark
   surfaces. Three light slots (3 aqua, 4 yellow, 5 magenta) sit below 3:1 on the surface, so
   charts always ship a legend and a table view. Change it only as a whole, then re-validate.
   A preset may override --chart-1…8 (all eight, both modes). */
:root {
  --chart-1: #2a78d6; --chart-2: #eb6834; --chart-3: #1baf7a; --chart-4: #eda100;
  --chart-5: #e87ba4; --chart-6: #008300; --chart-7: #4a3aa7; --chart-8: #e34948;
  --chart-other: var(--n-400);
  --chart-grid: var(--border);
  --chart-axis: var(--border-strong);
}
:root[data-theme="dark"] {
  --chart-1: #3987e5; --chart-2: #d95926; --chart-3: #199e70; --chart-4: #c98500;
  --chart-5: #d55181; --chart-6: #008300; --chart-7: #9085e9; --chart-8: #e66767;
  --chart-other: var(--n-500);
}
