/* ============================================================================================
   tokens.css — the design tokens, every theme, every accent, every appearance mode.

   The rule this file exists for: an appearance setting is ONE attribute on <html> plus token
   overrides in THIS file. No component ever knows which theme it is in. The moment a second
   place decides a colour, the contrast guarantee stops being checkable.

     <html data-theme="light|dim|dark" data-accent="…" data-contrast="high"
           data-magnify="large|larger" data-density="compact">

   Light is the base (:root). Dim and Dark override the neutral ramp. Every accent is declared
   once as a PAIR — an ink tone for the light family, a lit tone for Dark — and the theme block
   picks which half becomes --accent. Every foreground token clears 4.5:1 on every ground it is
   drawn on, in every theme and with every accent: test_theme_tokens.py (or check-contrast.mjs)
   is that arithmetic, read out of this file. Edit a value, run the check.

   To re-brand: change the default accent pair in :root, or
   re-tune the neutral ramp — then run the check. Do not add a colour anywhere else.
   ============================================================================================ */

/* Ferry: copied from backbone/starter/tokens.css on 2026-11-13. Two deliberate changes, both
   ADOPT.md "decide first" items: the default accent is BLUE (the :root pair below; green moved
   into a data-accent block so all six stay measured), and the typeface is live rather than
   commented out. The THEME is Dim and only Dim (#18), which is not this file's business: it is
   the static data-theme attribute on index.html and on hub/generate.py's pages. Light and Dark
   stay below, unread, so this sheet remains a diffable copy of the starter (Light is also the
   base Dim overrides) and the contrast test keeps measuring them. This one sheet is read by the console AND copied beside the public hub pages, so the two are
   one palette by construction. */

/* Your typeface. Self-host it (a strict CSP has font-src 'self'), one variable file per style so
   500/600 are drawn weights, and font-display: swap over a metric-close fallback stack. */
@font-face {
  font-family: 'Source Sans 3 Variable';
  font-style: normal;
  font-display: swap;
  font-weight: 200 900;
  src: url('/vendor/fonts/source-sans-3-latin-wght-normal.woff2') format('woff2-variations');
}
@font-face {
  font-family: 'Source Sans 3 Variable';
  font-style: italic;
  font-display: swap;
  font-weight: 200 900;
  src: url('/vendor/fonts/source-sans-3-latin-wght-italic.woff2') format('woff2-variations');
}

:root {
  color-scheme: light;

  /* Type: nine steps named by ROLE, not by pixel. A new element asks "which altitude is it",
     never "what size". Type is deliberately NOT on the density axis. */
  --font-sans: 'Source Sans 3 Variable', -apple-system, BlinkMacSystemFont, 'Segoe UI',
               'Helvetica Neue', Arial, sans-serif;
  --font-mono: ui-monospace, 'Cascadia Mono', Consolas, Menlo, monospace;
  --fs-3xs: 10px;   /* chart ticks, badge counters */
  --fs-2xs: 11px;   /* the uppercase eyebrow over a group */
  --fs-xs:  12px;   /* meta, chips, secondary lines */
  --fs-sm:  13px;   /* controls, dense table cells */
  --fs-md:  14px;   /* body */
  --fs-lg:  16px;   /* section headings, card titles, a lead paragraph */
  --fs-xl:  19px;   /* page headings */
  --fs-2xl: 23px;   /* a figure worth reading across the room */
  --fs-3xl: 28px;   /* display: the sign-in lockup, a dashboard greeting */

  /* Leading, as three intents. */
  --lh-tight: 1.25;   /* headings, and anything sized to fit rather than to be read */
  --lh-body:  1.55;   /* running text */
  --lh-ui:    1.4;    /* rows, cells, controls */

  /* Radius: three values plus a shape. */
  --r-sm:   6px;      /* controls: buttons, inputs, chips */
  --r-md:  10px;      /* cards, panels, the surfaces a page is made of */
  --r-lg:  16px;      /* things that float clear: dialogs, sheets, the sign-in box */
  --r-pill: 999px;

  /* The focus ring as two numbers. Anything that clips (a scroller) must reserve --focus-reach
     or it slices the ring off the field at its edge. */
  --focus-w:   2px;
  --focus-gap: 2px;
  --focus-reach: calc(var(--focus-w) + var(--focus-gap));

  /* Elevation: four altitudes, picked by how far the thing is from the page, not by how
     important it is. Two layers each: a tight contact shadow and a wide soft one. */
  --shadow-rgb: 44 36 28;   /* warm ink, not pure black */
  --elev-1: 0 1px 2px rgb(var(--shadow-rgb) / 0.06), 0 1px 3px rgb(var(--shadow-rgb) / 0.05);
  --elev-2: 0 1px 2px rgb(var(--shadow-rgb) / 0.06), 0 4px 12px rgb(var(--shadow-rgb) / 0.09);
  --elev-3: 0 2px 4px rgb(var(--shadow-rgb) / 0.06), 0 12px 28px rgb(var(--shadow-rgb) / 0.13);
  --elev-4: 0 4px 8px rgb(var(--shadow-rgb) / 0.08), 0 24px 56px rgb(var(--shadow-rgb) / 0.20);

  /* Neutral ramp — Light, the default. */
  --bg:        #faf8f5;   /* the page */
  --surface:   #f1ede7;   /* cards, panels, buttons */
  --hover:     #ebe5dc;   /* a row or button under the pointer */
  --border:    #d5cdc0;   /* the quiet rule between two things already visible */
  --text:      #2a2622;
  --text-muted: #635a50;
  --muted:     #635a50;   /* secondary text; clears 4.5:1 on every ground incl. --accent-tint */
  --on-accent: #ffffff;   /* what a filled accent control carries */

  /* Two borders, because they have two thresholds. A panel's edge can be a trace (a shadow does
     the separating); a text field has no shadow, so its border is the only thing announcing it
     is a control and must clear 3:1 (WCAG 1.4.11) on the harder of --bg / --surface. */
  --border-control: #8f8172;
  --hairline: color-mix(in srgb, var(--border) 55%, transparent);

  /* Accent pair — Ferry's default (blue). A data-accent block restates these four. */
  --accent-ink:   #1f5f96;
  --accent-ink-h: #184a76;
  --accent-lit:   #7fb8e6;
  --accent-lit-h: #9bcaf0;
  --accent:   var(--accent-ink);
  --accent-h: var(--accent-ink-h);

  /* Derived tint: the ground every selected row, pill, tab and nav item is drawn on. It is a
     GROUND, so the muted family is measured on it too. Mixed into --tint-base (not --bg) so a
     theme with a mid-grey page can tint towards paper instead. */
  --tint-base: var(--bg);
  --accent-tint: color-mix(in srgb, var(--accent) 18%, var(--tint-base));
  --accent-rule: color-mix(in srgb, var(--accent) 45%, var(--tint-base));

  /* Status — each ink clears 4.5:1 on the page AND on its own -bg fill. */
  --danger:     #a32c22;  --danger-bg:  #fbe6e2;
  --warn:       #764a0c;  --warn-bg:    #f8eeda;
  --info:       #1f5f96;  --info-bg:    #e2eefa;
  --success:    #2b6340;  --success-bg: #e2f0e6;

  /* Shell dimensions. */
  --hdr-h: 52px;
  --sidebar-w: 240px;

  /* The strips a notch and a home indicator occupy (viewport-fit=cover draws the app under
     them). 0 on any device without cutouts, so every use is unconditional. */
  --safe-t: env(safe-area-inset-top, 0px);
  --safe-r: env(safe-area-inset-right, 0px);
  --safe-b: env(safe-area-inset-bottom, 0px);
  --safe-l: env(safe-area-inset-left, 0px);

  /* Density: one multiplier over the vertical rhythm (page padding, section gaps, card padding,
     row height). At 1 every computed value is exactly what the rule states. */
  --density-scale: 1;

  /* Motion: two durations and one curve. Nothing moves on hover; panels rise 4px once. */
  --motion: 150ms;
  --motion-in: 180ms;
  --ease: cubic-bezier(0.2, 0, 0, 1);

  /* Published by the help button while it is on screen (see base.css): how far it reaches in
     from the right and up from the bottom, so a page's own corner control can clear it. */
  --help-r: 20px;
  --help-clear: 0px;
  --help-clear-b: 0px;
}

:root[data-density="compact"] { --density-scale: 0.58; }

/* Light is the base, so this block only anchors the explicit choice. */
:root[data-theme="light"] { color-scheme: light; }

/* Dim — a soft warm grey between Light and Dark. The tightest ground to write on, so the status
   inks are a step darker than Light's. Tints towards paper so a chip reads as lifted. */
:root[data-theme="dim"] {
  color-scheme: light;
  --bg:        #ddd6c9;
  --surface:   #e8e2d7;
  --border:    #b3a894;
  --text:      #24211d;
  --text-muted: #4e4740;
  --muted:     #4e4740;
  --hover:     #d1c9b9;
  --border-control: #7d7263;
  --tint-base: #f4efe6;

  --danger:     #9c2a20;  --danger-bg:  #f0dcd6;
  --warn:       #6f460b;  --warn-bg:    #eee0c6;
  --info:       #1c5789;  --info-bg:    #dbe6f2;
  --success:    #275b3b;  --success-bg: #dcebe0;
}

/* Dark — a warm charcoal, and the one place --accent points at the LIT half of the pair. */
:root[data-theme="dark"] {
  color-scheme: dark;
  --bg:        #1b1917;
  --surface:   #24211e;
  --border:    #423c37;
  --text:      #ece7e0;
  --text-muted: #a79e94;
  --muted:     #a79e94;
  --hover:     #2f2b27;
  --border-control: #746a61;
  --on-accent: #1b1917;
  --accent:   var(--accent-lit);
  --accent-h: var(--accent-lit-h);

  --danger:     #f0857a;  --danger-bg:  #3a201c;
  --warn:       #e0b45f;  --warn-bg:    #382b16;
  --info:       #7fb8e6;  --info-bg:    #1c2b3a;
  --success:    #7cc496;  --success-bg: #1c2f22;

  /* A shadow on a dark ground vanishes at the light ramp's alphas: pure black, ~triple. */
  --shadow-rgb: 0 0 0;
  --elev-1: 0 1px 2px rgb(0 0 0 / 0.30), 0 1px 3px rgb(0 0 0 / 0.20);
  --elev-2: 0 1px 2px rgb(0 0 0 / 0.30), 0 4px 12px rgb(0 0 0 / 0.34);
  --elev-3: 0 2px 4px rgb(0 0 0 / 0.30), 0 12px 28px rgb(0 0 0 / 0.42);
  --elev-4: 0 4px 8px rgb(0 0 0 / 0.34), 0 24px 56px rgb(0 0 0 / 0.55);
  --hairline: color-mix(in srgb, var(--border) 80%, transparent);
}

/* Selectable accents. Each declares BOTH tones; the theme decides which becomes --accent. */
:root[data-accent="green"] {
  --accent-ink: #2b6340; --accent-ink-h: #215033;
  --accent-lit: #7cc496; --accent-lit-h: #96d3ac;
}
:root[data-accent="purple"] {
  --accent-ink: #6a4a9c; --accent-ink-h: #543a7d;
  --accent-lit: #b79ae4; --accent-lit-h: #c9b0ee;
}
:root[data-accent="amber"] {
  --accent-ink: #7a4e0d; --accent-ink-h: #613d09;
  --accent-lit: #e0b45f; --accent-lit-h: #ecc87e;
}
:root[data-accent="rose"] {
  --accent-ink: #98304f; --accent-ink-h: #7d243f;
  --accent-lit: #ef9bb8; --accent-lit-h: #f6b5ca;
}
:root[data-accent="teal"] {
  --accent-ink: #10615d; --accent-ink-h: #0c4b48;
  --accent-lit: #63c6bd; --accent-lit-h: #82d6ce;
}

/* Magnify: a root zoom enlarges text AND controls with correct reflow in a px-based UI.
   Chromium does not scale vh for an ancestor zoom, so the fixed-height shell counter-scales. */
:root[data-magnify="large"]  { zoom: 1.15; }
:root[data-magnify="larger"] { zoom: 1.30; }
:root[data-magnify="large"]  .app { height: calc(100vh / 1.15); height: calc(100svh / 1.15); }
:root[data-magnify="larger"] .app { height: calc(100vh / 1.30); height: calc(100svh / 1.30); }

/* High contrast is a MODE, not a darker palette. Text and borders go to the extremes over
   whichever theme is active — and depth goes flat, because a soft shadow is exactly the
   low-contrast cue this mode exists to remove (Windows' forced colours discard shadows too).
   The hairline goes back to a real border, so exactly one boundary mechanism is always drawn. */
:root[data-contrast="high"] {
  --bg:         #ffffff;
  --surface:    #ffffff;
  --border:     #201d1a;
  --text:       #000000;
  --text-muted: #201d1a;
  --muted:      #201d1a;
  --hover:      #ebe5dc;
  --tint-base:  var(--bg);
}
:root[data-contrast="high"][data-theme="dark"] {
  --bg:         #000000;
  --surface:    #131110;
  --border:     #f2ede6;
  --text:       #ffffff;
  --text-muted: #ded7cf;
  --muted:      #ded7cf;
  --hover:      #2b2724;
}
:root[data-contrast="high"] {
  --elev-1: none;
  --elev-2: none;
  --elev-3: none;
  --elev-4: none;
  --hairline: var(--border);
  --border-control: var(--border);
}
:root[data-contrast="high"] :focus-visible {
  outline: var(--focus-w) solid var(--text);
  outline-offset: var(--focus-gap);
}

/* Paper, said once. A theme is how somebody reads a SCREEN; printed in Dark a page is pale ink
   on white (browsers drop backgrounds and keep ink). Tripled selector: a theme block is (0,2,0)
   and high contrast over it (0,3,0), and a media query adds no weight. */
@media print {
  :root:root:root {
    color-scheme: light;
    --bg: #fff; --surface: #fff; --hover: #fff; --tint-base: #fff;
    --text: #000; --text-muted: #333; --muted: #333;
    --border: #999; --hairline: #bbb; --border-control: #999;
    --accent: var(--accent-ink); --accent-h: var(--accent-ink-h); --on-accent: #fff;
    --danger: #a32c22; --warn: #764a0c; --info: #1f5f96; --success: #2b6340;
    --elev-1: none; --elev-2: none; --elev-3: none; --elev-4: none;
  }
}
