/* ============================================================
   MEANSUITE DESIGN TOKENS
   The single source of the palette and the shared shape/size constants.

   Extracted from app.css on 2026-09-19 so that the three pages which deliberately sit
   OUTSIDE the app shell can share it: templates/login.html, templates/timeclock.html and
   templates/tasking-print.html each carried their own hand-written hexes (52 between them),
   which meant the first screen a user saw was painted in a different blue from every screen
   after it, and a palette change had to be repeated in four places or silently miss three.
   Those pages keep their own LAYOUT css -- they should, none of them is an app panel -- they
   just stop inventing colours.

   Nothing in here draws anything. It is `--token: value` and nothing else, so the file can be
   linked by any page without dragging the app's layout along with it. Every rule that USES a
   token still lives in app.css (or in the standalone page's own <style>).

   Load order: this file first, then the page's own stylesheet. See index.html / login.html /
   timeclock.html / tasking-print.html.
============================================================ */

/* ============================================================
   THEME VARIABLES
============================================================ */
/* Dark Navy Theme (default) */
body.theme-dark {
  /* THEME A (2026-09-19): the palette below replaces the previous one. Surfaces climb in four steps —
     page (--bg-body/--bg-app) < panel (--bg-panel/--bg-sidebar) < card (--bg-card) < raised (--bg-raised) —
     and structure comes from those steps plus the hairlines (--border-soft between rows, --border on
     cards, --border-control on inputs). Comments further down that quote an old hex describe the
     history of that token, not its current value. */
  /* Without this, the browser assumes a light UI for anything it renders itself rather than
     reading from our CSS: a <select>'s own dropdown popup, scrollbars, checkboxes/radios,
     date/time pickers. Every one of those was showing black text on a white or grey system
     background on this theme — the exact inverse of the page around it. Set per theme class
     (see body.theme-light's own color-scheme below), not once on :root, since this app has
     both and the fix for one theme is the bug for the other. */
  color-scheme: dark;
  --bg-body:       #070c17;
  --bg-app:        #070c17;
  --bg-sidebar:    #0e1a30;
  --bg-panel:      #0e1a30;
  --bg-panel-2:    #0a1426;
  --bg-card:       #16294a;
  --bg-input:      #0e1a30;
  /* Lightened from #1e2d42 -- at that value, borders read as barely-there against these
     near-black surfaces (all within a few percent of each other in lightness), so a
     table row, a card, and an input all blurred into one flat surface with nothing to
     mark where one ends and the next begins. This still reads as a subtle hairline, not
     a heavy outline, but is now actually visible next to bg-panel/bg-card/bg-input. */
  --border:        #2d4676;
  --accent:        #74adff;
  --accent-light:  #9cc2ff;
  /* Same as --accent-light here (plenty of contrast against a dark active-bg tint already)
     but NOT the same in the light theme below — see that block's comment. Use this,
     not --accent-light directly, for actual body/label text on a tinted card. */
  --accent-text:   var(--accent-light);
  --status-green-light: #72d693;
  --status-amber-light: #f6c650;
  --status-red-light: #ff8794;
  /* The Bus/PTV door badge's text. #8347c9 on the badge's own purple wash over these surfaces
     measured 1.7-2.6:1 (WCAG AA is 4.5), and this is the door number a driver reads to know
     where to go. A light lavender on the wash is about 9:1 here; the light theme (below) keeps a
     deep purple. */
  --bus-badge-text: #dcc8ff;
  --on-accent: #06122b;
  /* The sidebar identity disc (.avatar). It was two literal hexes sitting inside app.css's
     THEME A block -- the block whose own rule is that colours come from tokens -- on the one
     element that is the same navy in both themes, in a light theme whose sidebar CLAUDE.md
     describes as "a light panel, not navy". Tokenised per theme instead: white initials on
     #3b62b5 measure 5.83:1 here, and 6.07:1 on the light theme's disc below. */
  --avatar-bg:  #3b62b5;
  --avatar-ink: #ffffff;
  /* Gantt-only "guide line" — deliberately much fainter than --border, which was just
     strengthened for real card/table boundaries above and would make the Gantt's dense
     per-hour lines read as a spreadsheet grid if reused here (the actual complaint this
     fixes: "excel table" instead of "whiteboard with guide lines"). */
  --gantt-guide: rgba(255,255,255,0.06);
  /* Live Board's "open flight" row wash — same fixed rgba(160,100,20,0.08) reads as
     genuinely subtle over these near-black surfaces, so dark theme keeps it as-is. */
  --status-amber-wash: rgba(246,198,80,0.10);
  --status-amber-wash-hover: rgba(246,198,80,0.20);
  /* A bolder, more saturated amber than --status-amber-light above — reserved for the
     field-level change highlight (.cell-changed-highlight) and the row tint it sits inside
     (tr.flight-alert-changed). Kept as its own token rather than just raising
     --status-amber-light's saturation, because that one is shared by ~10 other calmer
     warning/pending/overdue-soon cues (.tab-count.warn, .tap-qual-warn, .dash-kpi.is-alert,
     .gantt-duty-empty, ...) that need to stay at their current strength. */
  --status-amber-highlight: #f6c650;
  --status-amber-highlight-wash: rgba(246,198,80,0.22);
  --status-amber-highlight-pulse-low: rgba(246,198,80,0.08);
  --status-amber-highlight-pulse-high: rgba(246,198,80,0.28);
  /* Live Board's card alert states (see the .flight-alert-* rules). Orange is a fourth
     status hue used only there; red reuses the existing --status-red-light for its edge
     and needs only a wash. The two pulse stops are the low and high points of the "new"
     card's fade — kept as variables so the whole state's palette lives in one place per
     theme rather than as literals scattered through the keyframes. */
  --status-orange-light: #f7a35c;
  --status-orange-wash: rgba(247,163,92,0.18);
  --status-orange-pulse-low: rgba(247,163,92,0.06);
  --status-orange-pulse-high: rgba(247,163,92,0.22);
  /* Segregation: a violet, the one hue no other flight state uses. Deliberately distinct
     from the amber/orange/red run, which reads as an escalating urgency scale -- segregation
     is not more or less urgent than those, it is a different kind of fact about the flight. */
  --status-violet-light: #d5a0ff;
  --status-violet-wash: rgba(213,160,255,0.16);
  --status-red-wash: rgba(255,135,148,0.16);
  /* How much of its own colour a count/status pill (.tab-count and friends) mixes into the
     surface behind it. One number per theme rather than an alpha hardcoded per variant:
     the three status pills each carried their own fixed dark rgba, which meant the light
     theme inherited a dark-green/amber/red wash over a white card and the pill's own text
     lost contrast against it. Measured over --bg-body/--bg-panel/--bg-card: at 18% the
     weakest pair here is red on a card, 4.66:1.
     Re-measured 2026-09-20 across every hue this wash is used with, because the figure above
     said 4.71:1 and had not been checked since --status-indigo-light was added underneath it.
     Current worst-case per hue, dark theme: red 4.66, indigo 4.78, violet 4.98, orange 5.12,
     green 5.47, accent 5.63, amber 6.08. If you add a hue here, measure it -- a stated floor
     nobody re-measures stops being a floor the moment the next colour lands under it. */
  --badge-wash: 18%;
  /* Not Applicable: an indigo distinct from segregation's lavender-violet above and from
     every category/transport hue below (blue, teal, pink) — the one hue not already spoken
     for by another flight badge or filter chip.
     Lightened from #8b96ff 2026-09-20: that value was the one pill hue in either theme that
     actually failed AA, measuring 3.99:1 as ink on its own 18% wash over --bg-card. This is
     4.78:1 on the same pair and still reads as the same indigo beside the violet above. */
  --status-indigo-light: #a3adff;
  /* Flight colour language — one flight reads the same on every surface (Live Board rows,
     Allocation chips, the Flights table). Three independent facts, three axes:
       leg direction  — blue arrival / auburn departure (a bar + a ▲/▼ glyph)
       sector         — purple transborder / green domestic / mustard international (a tag)
       transport crew — sky Cobus / pink PTV (the card tint + the type icon)
     Tuned for the dark surfaces here; body.theme-light redefines all six darker for white. */
  --leg-arrival:         #4fd6c0;
  --leg-departure:       #f7a35c;
  --sector-transborder:  #b0a3ff;
  --sector-domestic:     #72d693;
  --sector-international: #e2bf55;
  /* What to push sector ink TOWARD to clear 4.5:1 where the hue is used as text on its own
     20% wash — white here, black in the light block. The three hues above are tuned to read
     as a tag's fill and border; used as 11px ink on a tinted chip they measured 3.47-4.14 in
     this theme and 4.27-4.44 in light. A token rather than two hard-coded literals in the
     rule, and a direction rather than three more hues, so adding a fourth sector needs one
     line above and nothing here. */
  --sector-ink-boost:    #ffffff;
  --transport-cobus:     #5cc3f5;
  --transport-ptv:       #f783bc;
  /* Flights-table Category filter/badge — a third axis alongside sector (above) and
     assignment, so needs its own hues distinct from both. --category-remote reuses
     --transport-cobus's blue (remote arrivals are worked by Cobus buses too);
     --category-bus is a new teal for the "Cobus - Departures" category so it doesn't
     collide with --sector-domestic's green sitting in the same filter bar; PTV reuses
     --transport-ptv directly rather than a fourth token. */
  --category-remote:     #5cc3f5;
  --category-bus:        #2ec4b6;
  --text-primary:  #eaf0fb;
  /* Lightened from #6a82a0 -- that value sat right at ~4:1 contrast against these dark
     backgrounds (borderline-failing WCAG AA's 4.5:1 for normal text, and this app's
     secondary text is routinely 11-12px, where 4:1 reads as genuinely strained, not just
     technically borderline). Every table header, field label, timestamp, and TOW/O/N
     placeholder in the app uses this color, so it was the single highest-impact fix for
     "difficult to read" in dark theme specifically. ~6.8:1 against bg-panel now. */
  --text-secondary:#9dadce;
  --text-heading:  #f5f8fe;
  --active-bg:     #26406e;
  --hover-bg:      #12244a;
  --scrollbar:     #2d4676;
  /* Elevation — dark surfaces need a near-black, low-opacity shadow just for
     depth separation; a visible "drop" would look wrong against a dark bg. */
  --shadow-sm:     0 1px 2px rgba(0,0,0,0.45);
  --shadow-md:     0 12px 32px rgba(0,0,0,0.55);
  /* Theme A additions */
  --on-status: #0b1220;   /* ink on a solid --status-* fill (bright in this theme, so dark ink) */
  --bg-raised: #223b68;
  --border-soft: #1f3257;
  --border-control: #6580b0;
  --accent-solid: #2b66d9;
  --accent-solid-hover: #2358c4;
  --focus-ring: #9cc2ff;
  --status-done: #86c2ab;
  --text-body: #bdc9e0;
  /* Weekly Schedule shift blocks (2026-09-19). These used to be a hard-coded array in app.js
     (SHIFT_COLORS) painted with a literal white, which made the schedule grid the one surface
     in the app that ignored the theme entirely -- and the one place that failed WCAG AA: four
     of the six old colours measured 2.96:1 to 3.82:1 against their white 11px text, and the
     idle OT chip measured 1.92:1. A shift block is a solid fill with text on top, so each hue
     is chosen against --shift-ink rather than for how it looks as an accent; here that means
     light hues with dark ink, and the light theme below inverts both. Worst pair: 9.07:1. */
  --shift-1: #8fc4f2;
  --shift-2: #7fd4a4;
  --shift-3: #f0bd73;
  --shift-4: #c9a5f5;
  --shift-5: #f5a0b5;
  --shift-6: #79d4c9;
  /* The one ink every shift block writes in, whichever hue it drew. */
  --shift-ink: #08131f;
}

/* Light Theme */
body.theme-light {
  /* THEME A (2026-09-19): the palette below replaces the previous one. Surfaces climb in four steps —
     page (--bg-body/--bg-app) < panel (--bg-panel/--bg-sidebar) < card (--bg-card) < raised (--bg-raised) —
     and structure comes from those steps plus the hairlines (--border-soft between rows, --border on
     cards, --border-control on inputs). Comments further down that quote an old hex describe the
     history of that token, not its current value. */
  /* See body.theme-dark's own color-scheme comment — this is the other half of the fix. */
  color-scheme: light;
  --bg-body:       #e7edf6;
  --bg-app:        #e7edf6;
  --bg-sidebar:    #f7f9fd;
  --bg-panel:      #f7f9fd;
  --bg-panel-2:    #eef3fa;
  --bg-card:       #ffffff;
  --bg-input:      #ffffff;
  --border:        #c6d2e4;
  --accent:        #1d5bd0;
  --accent-light:  #1d5bd0;
  /* 2026-09-18 audit: --accent-light measures ~3.66:1 against this theme's near-white
     cards/banners (active-bg is a near-white tint, not a real dark surface the way it is
     above) -- fine for an icon or a hover tint, not for body/label text, which needs
     4.5:1. Darkened specifically for that use rather than retuning --accent-light itself,
     which 50+ non-text usages (active states, borders, hovers) already rely on looking
     the way it does. ~6.5:1 against --active-bg. */
  --accent-text:   #1650b8;
  --status-green-light: #13653a;
  --status-amber-light: #7a5500;
  --status-red-light: #9e1a2d;
  --bus-badge-text: #5b2b96;
  --on-accent: #ffffff;
  /* See body.theme-dark's --avatar-bg. The disc stays a real disc here rather than going
     transparent: --bg-sidebar is #f7f9fd in this theme, so the collapsed sidebar was rendering
     white initials on near-white at about 1.05:1 -- invisible. This measures 5.76:1 against
     the sidebar, with the initials at 6.07:1 on it. */
  --avatar-bg:  #1d5bd0;
  --avatar-ink: #ffffff;
  /* See body.theme-dark's --gantt-guide comment. */
  --gantt-guide: rgba(30,60,110,0.10);
  /* Same fixed rgba(160,100,20,0.08) this app used to use in both themes reads as a
     near-solid peach wash over these near-white surfaces (a translucent overlay's
     perceived strength doesn't scale evenly between a near-black and a near-white base) —
     confirmed visually, most of a busy Live Board table (every "open" flight past its
     departure) looked like it was flagged as an error rather than a light "needs review"
     cue. Roughly a third the strength reads as the equivalent subtle tint here. */
  --status-amber-wash: rgba(122,85,0,0.06);
  --status-amber-wash-hover: rgba(122,85,0,0.11);
  /* See body.theme-dark's --status-amber-highlight comment. Deliberately darker/more
     saturated than --status-amber-light (#9a5b0f) below, the same relationship the dark
     theme's pair has, so the highlight still reads as "louder than the pending/warn amber"
     against a near-white surface. */
  --status-amber-highlight: #7a5500;
  --status-amber-highlight-wash: rgba(122,85,0,0.14);
  --status-amber-highlight-pulse-low: rgba(122,85,0,0.03);
  --status-amber-highlight-pulse-high: rgba(122,85,0,0.12);
  /* Same story as --status-amber-wash directly above: roughly a third the dark theme's
     alpha, since a wash that reads as subtle over near-black reads as a solid block of
     colour over near-white. The orange edge colour is darkened the same way
     --status-amber-light is, so a badge sitting on it stays legible. */
  --status-orange-light: #8f4305;
  --status-orange-wash: rgba(143,67,5,0.10);
  /* Darkened the same way the other light-theme edges are, so the badge ink stays legible. */
  --status-violet-light: #7a2fc0;
  --status-violet-wash: rgba(122,47,192,0.10);
  --status-orange-pulse-low: rgba(143,67,5,0.02);
  --status-orange-pulse-high: rgba(143,67,5,0.08);
  --status-red-wash: rgba(158,26,45,0.10);
  /* See body.theme-dark's --status-indigo-light comment. Darkened the same way the other
     light-theme edges are, so the badge ink stays legible. */
  --status-indigo-light: #4749c4;
  /* Flight colour language — see body.theme-dark for what the six mean. Darker here so the
     tag ink and the card bar stay legible on near-white surfaces. */
  --leg-arrival:         #07695e;
  --leg-departure:       #8f4305;
  --sector-transborder:  #4f3dc4;
  --sector-domestic:     #13653a;
  --sector-international: #6b5400;
  /* See the dark block's note — darker here, since the chip surface is light. */
  --sector-ink-boost:    #000000;
  --transport-cobus:     #085b8a;
  --transport-ptv:       #a32862;
  --category-remote:     #085b8a;
  --category-bus:        #157e70;
  --text-primary:  #0e1a2e;
  --text-secondary:#4a5b80;
  --text-heading:  #0a1424;
  --active-bg:     #dbe6f8;
  --hover-bg:      #edf2fa;
  --scrollbar:     #c0cce0;
  /* Elevation — tinted with the brand navy rather than pure black, so it reads
     as a soft lift instead of a grey smudge on white. */
  --shadow-sm:     0 1px 2px rgba(16,32,64,0.12);
  --shadow-md:     0 12px 32px rgba(16,32,64,0.18);
  /* Theme A additions */
  --on-status: #ffffff;   /* ink on a solid --status-* fill (darkened in this theme, so white ink) */
  --bg-raised: #ffffff;
  --border-soft: #dbe3ef;
  --border-control: #7186a6;
  --accent-solid: #1d5bd0;
  --accent-solid-hover: #1650b8;
  --focus-ring: #1d5bd0;
  --status-done: #246a4f;
  --text-body: #33435f;
  /* Lower than dark's — see --badge-wash there. Measured on a canvas composite over every
     surface this badge lands on: at 14% the weakest pair (amber on --bg-body) is 4.65:1;
     at the 25% the status variants used to hardcode it was 4.16:1, under AA at 11px. */
  --badge-wash: 14%;
  /* See body.theme-dark's --shift-1 comment for why these exist and how they were chosen.
     Darkened here and paired with white ink, the same relationship every other status hue
     has between the two themes. Worst pair: 6.27:1. */
  --shift-1: #1b5c96;
  --shift-2: #1a6b47;
  --shift-3: #8a5406;
  --shift-4: #6b3bb0;
  --shift-5: #a32862;
  --shift-6: #0f6b63;
  --shift-ink: #ffffff;
}

/* Apply theme variables */
body {
  background: var(--bg-body);
  /* Shared, theme-independent design tokens — radius/motion don't change between
     light and dark, only color does, so these live outside the theme blocks above. */
  --radius-sm: 8px;
  --radius-md: 12px;
  --radius-lg: 16px;
  --ease: cubic-bezier(0.4, 0, 0.2, 1);
  /* Shared shape for the small solid-pill status/count badges (.tab-count, .nav-badge) --
     see the "Chips & badges" consolidation note above .tab-count's own rule. Each badge
     keeps its own background/color (that's the semantic part -- a count vs. a change
     highlight are different facts and shouldn't look identical), just draws its size,
     radius and weight from one place instead of a hand-tuned copy each. */
  --badge-radius: var(--radius-md);
  --badge-padding: 1px 6px;
  --badge-font-size: 11px;
  --badge-font-weight: 700;

  /* ── Control heights (2026-09-19) ─────────────────────────────────────────────────────
     Theme A committed to one control height and then shipped five: .teaming-btn 30px,
     .plan-tab 31, .plan-filter-chip 32, .toggle-opt 33, .plan-btn 36. Every control now reads
     one of these two instead of hand-setting its own padding.

     They are TOKENS rather than a `min-height: 44px` rule in a media query on purpose, and
     the reason is a bug this replaces. app.css had exactly that rule -- a deliberate
     coarse-pointer block raising .plan-btn to 44px and .plan-filter-chip to 40px for a gloved
     thumb on the ramp -- and the Theme A section 110 lines further down re-declared both at
     36px and 32px. A media query adds no specificity, so the later rule won on source order
     and the touch sizing was silently dead (measured at 375px: 36px and 32px). A custom
     property cannot lose that way: the override below changes what every `var(--control-h)`
     resolves to, wherever in the file it is read from. */
  --control-h: 36px;
  --control-h-dense: 32px;

  /* ── Floating bottom chrome (2026-09-22) ──────────────────────────────────────────────
     Three things float over the bottom of a panel: the two FAB bubbles and, for a real
     Admin, the View As bar above them. Whatever a panel's last row is, it has to be able to
     scroll clear of all of it, and .content's bottom padding is what makes that true.

     They are tokens for the same reason the heights above are: the padding and the offsets
     were two independent literals and they drifted. .content reserved 84px while the View As
     bar sat at bottom:74px and stood 52px tall, needing 126 -- so the bar covered the last
     42px of EVERY panel for every admin, confirmed on Our Flights, the flight dropmenu,
     Configuration and My Shift. Derive the padding from the offsets and the arithmetic
     cannot go stale again; change an offset here and the gutter follows it. */
  /* ── Stacking order (2026-09-29) ──────────────────────────────────────────
     Every position:fixed layer in the app, as one ordered list rather than a literal at each
     rule. It is here because a stacking order is a shared fact -- the whole point is that two
     rules 6,000 lines apart cannot disagree about which of them is on top -- and because the
     bug it replaces was exactly that disagreement.

     What went wrong: .modal-bg carried z-index 100 and #panel-tasking.tasking-fullscreen
     carried 1000, so maximising the Allocation board put it OVER every dialog in the app. The
     qualification-override confirm (confirmAction(), the one gate between a drag and an
     assignment the server refused) rendered underneath the board: invisible, with its backdrop
     holding body{overflow:hidden} and its promise unresolved, which reads as a frozen board.
     The board's own context menu and assign picker (700) were under it for the same reason.

     Read in order, top last. A dialog is above the thing that opened it, always; only the two
     notices that must survive a dialog -- the connection banner and the toast -- are above one. */
  --z-popover: 700;         /* a menu or picker anchored to a control, inside normal page flow */
  --z-panel: 900;           /* a fixed panel over the page (Our Flights' flight dialog) */
  --z-board-max: 1000;      /* the maximised Allocation board (#panel-tasking.tasking-fullscreen) */
  --z-board-popover: 1050;  /* the board's own menu/picker -- ABOVE the maximised board */
  --z-modal: 1100;          /* .modal-bg: a dialog outranks whatever raised it */
  --z-fab: 1200;            /* the floating bottom bubbles */
  --z-banner: 9000;         /* ConnectionGuard's dead-session / offline banner */
  --z-toast: 9999;          /* #toast */

  --fab-inset: 20px;        /* how far the FAB bubbles sit off the bottom edge */
  --fab-size: 50px;         /* their diameter */
  --viewas-bottom: 74px;    /* the View As bar's own bottom offset */
  --viewas-h: 52px;         /* its height with the Desktop/Mobile pair beside the select. That
                               pair is hidden on a phone, where the bar measures 40 -- the taller
                               figure is kept deliberately, because this token is only ever read
                               to RESERVE room and over-reserving by 12px costs nothing while
                               under-reserving is the bug being fixed. */
}

/* The ramp phone. A coarse pointer or a narrow screen raises both heights for every control
   at once -- see the note above for why this is a variable swap and not a pile of overrides. */
@media (pointer: coarse), (max-width: 768px) {
  body {
    --control-h: 44px;
    --control-h-dense: 40px;
  }
}
