/* ============================================================================
   haul-palette.css — Wave 80. THE dual-palette contract for every Haul IQ
   surface (marketing / auth / app shell / portals). Served at
   /static/vendor/haul-palette.css; linked FIRST in <head>, above each page's
   own <style>, so a page can still refine a token locally.

   WHY THIS FILE EXISTS
   --------------------
   Waves 73/74 unified thirteen drifting surfaces onto ONE canonical palette —
   and that palette is LIGHT. Measured on 2026-09-07: all 18 shipped pages
   declare --bg-0 #f7f9fc; there was no dark half and no way to switch. So the
   founder's "there is a light theme as well which is easier for the eyes" had
   no counterpart to be a choice AGAINST. This file supplies the missing half
   and the switch, without moving the default look one pixel.

   THE CONTRACT
   ------------
     * LIGHT is the default and stays on each page's bare :root — untouched.
       Nothing here changes what ships when no preference exists.
     * DARK applies when the OS asks for it and no explicit choice is stored:
       @media (prefers-color-scheme: dark) { :root:not([data-theme]) }
     * An explicit choice WINS IN BOTH DIRECTIONS and beats the OS:
       :root[data-theme="light"] / :root[data-theme="dark"]
       (stamped pre-paint from localStorage['haul.theme.v1'] by the one-line
       boot script in every page's <head>; toggled by haul-palette.js.)

   WHY !important
   --------------
   Not decoration and not laziness. Two legacy runtime appliers write the LIGHT
   palette onto <html> as INLINE custom properties on every boot:
     * tms.html  _applyVars()  → --bg-0..3 --text-0..2 --border --border-bright
                                 --accent --accent-dim --accent-ink --accent-rgb
     * vendor/haul-theme.js    → --border --accent --warn (+ legacy aliases)
   A normal stylesheet declaration loses to an inline one, so without
   !important the dark palette would be silently overwritten at boot on the app
   shell and the driver portal — the exact "computes-then-discards" failure this
   codebase keeps finding. An IMPORTANT author declaration beats a normal inline
   one, so these win. A page that must refine a token does it in its OWN <style>
   (later in document order, same specificity + importance) — see tms.html's
   --accent-ink note.

   AND IT MUST DO SO UNDER EVERY SELECTOR FORCED HERE, NOT JUST THE DARK ONES.
   Section 3 below forces the light palette at !important too, so a page whose
   token means something DIFFERENT from this file has to restate it under all
   three: :root[data-theme="dark"], the OS-dark @media rule, AND
   :root[data-theme="light"]. Wave 80 restated only the two dark ones on
   tms.html and shipped a 2.21:1 button label to anyone who explicitly picked
   light. F1/F2/F3 in src/__tests__/theme-light-contrast-smoke.ts now model the
   cascade in all four states and fail if a surface misses one.

   CONTRAST: every foreground/background pair in BOTH palettes is measured by
   src/__tests__/theme-light-contrast-smoke.ts. That test is the proof; this
   comment is not.
   ============================================================================ */

/* ---------------------------------------------------------------------------
   1. LIGHT — the shipped default, restated here so this file is the complete
   token contract on its own. Bare :root, NORMAL priority: it loads before each
   page's <style>, so any page-local light value still wins. Values are verbatim
   the canonical light contract (site-css.txt and all 18 pages agree to the digit).
   --------------------------------------------------------------------------- */
:root {
  --bg-0:#f7f9fc; --bg-1:#ffffff; --bg-2:#f1f5fa; --bg-3:#e7edf5;
  --text-0:#0d1520; --text-1:#4a5a6f; --text-2:#5c6f88;
  --border:#dde5ef; --border-hot:#c3d0e0; --border-bright:#c3d0e0;
  --accent:#2da8ff; --accent-dim:#2da8ff40; --accent-rgb:45,168,255;
  --accent-ink:#0a66c2; --on-accent:#04202f;
  --green:#3ddc84; --green-ink:#147a46; --gold:#f5c518; --gold-ink:#8a6a00;
  --red:#c9271b; --error:#c9271b; --amber:#b45309; --warn:#b45309;
  --blue:#1a68c4; --indigo:#4f46e5; --gray:#5b6b7f;
  --row-zebra:rgba(13,21,32,.025); --row-hover:var(--bg-2);
  /* Surfaces that used to be literal near-whites in page CSS (Wave 80 routed
     them through tokens so they stop being white in dark mode). */
  --bg-0-rgb:247,249,252;                /* canvas as a triplet, so a page can keep
                                            its own alpha: rgba(var(--bg-0-rgb),.85) */
  --bg-1-rgb:255,255,255;                /* card surface as a triplet              */
  --info-fill:#e3f1ff;                   /* soft info wash (dashboard hero)        */
  --shadow-tint:rgba(16,24,40,.04);      /* hairline ambient shadow                */
}

/* ---------------------------------------------------------------------------
   2. DARK — the missing half. Felix's "steel" architecture applied to Haul IQ's
   own brand blue: page darker than cards (elevation = surface lift, never a
   heavy outline), hairline borders, one restrained accent, status hues lifted
   until each clears AA on bg-0/bg-1/bg-2.
   The token list is written ONCE here and reused verbatim by the three
   selectors below, so OS-dark and explicit-dark can never drift apart.
   --------------------------------------------------------------------------- */
:root[data-theme="dark"] {
  --bg-0:#131820!important;   /* page canvas                                    */
  --bg-1:#1a2029!important;   /* cards / raised surfaces — LIGHTER than canvas  */
  --bg-2:#222a35!important;   /* inset / subtle fill                            */
  --bg-3:#2b3541!important;   /* hover / active fill                            */
  --text-0:#eef2f7!important; /* primary   — 15.8:1 on bg-0                     */
  --text-1:#b9c4d2!important; /* secondary — 10.1:1 on bg-0                     */
  --text-2:#97a5b8!important; /* muted     —  7.1:1 on bg-0                     */
  --border:#2c3644!important; --border-hot:#3d4a5c!important; --border-bright:#3d4a5c!important;
  --accent:#2da8ff!important; --accent-dim:#2da8ff40!important; --accent-rgb:45,168,255!important;
  --accent-ink:#7cc4ff!important; /* brand blue as TEXT/links on dark — 9.5:1 on bg-0.
     NOTE the documented semantic split: tms.html consumes --accent-ink as the
     label colour ON accent fills, so tms.html re-points it to --on-accent in its
     own <style>. Every other surface uses it as the link colour. */
  --on-accent:#04202f!important;  /* label on a brand fill — 6.5:1 on --accent  */
  --green:#3ddc84!important; --green-ink:#4ade9b!important;
  --gold:#f5c518!important;  --gold-ink:#e0b53d!important;
  --red:#ff7a6d!important;   --error:#ff7a6d!important;
  --amber:#f0a83c!important; --warn:#f0a83c!important;
  --blue:#6cb2f7!important;  --indigo:#a5a0ff!important; --gray:#97a5b8!important;
  --row-zebra:rgba(255,255,255,.028)!important; --row-hover:var(--bg-2)!important;
  --bg-0-rgb:19,24,32!important; --bg-1-rgb:26,32,41!important;
  --info-fill:#1d2c3d!important;
  --shadow-tint:rgba(0,0,0,.34)!important;
  /* Elevation: on dark, lift comes from the surface ramp; shadows go deep and
     neutral instead of the light theme's soft ink. */
  --shadow-1:0 1px 2px rgba(0,0,0,.42),0 1px 1px rgba(0,0,0,.30)!important;
  --shadow-2:0 6px 16px rgba(0,0,0,.48),0 1px 3px rgba(0,0,0,.34)!important;
  --shadow-3:0 16px 44px rgba(0,0,0,.60),0 4px 14px rgba(0,0,0,.42)!important;
  --shadow-focus:0 0 0 3px rgba(45,168,255,.34)!important;
  color-scheme:dark!important;  /* native controls/scrollbars follow (beats the
                                   inline colorScheme the legacy applier sets) */
}

/* OS-dark, no explicit choice stored. Same token list, verbatim. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme]) {
    --bg-0:#131820!important; --bg-1:#1a2029!important; --bg-2:#222a35!important; --bg-3:#2b3541!important;
    --text-0:#eef2f7!important; --text-1:#b9c4d2!important; --text-2:#97a5b8!important;
    --border:#2c3644!important; --border-hot:#3d4a5c!important; --border-bright:#3d4a5c!important;
    --accent:#2da8ff!important; --accent-dim:#2da8ff40!important; --accent-rgb:45,168,255!important;
    --accent-ink:#7cc4ff!important; --on-accent:#04202f!important;
    --green:#3ddc84!important; --green-ink:#4ade9b!important;
    --gold:#f5c518!important;  --gold-ink:#e0b53d!important;
    --red:#ff7a6d!important;   --error:#ff7a6d!important;
    --amber:#f0a83c!important; --warn:#f0a83c!important;
    --blue:#6cb2f7!important;  --indigo:#a5a0ff!important; --gray:#97a5b8!important;
    --row-zebra:rgba(255,255,255,.028)!important; --row-hover:var(--bg-2)!important;
    --bg-0-rgb:19,24,32!important; --bg-1-rgb:26,32,41!important;
    --info-fill:#1d2c3d!important;
    --shadow-tint:rgba(0,0,0,.34)!important;
    --shadow-1:0 1px 2px rgba(0,0,0,.42),0 1px 1px rgba(0,0,0,.30)!important;
    --shadow-2:0 6px 16px rgba(0,0,0,.48),0 1px 3px rgba(0,0,0,.34)!important;
    --shadow-3:0 16px 44px rgba(0,0,0,.60),0 4px 14px rgba(0,0,0,.42)!important;
    --shadow-focus:0 0 0 3px rgba(45,168,255,.34)!important;
    color-scheme:dark!important;
  }
}

/* ---------------------------------------------------------------------------
   3. EXPLICIT LIGHT — wins over OS-dark. Same values as the bare :root above,
   restated at !important so a stored "light" choice survives both the OS
   preference and any runtime applier.
   --------------------------------------------------------------------------- */
:root[data-theme="light"] {
  --bg-0:#f7f9fc!important; --bg-1:#ffffff!important; --bg-2:#f1f5fa!important; --bg-3:#e7edf5!important;
  --text-0:#0d1520!important; --text-1:#4a5a6f!important; --text-2:#5c6f88!important;
  --border:#dde5ef!important; --border-hot:#c3d0e0!important; --border-bright:#c3d0e0!important;
  --accent:#2da8ff!important; --accent-dim:#2da8ff40!important; --accent-rgb:45,168,255!important;
  --accent-ink:#0a66c2!important; --on-accent:#04202f!important;
  --green:#3ddc84!important; --green-ink:#147a46!important;
  --gold:#f5c518!important;  --gold-ink:#8a6a00!important;
  --red:#c9271b!important;   --error:#c9271b!important;
  --amber:#b45309!important; --warn:#b45309!important;
  --blue:#1a68c4!important;  --indigo:#4f46e5!important; --gray:#5b6b7f!important;
  --row-zebra:rgba(13,21,32,.025)!important; --row-hover:var(--bg-2)!important;
  --bg-0-rgb:247,249,252!important; --bg-1-rgb:255,255,255!important;
  --info-fill:#e3f1ff!important;
  --shadow-tint:rgba(16,24,40,.04)!important;
  --shadow-1:0 1px 2px rgba(16,24,40,.06),0 1px 1px rgba(16,24,40,.04)!important;
  --shadow-2:0 6px 16px rgba(16,24,40,.10),0 1px 3px rgba(16,24,40,.06)!important;
  --shadow-3:0 16px 44px rgba(16,24,40,.16),0 4px 14px rgba(16,24,40,.10)!important;
  --shadow-focus:0 0 0 3px rgba(45,168,255,.22)!important;
  color-scheme:light!important;
}

/* ---------------------------------------------------------------------------
   4. THE TOGGLE — one unobtrusive sun/moon control, styled from tokens so it
   reads correctly in both palettes. Markup is a <button data-haul-theme-toggle>;
   haul-palette.js wires it by delegation (so a nav injected at runtime works).
   --------------------------------------------------------------------------- */
.haul-theme-toggle {
  -webkit-appearance:none; appearance:none;
  width:34px; height:34px; flex:none;
  display:inline-flex; align-items:center; justify-content:center;
  align-self:center;
  border:1px solid var(--border-bright); border-radius:999px;
  background:var(--bg-1); color:var(--text-1);
  font-size:14px; line-height:1; cursor:pointer;
  transition:background .15s,color .15s,border-color .15s;
}
.haul-theme-toggle:hover { background:var(--bg-2); color:var(--text-0); border-color:var(--border-hot); }
.haul-theme-toggle:focus-visible { outline:none; box-shadow:var(--shadow-focus); }
/* Standalone surfaces with no nav to hang it in (auth pages) park it top-right. */
.haul-theme-toggle.floating { position:fixed; top:14px; right:14px; z-index:60; box-shadow:var(--shadow-1); }
@media (prefers-reduced-motion: reduce) { .haul-theme-toggle { transition:none; } }

/* ---------------------------------------------------------------------------
   5. THE TOGGLE'S ICON — haul-palette.js writes an inline Lucide moon/sun into
   the button (it used to write a ☾/☀ text glyph, which rendered in whatever
   emoji font the platform chose and sat on the text baseline instead of the
   button's optical centre). Sized and coloured here so every surface agrees.

   tms.html solved the same problem locally by hiding the glyph
   (`.theme-switch{font-size:0}`) and painting a CSS-masked moon/sun in
   `.theme-switch::before`. Now that the icon is real markup that pseudo-element
   would draw a SECOND moon beside it, so it is switched off here for any
   control the script has actually painted. The aria/title/aria-pressed contract
   and the `.theme-switch` class hook are untouched.
   --------------------------------------------------------------------------- */
[data-haul-theme-toggle] .haul-theme-ico { width:16px; height:16px; display:block; flex:none; }
[data-haul-painted]::before { content:none!important; }

/* ---------------------------------------------------------------------------
   6. CRISP — 2026-09-09 — the rendering rules tms.html adopted, applied to the
   17 shared surfaces this stylesheet already covers. Rendering only; no colour,
   no layout. (1) Subpixel antialiasing is what keeps letterforms sharp on a
   standard-density screen — the grayscale "antialiased" mode thins them there —
   so grayscale is used only at 2x and above, where it is the crisper choice.
   (2) Icons snap to the pixel grid: geometricPrecision, integer sizes, and a
   2px stroke at 16px (1.75 blurs at that size).
   --------------------------------------------------------------------------- */
@media (max-resolution: 1.99dppx) {
  html, body, input, select, textarea, button { -webkit-font-smoothing:subpixel-antialiased; -moz-osx-font-smoothing:auto; }
}
@media (min-resolution: 2dppx) {
  html, body { -webkit-font-smoothing:antialiased; -moz-osx-font-smoothing:grayscale; }
}
html { font-optical-sizing:auto; }
svg { shape-rendering:geometricPrecision; }
