/*
 * tokens.css — DayParter's ONE declaration site for every --dp- primitive and
 * every hex value in the repository. Canonical for values (DESIGN.md doc 328,
 * "Living Artifacts": if a hex here and a hex in DESIGN.md diverge, this file is
 * the implementation of record). Adding a token not named in DESIGN.md's Token
 * Vocabulary is a contract change that must land in DESIGN.md in the same commit
 * (Hard Rule 12).
 *
 * HOW IT SHIPS: served UNHASHED at the stable path /static/tokens.css. It is never
 * imported from a Svelte component (that would bundle + content-hash it); it is
 * linked from app.html and emitted into the build root by a Vite plugin. See
 * web/src/app.html and web/vite.config.ts.
 *
 * TWO TIERS (DESIGN.md "Token Vocabulary"):
 *   - Primitives  --dp-<hue>-<step>  — theme-invariant raw values, declared and
 *     referenced ONLY in this file.
 *   - Semantics   --dp-<category>-<role>  — theme-dependent aliases onto primitives;
 *     the only tier component code may use.
 *
 * THEMING: `dark` is the default (emitted when the user has expressed no preference)
 * and, per Hard Rule 13, the only theme whose completeness is a v1 gate. Light values
 * are fully DECLARED here (retrofit stays cheap) but NOT SHIPPED at v1 — there is no
 * theme toggle and no preference detection. Dark semantics are the :root defaults;
 * [data-theme="light"] overrides them.
 *
 * All contrast ratios cited in comments are the measured WCAG 2.1 figures from
 * DESIGN.md; web/tests/contrast.test.ts RE-COMPUTES them from the resolved values
 * below rather than trusting the comments.
 */

/* ========================================================================== *
 * PRIMITIVES — theme-invariant raw ramp values. One shared 11-step OKLCH
 * lightness ladder across every hue (step numbers are cross-hue comparable).
 * ========================================================================== */
:root {
	/* --dp-navy-* — ground, chrome, structure. Brand pin: navy-950. */
	--dp-navy-50: #f3f7ff;
	--dp-navy-100: #e4edff;
	--dp-navy-200: #d3e2ff;
	--dp-navy-300: #b7cdf6;
	--dp-navy-400: #a1b8e4;
	--dp-navy-500: #8ca5d3;
	--dp-navy-600: #6e88ba;
	--dp-navy-700: #556fa4;
	--dp-navy-800: #39548a;
	--dp-navy-900: #1c366d;
	--dp-navy-950: #03184f;

	/* --dp-green-* — positive data, success. Brand pin: green-500. */
	--dp-green-50: #e8ffe1;
	--dp-green-100: #d5f9ca;
	--dp-green-200: #c2f2b4;
	--dp-green-300: #9ce286;
	--dp-green-400: #7cd060;
	--dp-green-500: #67bd4a;
	--dp-green-600: #4d9f2f;
	--dp-green-700: #378416;
	--dp-green-800: #236600;
	--dp-green-900: #144500;
	--dp-green-950: #072700;

	/* --dp-lime-* — peak data, top of the heatmap scale. Brand pin: lime-200. */
	--dp-lime-50: #eefddb;
	--dp-lime-100: #e1f6c5;
	--dp-lime-200: #c6f57a;
	--dp-lime-300: #b6dc7b;
	--dp-lime-400: #9ec954;
	--dp-lime-500: #8cb543;
	--dp-lime-600: #72972a;
	--dp-lime-700: #5c7d10;
	--dp-lime-800: #446000;
	--dp-lime-900: #2d4100;
	--dp-lime-950: #172400;

	/* --dp-blue-* — interactive ONLY, never data (Hard Rule 4). Brand pin: blue-600. */
	--dp-blue-50: #eef9ff;
	--dp-blue-100: #daf1ff;
	--dp-blue-200: #c3e8ff;
	--dp-blue-300: #92d6ff;
	--dp-blue-400: #5fc3fc;
	--dp-blue-500: #43b0ea;
	--dp-blue-600: #0293ce;
	--dp-blue-700: #0079ab;
	--dp-blue-800: #005c84;
	--dp-blue-900: #003e5b;
	--dp-blue-950: #002234;

	/* --dp-slate-* — neutral; navy-hued, near-zero chroma. */
	--dp-slate-50: #f5f7fb;
	--dp-slate-100: #eaedf3;
	--dp-slate-200: #dee2e9;
	--dp-slate-300: #c8ccd4;
	--dp-slate-400: #b3b7c0;
	--dp-slate-500: #a0a5ae;
	--dp-slate-600: #838892;
	--dp-slate-700: #6b707a;
	--dp-slate-800: #505560;
	--dp-slate-900: #343944;
	--dp-slate-950: #1a1f28;

	/* --dp-amber-* — status hue only (OKLCH 77deg). Only the steps DESIGN.md binds
	 * to a role carry a value; unlisted steps are reserved and must not be invented
	 * (Hard Rule 12). */
	--dp-amber-300: #fdc05c;
	--dp-amber-400: #f1a700;
	--dp-amber-600: #b47c00;
	--dp-amber-700: #956600;
	--dp-amber-900: #4e3300;

	/* --dp-red-* — status hue only (OKLCH 27deg). Same reserved-step rule as amber. */
	--dp-red-300: #ffb7ae;
	--dp-red-400: #ff968a;
	--dp-red-500: #ff7266;
	--dp-red-600: #e24f46;
	--dp-red-700: #c5312e;
	--dp-red-900: #710006;

	/* --dp-heat-* — the heatmap sequential scale. Seven steps, navy -> green -> lime,
	 * interpolated in OKLab with an evenly spaced lightness ladder (L 0.330 -> 0.912).
	 * heat-5 is brand green to within one unit/channel; heat-7 is brand lime exactly.
	 * These are DATA colors and may appear ONLY inside heatmap components (Hard Rule 4). */
	--dp-heat-1: #14306f;
	--dp-heat-2: #1f5570;
	--dp-heat-3: #34786c;
	--dp-heat-4: #4c9a61;
	--dp-heat-5: #66bc4b;
	--dp-heat-6: #97d962;
	--dp-heat-7: #c6f57a;

	/* ---- dimension: 4px base, 4/8 rhythm, rem-expressed at a 16px root ---- */
	--dp-space-0: 0;
	--dp-space-1: 0.25rem;
	--dp-space-2: 0.5rem;
	--dp-space-3: 0.75rem;
	--dp-space-4: 1rem;
	--dp-space-6: 1.5rem;
	--dp-space-8: 2rem;
	--dp-space-12: 3rem;
	--dp-space-16: 4rem;
	--dp-space-24: 6rem;

	--dp-radius-none: 0;
	--dp-radius-sm: 0.25rem; /* heatmap cell */
	--dp-radius-md: 0.5rem; /* controls */
	--dp-radius-lg: 0.75rem; /* cards */
	--dp-radius-full: 9999px; /* pills, avatars */

	/* The only sanctioned px exceptions (Hard Rule 14). */
	--dp-border-width: 1px;
	--dp-border-width-strong: 2px;
	--dp-focus-ring-width: 2px;
	--dp-focus-ring-offset: 2px;

	--dp-cell-size: 2rem;
	--dp-cell-size-compact: 1.25rem; /* mobile/PWA density */
	--dp-cell-gap: 0.125rem;
	--dp-target-min: 2.75rem; /* 44px minimum touch target on the PWA surface */

	--dp-container-sm: 40rem;
	--dp-container-md: 48rem;
	--dp-container-lg: 64rem;
	--dp-container-xl: 80rem;

	/* ---- typography: one interface family, one figure family ---- */
	--dp-font-sans: Inter, 'Inter var', system-ui, -apple-system, 'Segoe UI', sans-serif;
	--dp-font-mono: 'JetBrains Mono', ui-monospace, 'SF Mono', Menlo, monospace;

	--dp-font-size-2xs: 0.6875rem; /* heatmap in-cell numerals + axis labels ONLY */
	--dp-font-size-xs: 0.75rem;
	--dp-font-size-sm: 0.875rem; /* smallest size permitted for body copy */
	--dp-font-size-md: 1rem;
	--dp-font-size-lg: 1.125rem;
	--dp-font-size-xl: 1.375rem;
	--dp-font-size-2xl: 1.75rem;
	--dp-font-size-3xl: 2.25rem;

	--dp-font-weight-regular: 400;
	--dp-font-weight-medium: 500; /* minimum weight for text on a --dp-heat-* fill */
	--dp-font-weight-semibold: 600;
	--dp-font-weight-bold: 700;

	--dp-line-height-tight: 1.15;
	--dp-line-height-snug: 1.3;
	--dp-line-height-normal: 1.5; /* body minimum (WCAG 1.4.12) */
	--dp-line-height-relaxed: 1.7;

	--dp-letter-spacing-tight: -0.01em;
	--dp-letter-spacing-normal: 0;
	--dp-letter-spacing-wide: 0.06em; /* small-caps status labels only */

	--dp-font-feature-tabular: 'tnum' 1, 'zero' 1;

	/* ---- duration + easing ---- */
	--dp-duration-instant: 0ms; /* the prefers-reduced-motion value for EVERY transition */
	--dp-duration-fast: 120ms;
	--dp-duration-base: 200ms;
	--dp-duration-slow: 320ms;
	--dp-duration-deliberate: 600ms; /* high-consequence confirmations only */
	--dp-ease-standard: cubic-bezier(0.2, 0, 0, 1);
	--dp-ease-decelerate: cubic-bezier(0, 0, 0, 1); /* enter */
	--dp-ease-accelerate: cubic-bezier(0.3, 0, 1, 1); /* exit */
}

/* ========================================================================== *
 * SEMANTIC ALIASES — DARK (default). Component code uses ONLY this tier.
 * dark worst-case surface = --dp-surface-overlay #12306E (the lightest dark
 * surface); every foreground is proven against it in contrast.test.ts.
 * ========================================================================== */
:root {
	/* surfaces (grounds) */
	--dp-surface-base: var(--dp-navy-950); /* page ground */
	--dp-surface-raised: #0a2159; /* cards, panels */
	--dp-surface-overlay: #12306e; /* modals, popovers, sheets — dark worst case */
	--dp-surface-sunken: #02123d; /* wells, the heatmap gutter */

	/* borders */
	--dp-border-subtle: #2a3f74; /* dividers — decorative only, exempt from the 3:1 floor */
	--dp-border-strong: var(--dp-navy-700); /* input borders, control outlines (base 3.36) */

	/* text */
	--dp-text-primary: var(--dp-navy-50); /* 15.68 base / 11.69 overlay */
	--dp-text-secondary: var(--dp-navy-300); /* 10.48 / 7.81 */
	--dp-text-muted: var(--dp-navy-500); /* 6.76 / 5.04 — lowest sanctioned body */
	--dp-text-disabled: var(--dp-navy-700); /* EXEMPT (WCAG 1.4.3) — disabled labels only */
	--dp-text-on-action: #ffffff; /* filled-button label (sits on the fill) */

	/* actions (blue = interactive only) */
	--dp-action-primary: var(--dp-blue-400); /* links, text buttons — 8.58 / 6.39 */
	--dp-action-primary-hover: var(--dp-blue-300); /* 10.65 / 7.94 */
	--dp-action-primary-fill: var(--dp-blue-700); /* filled primary ground — white BODY label 4.86; fill edge 3.47 on base */
	--dp-focus-ring: var(--dp-blue-400); /* 8.58 / 6.39 — never removed (Hard Rule 8) */

	/* status */
	--dp-status-success: var(--dp-green-400); /* running, healthy — 8.86 / 6.60 */
	--dp-status-warning: var(--dp-amber-400); /* expiring, drift, entitlement — 8.23 / 6.13 */
	--dp-status-danger: var(--dp-red-400); /* expired, revoked, breaker, errors — 8.00 / 5.96 */
	--dp-status-danger-fill: var(--dp-red-700); /* destructive ground — white BODY label 5.45; fill edge 3.09 on base */
	--dp-status-danger-fill-light: var(--dp-red-300); /* body-size destructive fill + navy label */
	--dp-status-neutral: var(--dp-slate-400); /* not-automated, never-connected — 8.38 / 6.24 */
	--dp-status-info: var(--dp-navy-300); /* reverting, pending queued reverts — 10.48 / 7.81 */

	/* shadows — navy-tinted, never neutral black (a black shadow on navy reads as a hole) */
	--dp-shadow-none: none;
	--dp-shadow-sm: 0 1px 2px rgba(1, 8, 28, 0.55);
	--dp-shadow-md: 0 4px 12px rgba(1, 8, 28, 0.6);
	--dp-shadow-lg: 0 12px 32px rgba(1, 8, 28, 0.7);
	--dp-shadow-focus: 0 0 0 2px var(--dp-surface-base), 0 0 0 4px var(--dp-focus-ring);

	/* heat-cell label default (overridden per step below). The computed token
	 * governs EVERY mark drawn on a heat fill — numeral, stipple, ring, glyph. */
	--dp-heat-cell-label: #ffffff;

	/* non-data heatmap state tokens — texture-encoded, hue-free (Hard Rule 6).
	 * no-data: an achromatic slate-900 fill (measured against #343944, NOT the page
	 * ground) carrying a compliant hatch + a non-numeric em-dash glyph. */
	--dp-heat-nodata: var(--dp-slate-900); /* the no-data cell fill */
	--dp-heat-nodata-hatch: var(--dp-slate-600); /* 45deg hatch — 3.25 on the fill */
	--dp-heat-nodata-glyph: var(--dp-slate-300); /* centered em dash — 7.19 on the fill */
	/* low-sample stipple + restated ring both resolve through --dp-heat-cell-label,
	 * so each inherits the per-step label ratios (worst case 4.89) instead of a fixed
	 * color that is only correct at one end of the ramp. */
	--dp-heat-lowsample: var(--dp-heat-cell-label);
	--dp-heat-restated: var(--dp-heat-cell-label);

	/* schedule-preview surface — the read-only 24×7 on/off proposal grid. These are
	 * SEMANTIC aliases so the schedule components resolve neither a raw primitive
	 * (Hard Rule 1) nor a --dp-heat-* data token (Hard Rule 4): the grid is an
	 * on/off proposal surface, NOT a heat-data visualization, so it must not borrow
	 * the heatmap ramp. Values map to the existing palette so the shipped visual is
	 * byte-for-byte unchanged; state is carried on FILL and GLYPH, never hue alone.
	 * ON — stays on (solid fill, filled-dot glyph). */
	--dp-schedule-on-fill: var(--dp-heat-5); /* solid ON fill */
	--dp-schedule-on-glyph: var(--dp-navy-950); /* glyph on the light ON fill */
	/* kept-ON despite a thin sample — the no-data fill + hollow-ring glyph, so
	 * "held" reads distinct from ON-performs-well and never as "chosen". */
	--dp-schedule-kept-fill: var(--dp-heat-nodata);
	--dp-schedule-kept-glyph: var(--dp-heat-nodata-glyph);
}

/* Explicit dark scope — identical to the :root defaults, present so an explicit
 * data-theme="dark" behaves the same as no attribute at all. */
[data-theme='dark'] {
	color-scheme: dark;
}

/* Dark surface-scoped override: --dp-border-strong fails the 3:1 floor on the
 * lightest dark surface (navy-700 on overlay = 2.51). "Further from the surface"
 * on a DARK ground means a LOWER step (lighter): navy-600 = 3.52 on overlay.
 * Applied under the overlay surface scope, never hand-picked in a component. */
[data-dp-surface='overlay'] {
	--dp-border-strong: var(--dp-navy-600);
}

/* ========================================================================== *
 * SEMANTIC ALIASES — LIGHT. Declared (Hard Rule 13) but NOT shipped at v1: no
 * theme toggle, no preference detection. Every value below is the light column of
 * DESIGN.md's semantic table verbatim; a divergence is a contract defect.
 * light worst-case surface = --dp-surface-sunken #EAEDF3 (the darkest light surface).
 * ========================================================================== */
[data-theme='light'] {
	color-scheme: light;

	/* surfaces */
	--dp-surface-base: #ffffff;
	--dp-surface-raised: var(--dp-slate-50); /* #F5F7FB */
	--dp-surface-overlay: #ffffff;
	--dp-surface-sunken: var(--dp-slate-100); /* #EAEDF3 — light worst case */

	/* borders */
	--dp-border-subtle: var(--dp-slate-200); /* decorative only, exempt */
	--dp-border-strong: var(--dp-slate-600); /* 3.56 base / 3.03 sunken */

	/* text */
	--dp-text-primary: var(--dp-navy-950); /* 16.83 / 14.35 */
	--dp-text-secondary: var(--dp-slate-800); /* 7.47 / 6.37 */
	--dp-text-muted: var(--dp-slate-700); /* 4.97 base; overridden to slate-800 on sunken */
	--dp-text-disabled: var(--dp-slate-500); /* EXEMPT — disabled labels only */
	--dp-text-on-action: #ffffff; /* white on blue-700 (4.86) */

	/* actions */
	--dp-action-primary: var(--dp-blue-700); /* 4.86 base; overridden to blue-800 on sunken */
	--dp-action-primary-hover: var(--dp-blue-800); /* 7.33 / 6.25 */
	--dp-action-primary-fill: var(--dp-blue-700); /* fill ground; white label at 4.86 */
	--dp-focus-ring: var(--dp-blue-600); /* 3.46 base; overridden to blue-700 on sunken/dividers */

	/* status */
	--dp-status-success: var(--dp-green-700); /* 4.69 base; overridden to green-800 on sunken */
	--dp-status-warning: var(--dp-amber-700); /* 5.03 base; overridden to amber-900 on sunken */
	--dp-status-danger: var(--dp-red-700); /* 5.45 / 4.65 */
	--dp-status-danger-fill: var(--dp-red-700); /* white label at 5.45 */
	--dp-status-danger-fill-light: var(--dp-red-300); /* fill + red-900 label + mandatory red-700 border */
	--dp-status-neutral: var(--dp-slate-700); /* 4.97 base; overridden to slate-800 on sunken */
	--dp-status-info: var(--dp-navy-800); /* 7.48 / 6.37 */

	/* shadows — navy-tinted, lighter alpha for the light ground */
	--dp-shadow-sm: 0 1px 2px rgba(3, 24, 79, 0.08);
	--dp-shadow-md: 0 4px 12px rgba(3, 24, 79, 0.1);
	--dp-shadow-lg: 0 12px 32px rgba(3, 24, 79, 0.14);
}

/* Light surface-scoped overrides on the darkest light surface (sunken). "Further
 * from the surface" on a LIGHT ground means a HIGHER step (darker). Each override
 * resolves to a primitive already declared above — no new hue, no new ramp step. */
[data-theme='light'] [data-dp-surface='sunken'] {
	--dp-text-muted: var(--dp-slate-800); /* 4.24 fail -> 6.37 */
	--dp-action-primary: var(--dp-blue-800); /* 4.14 fail -> 6.25 */
	--dp-focus-ring: var(--dp-blue-700); /* 2.95 fail -> 4.14 on sunken (and 3.74/3.02 on slate-200/300 dividers) */
	--dp-status-success: var(--dp-green-800); /* 4.00 fail -> 6.02 */
	--dp-status-warning: var(--dp-amber-900); /* 4.29 fail -> 9.96 */
	--dp-status-neutral: var(--dp-slate-800); /* 4.24 fail -> 6.37 */
}

/* ========================================================================== *
 * HEAT-CELL LABEL — the computed token. Its value switches at the step where a
 * white label drops below 4.5:1: white on steps 1-3 (>=5.19), navy on steps 4-7
 * (>=4.89, worst case). A later HeatCell sets [data-dp-heat="N"] and reads
 * --dp-heat-cell-fill + --dp-heat-cell-label; it must NOT hand-pick a label color.
 * ========================================================================== */
[data-dp-heat='1'] {
	--dp-heat-cell-fill: var(--dp-heat-1);
	--dp-heat-cell-label: #ffffff;
}
[data-dp-heat='2'] {
	--dp-heat-cell-fill: var(--dp-heat-2);
	--dp-heat-cell-label: #ffffff;
}
[data-dp-heat='3'] {
	--dp-heat-cell-fill: var(--dp-heat-3);
	--dp-heat-cell-label: #ffffff;
}
[data-dp-heat='4'] {
	--dp-heat-cell-fill: var(--dp-heat-4);
	--dp-heat-cell-label: var(--dp-navy-950);
}
[data-dp-heat='5'] {
	--dp-heat-cell-fill: var(--dp-heat-5);
	--dp-heat-cell-label: var(--dp-navy-950);
}
[data-dp-heat='6'] {
	--dp-heat-cell-fill: var(--dp-heat-6);
	--dp-heat-cell-label: var(--dp-navy-950);
}
[data-dp-heat='7'] {
	--dp-heat-cell-fill: var(--dp-heat-7);
	--dp-heat-cell-label: var(--dp-navy-950);
}

/* ========================================================================== *
 * MOTION — one global guard satisfies Hard Rule 15 for the whole app: under
 * prefers-reduced-motion, every transition/animation collapses to instant.
 * ========================================================================== */
@media (prefers-reduced-motion: reduce) {
	*,
	*::before,
	*::after {
		transition-duration: var(--dp-duration-instant) !important;
		animation-duration: 0.001ms !important;
		animation-iteration-count: 1 !important;
		scroll-behavior: auto !important;
	}
}
