/*
 * Robovue semantic design tokens.
 *
 * This file is the ONLY place a raw color/spacing literal should appear
 * (enforced by scripts/check-design-tokens.sh) — every component in
 * components.css should reference a semantic custom property defined here,
 * never a hex/oklch value or a magic-number px/rem directly.
 *
 * Primitives come from vendored Open Props files (css/vendor/open-props/) —
 * see docs/design-system.md for the full rationale. Dark is the default
 * theme (:root); the `:root[data-theme="light"]` block below redefines the
 * SAME semantic names to different primitive values — never a second
 * parallel set of names.
 *
 * Theming is attribute-driven, not media-query-driven (ADR 0020): JS always
 * resolves the effective theme (explicit user choice via localStorage, or
 * prefers-color-scheme when unset) and writes it as `data-theme` on <html>
 * before first paint (see _Layout.cshtml's inline head script). CSS only
 * ever reads that attribute — this lets "System" mode, an explicit "Light"
 * override, and an explicit "Dark" override all be expressed without
 * duplicating this block's declarations for each case.
 */

:root {
  color-scheme: dark;

  /* A very slight cool tint on the neutral ramp (Open Props' --gray-hue/
     --gray-chroma are `none` — pure achromatic — unless overridden here). */
  --gray-hue: 255;
  --gray-chroma: 0.006;

  /* Explicit override: Open Props' fonts.min.css defines its own
     --font-mono (a chain starting with named commercial fonts like Dank
     Mono/Operator Mono). The locked design decision is a plain system
     monospace stack only — no new font asset, nothing exotic even as an
     unreachable fallback — so this must be redeclared here, not left to
     Open Props' default. */
  --font-mono: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;

  /* Surfaces (Open Props' --gray-0..12 ramp: 0 is lightest ~99% L, 12 is
     darkest ~31% L used here — 13/14/15 exist but read as near-black,
     reserved in case an even-darker canvas is wanted later). */
  --bg-canvas: var(--gray-13);
  --bg-surface: var(--gray-12);
  --bg-surface-hover: var(--gray-11);
  --border-subtle: var(--gray-10);
  --border-default: var(--gray-9);

  /* Text hierarchy. --text-tertiary was originally --gray-7 (58% L) — axe-core's
     color-contrast check (run against every page, see docs/design-system.md)
     measured only 3.07:1 against --bg-surface, below WCAG AA's 4.5:1; --gray-6
     (63% L) still only reached 3.77:1. --gray-5 (68% L) is the step that
     actually clears 4.5:1 while staying the most muted of the three text tiers. */
  --text-primary: var(--gray-1);
  --text-secondary: var(--gray-4);
  --text-tertiary: var(--gray-5);

  /* Accent (indigo, hue 270 from open-props/oklch-hues.min.css) — picked
     deliberately far from every status hue below so accent can never be
     mistaken for a status color. Values are frozen literals derived from
     open-props/colors-oklch.min.css's own step curve rather than a live
     var(--color-hue) chain, so a future component can't accidentally change
     the accent by touching an unrelated --color-hue override elsewhere.
     -solid uses step 8 (53% L, .20 C), not step 6 (63% L) — axe-core's
     color-contrast check measured white (--accent-contrast) on step 6 at
     only 3.55:1 against WCAG AA's 4.5:1 (this is real white-on-solid-fill
     button contrast, not a subtle badge tint, so it needed a genuinely
     darker step, not just a tweak). */
  --accent-solid: oklch(53% 0.20 270);
  --accent-solid-hover: oklch(58% 0.21 270); /* step 7 */
  --accent-text: oklch(80% 0.14 270); /* step 3 */
  --accent-contrast: var(--gray-0); /* text/icon color drawn on top of --accent-solid */

  /* Status — same step (8: 53%/.20, and 3: 80%/.14) as accent for every hue,
     so only the hue itself communicates meaning; hues are red 25, orange 75,
     green 145 (open-props/oklch-hues.min.css). Subtle backgrounds and
     borders are derived from -solid via color-mix() rather than hand-picked
     as separate literals. Kept at the same step as accent-solid even though
     only -error-solid is currently used as a white-text solid button fill
     (danger button) — so a future solid-fill use of -ok/-warning doesn't
     silently fail the same contrast check this step was chosen to pass. */
  /* -subtle-bg mixes the solid hue into --bg-surface, which is itself a
     near-gray with its own (very low-chroma) hue of 255. `color-mix(in
     oklch, ...)` interpolates hue as a plain weighted average of the two
     endpoints' hue angles regardless of how little chroma one side has —
     so at a 16% mix, the *background's* near-meaningless hue (255) was
     dominating the result (weighted 84%), pulling ok/warning/error subtle
     backgrounds all toward the same pale blue-lavender instead of
     green/orange/red respectively (measured: warning and error subtle-bg
     were within 1-2 hex steps of each other, indistinguishable). Mixing
     `in oklab` instead interpolates the rectangular a/b components directly,
     so a near-zero-chroma endpoint contributes near-zero a/b instead of a
     misleading full-weight hue angle, and the result is correctly tinted
     by whichever color actually has chroma. Verified via direct OKLab/sRGB
     conversion (not just visual inspection) — this was present in both
     themes, just far more noticeable against light mode's near-white
     surface. */
  --status-ok-solid: oklch(53% 0.20 145);
  --status-ok-text: oklch(80% 0.14 145);
  --status-ok-subtle-bg: color-mix(in oklab, var(--status-ok-solid) 16%, var(--bg-surface));
  --status-ok-border: color-mix(in oklch, var(--status-ok-solid) 50%, transparent);

  --status-warning-solid: oklch(53% 0.20 75);
  --status-warning-text: oklch(80% 0.14 75);
  --status-warning-subtle-bg: color-mix(in oklab, var(--status-warning-solid) 16%, var(--bg-surface));
  --status-warning-border: color-mix(in oklch, var(--status-warning-solid) 50%, transparent);

  --status-error-solid: oklch(53% 0.20 25);
  --status-error-text: oklch(80% 0.14 25);
  --status-error-subtle-bg: color-mix(in oklab, var(--status-error-solid) 16%, var(--bg-surface));
  --status-error-border: color-mix(in oklch, var(--status-error-solid) 50%, transparent);

  --status-neutral-solid: var(--gray-7);
  --status-neutral-text: var(--gray-4);
  --status-neutral-subtle-bg: color-mix(in oklab, var(--status-neutral-solid) 16%, var(--bg-surface));
  --status-neutral-border: color-mix(in oklch, var(--status-neutral-solid) 50%, transparent);

  /* Code-syntax tokens (Prism), deliberately independent from the status/
     accent tokens above even where the hue coincides today — so retuning a
     badge color later can't silently reshuffle code highlighting, and vice
     versa. comment/punctuation intentionally alias the generic text
     hierarchy (a comment IS "muted text," not a distinct hue); tag/number/
     string reuse today's error/warning/ok hues as frozen literals (same
     reasoning: syntax tags-as-red, numbers-as-orange, strings-as-green is
     a conventional mapping, just no longer wired to the status tokens
     living value). class-name gets a genuinely new hue (100, a gold/olive)
     distinct from every other token's hue so it no longer collapses onto
     the same value as keyword (both previously aliased --accent-text). */
  --code-comment-text: var(--text-tertiary);
  --code-punctuation-text: var(--text-secondary);
  --code-tag-text: oklch(80% 0.14 25);
  --code-class-text: oklch(80% 0.16 100);
  --code-number-text: oklch(80% 0.14 75);
  --code-keyword-text: oklch(80% 0.14 270);
  --code-string-text: oklch(80% 0.14 145);

  /* Focus ring (WCAG 2.4.11) — always visible, never removed without a
     replacement. */
  --focus-ring-color: var(--accent-solid);
  --focus-ring: 2px solid var(--focus-ring-color);
  --focus-ring-offset: 2px;

  /* Type scale — aliased directly onto Open Props' own --font-size-*
     tokens (open-props/fonts.min.css). Open Props' scale has no step
     between 12px (step 0) and 16px (step 1) — rather than inventing a
     bespoke 14px literal to hit a "denser" target, this deliberately
     accepts 12/16px as the working secondary/body sizes so every type
     size stays a real Open Props token, no exceptions. */
  --text-xs: var(--font-size-0); /* 12px */
  --text-body: var(--font-size-1); /* 16px */
  --text-md: var(--font-size-2); /* ~17.6px */
  --text-lg: var(--font-size-3); /* 20px */
  --text-xl: var(--font-size-4); /* 24px */
  --text-2xl: var(--font-size-5); /* 32px */
  --text-3xl: var(--font-size-6); /* 40px — added for the marketing/ hero headline (docs/adr/0022), unused elsewhere in the authenticated app today; still a real Open Props step, no bespoke literal */

  --font-weight-body: var(--font-weight-4);
  --font-weight-medium: var(--font-weight-6);
  --font-weight-bold: var(--font-weight-7);
  --leading-tight: var(--font-lineheight-1);
  --leading-body: var(--font-lineheight-3);

  /* Spacing — aliased onto Open Props' --size-* (rem-based, so it scales
     with user font-size zoom, unlike the px variants of the same file). */
  --space-1: var(--size-1); /* 4px */
  --space-2: var(--size-2); /* 8px */
  --space-3: var(--size-3); /* 16px */
  --space-4: var(--size-4); /* 20px */
  --space-5: var(--size-5); /* 24px */
  --space-6: var(--size-6); /* 28px */
  --space-7: var(--size-7); /* 32px */
  --space-8: var(--size-8); /* 48px */
  --space-9: var(--size-9); /* 64px — marketing/ section padding-block rhythm (docs/adr/0022); a real Open Props step, same discipline as --text-3xl above. Unused in the authenticated app today. */

  /* Radius — Open Props' --radius-2/3 (open-props/borders.min.css) bracket
     an 8px "md" gap in their own scale; --radius-md is the one
     deliberately hand-picked value in this file for exactly that reason. */
  --radius-sm: var(--radius-2); /* 5px */
  --radius-md: 0.5rem; /* 8px — not an Open Props token, see comment above */
  --radius-lg: var(--radius-3); /* 1rem / 16px */

  /* Elevation */
  --shadow-card: var(--shadow-2);
  --shadow-popover: var(--shadow-3);

  /* Sticky site chrome. --header-height is an explicit fixed reference
     (not live-measured) so .connect-toc — and any future sticky sub-nav —
     can dock directly beneath the header via one shared offset. Derived
     from .site-header's padding (var(--space-3) top/bottom = 32px) plus
     its tallest in-flow child, the mobile <768px> .nav-toggle summary
     button (36px) → 68px total. Re-derive if the header's padding or
     tallest child changes; it is not kept in sync automatically. */
  --header-height: 4.25rem; /* 68px */

  /* Stacking order for persistent site chrome. .toast-region's z-index
     (1000, components.css) stays untouched and above both of these —
     toasts must float above header/sub-nav. Sub-nav is 15, not 10: Prism's
     vendored toolbar plugin (prism-toolbar.min.css, copy-to-clipboard
     button) hardcodes z-index:10 on its own absolutely-positioned toolbar,
     and a tied z-index resolves by DOM order — the toolbar, nested deeper
     in the document, would then paint on top of the sticky sub-nav instead
     of scrolling under it. 15 keeps the sub-nav above that fixed vendor
     value (and any other z:10 vendor content) while staying below the
     header. */
  --z-sticky-subnav: 15;
  --z-header: 20;
}

:root[data-theme="light"] {
  color-scheme: light;

  --bg-canvas: var(--gray-0);
  --bg-surface: var(--gray-1);
  --bg-surface-hover: var(--gray-2);
  --border-subtle: var(--gray-3);
  --border-default: var(--gray-4);

  --text-primary: var(--gray-13);
  --text-secondary: var(--gray-10);
  --text-tertiary: var(--gray-9);

  /* Darker/more saturated steps than the dark theme so each color still
     reaches AA contrast against a light canvas (step 9: 49% L / .19 C,
     step 10: 42% L / .17 C — same open-props/colors-oklch.min.css curve,
     just a darker step on it). --text-tertiary needed the same axe-core
     correction as the dark theme, just in the opposite direction —
     --gray-7 (58% L) measured only 3.72:1 against --bg-surface; --gray-9
     (49% L) is the step that clears 4.5:1. */
  /* Chroma raised from .19/.17 to .24/.22 — measured against the sRGB
     gamut boundary at this lightness (see docs/adr/0021), the old values
     had real headroom left (ceiling is ~.29/.27 at L49/L42) and read as
     dull/desaturated next to the dark theme's much brighter step-3 accent.
     Contrast only improves by going darker-and-more-saturated at the same
     L, so this is a strict vividness win, not a contrast trade-off. */
  --accent-solid: oklch(49% 0.24 270);
  --accent-solid-hover: oklch(42% 0.22 270);
  --accent-text: oklch(42% 0.22 270);
  --accent-contrast: var(--gray-0);

  /* Status hues are UNCHANGED here deliberately. Measured against the sRGB
     gamut boundary (docs/adr/0021), ok/warning/error are already at or
     within a hair of their maximum achievable chroma for this lightness —
     there's no headroom left to push them "more vivid" without either
     losing AA contrast or drifting hue toward a neighboring status color.
     The muddy/dull perception reported against dark mode's bright step-3
     colors is a real, physical consequence of dark-saturated-on-white
     vs. light-saturated-on-black, not a fixable token value — see ADR 0021.
     (The `-subtle-bg` mix-space fix above is the actual bug fix for the
     "colors look off" report; these three pairs were already correct.) */
  --status-ok-solid: oklch(49% 0.19 145);
  --status-ok-text: oklch(42% 0.17 145);
  --status-warning-solid: oklch(49% 0.19 75);
  --status-warning-text: oklch(42% 0.17 75);
  --status-error-solid: oklch(49% 0.19 25);
  --status-error-text: oklch(42% 0.17 25);
  --status-neutral-solid: var(--gray-8);
  --status-neutral-text: var(--gray-10);

  --code-comment-text: var(--text-tertiary);
  --code-punctuation-text: var(--text-secondary);
  --code-tag-text: oklch(42% 0.17 25);
  --code-class-text: oklch(42% 0.08 100);
  --code-number-text: oklch(42% 0.17 75);
  --code-keyword-text: oklch(42% 0.22 270);
  --code-string-text: oklch(42% 0.17 145);
}
