/* ============================================================
   palette.css — the colour system
   Loaded AFTER app.css so it is authoritative for colour. app.css still carries
   its old colour declarations; they are now dead but harmless, and should be
   deleted once this has been eyeballed in both schemes.

   THE IDEA
   Light and dark are not two designs. They are the same five tonal ramps read at
   different tones. Every "what should the dark version of this be?" question has
   a mechanical answer, so the two schemes cannot drift apart the way hand-picked
   values do.

   Ramps are written in oklch() because the relationships are then legible in the
   source: same hue down a column, lightness marching in even perceptual steps,
   chroma peaking mid-ramp and tapering at both ends. In hex this is unreadable
   and unmaintainable. (oklch needs Chrome 111+/Safari 15.4+, which this app
   already assumes elsewhere via color-mix.)

   Seed is the brand navy #03173d = oklch(0.212 0.079 261).

   THE IMPORTANT ROW IS "neutral".
   Its chroma is not zero. Neutrals carrying a few points of the seed's chroma is
   what makes surfaces feel like one material; pure grey behind pure white reads
   as absence, and that flatness was the thing no amount of shadow could fix.
   ============================================================ */

:root {
    /* ---- Primary: brand hue, full chroma ---- */
    --p-10: oklch(0.18 0.055 261);
    --p-20: oklch(0.28 0.090 261);
    --p-30: oklch(0.38 0.120 261);
    --p-40: oklch(0.47 0.145 261);
    --p-50: oklch(0.56 0.155 261);
    --p-60: oklch(0.65 0.145 261);
    --p-70: oklch(0.74 0.120 261);
    --p-80: oklch(0.82 0.090 261);
    --p-90: oklch(0.90 0.050 261);
    --p-95: oklch(0.95 0.026 261);
    --p-100: oklch(1 0 261);

    /* ---- Secondary: same hue, ~a third of the chroma ---- */
    --s-10: oklch(0.18 0.020 261);
    --s-20: oklch(0.28 0.030 261);
    --s-30: oklch(0.38 0.040 261);
    --s-40: oklch(0.47 0.048 261);
    --s-80: oklch(0.82 0.032 261);
    --s-90: oklch(0.90 0.020 261);

    /* ---- Tertiary: hue 200. Rotated toward the cyan already in the wordmark
            (#00d4ff) rather than M3's default +60, so the accent colour the brand
            already uses becomes part of the system instead of an outlier. ---- */
    --t-10: oklch(0.18 0.040 200);
    --t-20: oklch(0.28 0.060 200);
    --t-30: oklch(0.38 0.080 200);
    --t-40: oklch(0.47 0.095 200);
    --t-80: oklch(0.82 0.075 200);
    --t-90: oklch(0.90 0.045 200);

    /* ---- Neutral: seed hue at 3-14 thousandths of chroma. See header. ---- */
    /* Dark end carries roughly triple the chroma of the light end, and that
       asymmetry is deliberate. A dark surface absorbs far more chroma before it
       starts looking coloured, so at the light values these rungs read as plain
       grey-black and the brand navy disappeared from dark mode entirely. The
       light rungs have the opposite constraint — see the note there. */
    --n-0: oklch(0 0 261);
    --n-4: oklch(0.10 0.020 261);
    --n-6: oklch(0.13 0.022 261);
    --n-10: oklch(0.18 0.024 261);
    --n-12: oklch(0.21 0.025 261);
    --n-17: oklch(0.26 0.026 261);
    --n-20: oklch(0.29 0.026 261);
    --n-22: oklch(0.31 0.026 261);
    --n-24: oklch(0.33 0.026 261);
    /* Light end held to a whisper of chroma — enough that the greys are warm
       rather than dead, not enough to read as a blue page.

       Worth knowing if you retune: the flat look was caused by the LIGHTNESS gap
       between page and card, not by this. Chroma got raised at the same time as
       the gap was widened, which over-corrected into a visibly blue background.
       The gap is the load-bearing part; this row is taste, and can go to 0.002
       for fully neutral greys without reintroducing flatness. */
    --n-90: oklch(0.905 0.007 261);
    --n-92: oklch(0.925 0.006 261);
    --n-94: oklch(0.945 0.006 261);
    --n-96: oklch(0.962 0.005 261);
    --n-98: oklch(0.978 0.004 261);
    --n-100: oklch(1 0 261);

    /* ---- Neutral variant: a touch more chroma, for outlines and muted text ---- */
    --nv-30: oklch(0.38 0.022 261);
    --nv-50: oklch(0.56 0.022 261);
    --nv-60: oklch(0.65 0.020 261);
    --nv-80: oklch(0.82 0.016 261);
    --nv-90: oklch(0.90 0.012 261);

    /* ---- Semantic ramps ---- */
    --e-10: oklch(0.18 0.070 27);
    --e-20: oklch(0.28 0.110 27);
    --e-30: oklch(0.38 0.145 27);
    --e-40: oklch(0.47 0.175 27);
    --e-80: oklch(0.82 0.090 27);
    --e-90: oklch(0.90 0.055 27);

    --g-10: oklch(0.18 0.050 150);
    --g-20: oklch(0.28 0.075 150);
    --g-30: oklch(0.38 0.100 150);
    --g-40: oklch(0.47 0.120 150);
    --g-80: oklch(0.82 0.100 150);
    --g-90: oklch(0.90 0.060 150);

    --w-10: oklch(0.18 0.045 80);
    --w-20: oklch(0.28 0.070 80);
    --w-30: oklch(0.38 0.090 80);
    --w-40: oklch(0.47 0.110 80);
    --w-80: oklch(0.82 0.110 80);
    --w-90: oklch(0.90 0.070 80);
}

/* ============================================================
   ROLES — light
   Read the ramps at M3's light tones. Never reference a --p-NN directly from a
   component; reference a role, so dark mode follows for free.
   ============================================================ */
:root {
    --primary: var(--p-40);
    --on-primary: var(--p-100);
    --primary-container: var(--p-90);
    --on-primary-container: var(--p-10);

    --secondary: var(--s-40);
    --on-secondary: var(--n-100);
    --secondary-container: var(--s-90);
    --on-secondary-container: var(--s-10);

    --tertiary: var(--t-40);
    --on-tertiary: var(--n-100);
    --tertiary-container: var(--t-90);
    --on-tertiary-container: var(--t-10);

    --error: var(--e-40);
    --on-error: var(--n-100);
    --error-container: var(--e-90);
    --on-error-container: var(--e-10);

    --success: var(--g-40);
    --success-container: var(--g-90);
    --on-success-container: var(--g-10);

    --warning: var(--w-40);
    --warning-container: var(--w-90);
    --on-warning-container: var(--w-10);

    /* DELIBERATE DEPARTURE FROM M3's LIGHT MAPPING.
       M3 puts surface at N98 and its containers BELOW it, so cards sit a shade
       darker than the page and shadow does the separating. Followed literally
       that gave a 1.6% lightness gap between card and background — invisible,
       and the reason this scheme read as flat.

       Ground the page at N94 instead and let cards be N100. White cards on a
       tinted ground is a ~5.5% gap, it is what most shipped Android apps
       actually do (Gmail, Play Store), and it lets the neutral tint show. */
    --surface: var(--n-94);
    --on-surface: var(--n-10);
    --on-surface-variant: var(--nv-30);

    --surface-container-lowest: var(--n-100);
    --surface-container-low: var(--n-100);
    --surface-container: var(--n-98);
    --surface-container-high: var(--n-96);
    --surface-container-highest: var(--n-92);

    --outline: var(--nv-50);
    --outline-variant: var(--nv-80);

    /* Brand bar sits deliberately OUTSIDE the tonal system. Forcing #03173d into
       the ramp would wash it out, and plenty of apps hold their brand bar apart
       from the scheme. It gets its own pair rather than borrowing surface roles. */
    --brand-surface: #03173d;
    --on-brand-surface: #ffffff;
}

/* ============================================================
   ROLES — dark
   The same ramps, different tones. Note primary goes UP (40 -> 80) while surface
   goes DOWN (98 -> 6): that inversion is the whole mechanism.
   ============================================================ */
/* The `body` half of this selector is not decorative. app.css declares its dark
   tokens on `html[data-theme='dark'], html[data-theme='dark'] body`, and that
   second selector is specificity (0,1,2) — higher than a bare
   html[data-theme='dark'] at (0,1,1). Since body is an ancestor of every
   component, its declarations win and cascade down, so this whole file was being
   ignored in dark mode no matter what load order said. Light was unaffected
   because both files use plain :root there.
   Delete app.css's dark colour block and this can go back to one selector. */
html[data-theme='dark'],
html[data-theme='dark'] body {
    --primary: var(--p-80);
    --on-primary: var(--p-20);
    --primary-container: var(--p-30);
    --on-primary-container: var(--p-90);

    --secondary: var(--s-80);
    --on-secondary: var(--s-20);
    --secondary-container: var(--s-30);
    --on-secondary-container: var(--s-90);

    --tertiary: var(--t-80);
    --on-tertiary: var(--t-20);
    --tertiary-container: var(--t-30);
    --on-tertiary-container: var(--t-90);

    /* This pair is why the old scheme had a bug: --color-error was #7a2523 in
       dark, a background red used as text, so the destructive action label had to
       be hardcoded to #ff6b6b. Here --error is a tone chosen to be legible on a
       dark surface by construction, and the container is separate. */
    --error: var(--e-80);
    --on-error: var(--e-20);
    --error-container: var(--e-30);
    --on-error-container: var(--e-90);

    --success: var(--g-80);
    --success-container: var(--g-30);
    --on-success-container: var(--g-90);

    --warning: var(--w-80);
    --warning-container: var(--w-30);
    --on-warning-container: var(--w-90);

    --surface: var(--n-6);
    --on-surface: var(--n-90);
    --on-surface-variant: var(--nv-80);

    --surface-container-lowest: var(--n-4);
    --surface-container-low: var(--n-10);
    --surface-container: var(--n-12);
    --surface-container-high: var(--n-17);
    --surface-container-highest: var(--n-22);

    --outline: var(--nv-60);
    --outline-variant: var(--nv-30);

    --brand-surface: #0e1626;
    --on-brand-surface: #ffffff;
}

/* ============================================================
   LEGACY ALIASES
   --color-surface alone has ~650 usages; renaming them would be a 2000-line diff
   with no behavioural payoff. Instead the old names are redefined in terms of the
   new roles, so every existing rule keeps working while the system underneath
   changes. Migrate components to role names opportunistically, not in one go.
   ============================================================ */
:root {
    --color-bg: var(--surface);
    /* surface-container-lowest, not -low: cards want the widest separation from
       the page they sit on. See the departure note above. */
    --color-surface: var(--surface-container-lowest);
    --color-text: var(--on-surface);
    --color-text-secondary: var(--on-surface-variant);
    --color-muted-stroke: var(--outline-variant);

    --color-accent: var(--primary);
    --color-accent-contrast: var(--on-primary);
    --color-accent-soft: var(--primary-container);
    /* Hover moves one step DOWN the ramp in light (darker), and one step UP in
       dark (lighter) — see the dark block. */
    --color-accent-hover: var(--p-30);

    --color-link: var(--primary);
    --color-outline: var(--primary);
    --color-primary: var(--primary);
    --color-primary-border: var(--primary);
    --color-error: var(--error);

    --surface-2: var(--surface-container-high);
    --focus-ring: 0 0 0 3px oklch(0.47 0.145 261 / 0.30);

    --navbar-bg: var(--brand-surface);
    --navbar-text: var(--on-brand-surface);
}

/* Same specificity note as the roles block above. */
html[data-theme='dark'],
html[data-theme='dark'] body {
    --color-bg: var(--surface);
    /* -high, not -low. The 5% gap between surface (n-6, L 0.13) and
       surface-container-low (n-10, L 0.18) was not enough to separate a card from
       the page on a real screen — the ladder's rungs are close together at the
       dark end and ambient light closes what is left. -high (n-17, L 0.26) doubles
       the page's lightness, which reads the way Android dark mode does: a
       near-black ground with distinctly lifted cards. */
    --color-surface: var(--surface-container-high);
    --color-text: var(--on-surface);
    --color-text-secondary: var(--on-surface-variant);
    --color-muted-stroke: var(--outline-variant);

    --color-accent: var(--primary);
    --color-accent-contrast: var(--on-primary);
    --color-accent-soft: var(--primary-container);
    --color-accent-hover: var(--p-90);

    --color-link: var(--primary);
    --color-outline: var(--primary);
    --color-primary: var(--primary);
    --color-primary-border: var(--primary);
    --color-error: var(--error);

    /* Has to clear --color-surface, which is now -high in dark. An inset fill the
       same tone as the card it sits on is invisible. */
    --surface-2: var(--surface-container-highest);
    --focus-ring: 0 0 0 3px oklch(0.82 0.090 261 / 0.35);

    --navbar-bg: var(--brand-surface);
    --navbar-text: var(--on-brand-surface);
}
