/* ============================================================================
   BeyondMDX Console — design tokens
   THE single source of truth for color, type, spacing and BREAKPOINTS.
   Nothing in console/ may declare its own breakpoint or re-declare a token.
   ============================================================================ */

/* ── BREAKPOINTS ────────────────────────────────────────────────────────────
   Only TWO exist. They were chosen so that NO real device width sits near an
   edge — the bug we are fixing is admin.css carrying max-width:767px AND
   max-width:768px, a seam that lands inside the iPad Mini's 744-768pt range,
   letting one device get phone layout from one rule and tablet from another.

     phone    <  600px   real widths: 360 / 375 / 393 / 430  (nearest edge 170px away)
     tablet   >= 600px   real widths: 744 / 834 / 1024 / 1194
     desktop  >= 1240px  real widths: 1280+

   1240 (not 1024) because 1024 is EXACTLY iPad Pro 12.9" portrait. Every iPad
   is a tablet; only Macs and PCs are desktop.

   ⚠️ JS MUST NEVER HARD-CODE THESE NUMBERS. maps.html already carries a comment
   warning that its JS copies a media query "character for character. If they
   drift, the JS..." — so instead the active tier is published below as
   --bp-name, and core/session.js reads it back out of computed style. There is
   exactly one copy of each number, and it is in this file. */
:root { --bp-name: "phone"; }
@media (min-width: 600px)  { :root { --bp-name: "tablet"; } }
@media (min-width: 1240px) { :root { --bp-name: "desktop"; } }

/* ── THEME ──────────────────────────────────────────────────────────────────
   Two themes, one set of names. Every colour in the Console is a token declared
   twice below and NOWHERE else — a literal colour in a page is a bug, because it
   will be wrong in one of the two themes.

   `data-theme` on <html> is always an explicit "light" or "dark", resolved by
   the inline script in index.html before first paint (so there is no flash) and
   kept in step by core/theme.js. "Auto" is a stored PREFERENCE, never a value
   that reaches the DOM. */
:root {
  color-scheme: dark;
  /* ── Surfaces (dark: the front door, continuous with sign-in) */
  /* ⚠️ The GROUND stays deep — that part was right. What was wrong is that
     everything ON it sat at 4.5% white over a near-black page, so a card, the
     page behind it and the card below it were all within a couple of steps of
     each other and the whole screen read as one dark sheet (user, 2026-08-30).
     Surfaces and lines are lifted; --bg-deep is untouched, so the app is exactly
     as dark as before and only the separation changed.

     Translucent, not opaque, on purpose: the page carries a gradient that is far
     lighter at the top, and an opaque panel would go DARKER than the page there. */
  --bg-deep:    #06090d;
  --bg-grad: radial-gradient(120% 85% at 50% -18%, #223041 0%, #131b25 42%, #0a0f16 68%, #06090d 100%);
  --surface:    rgba(255, 255, 255, 0.075);
  --surface-2:  rgba(255, 255, 255, 0.115);
  --surface-3:  rgba(255, 255, 255, 0.17);
  --line:       rgba(255, 255, 255, 0.14);
  --line-2:     rgba(255, 255, 255, 0.24);
  --bar-bg:     rgba(8, 12, 18, 0.72);
  --menu-bg:    #161f2c;
  --shadow-lg:  0 40px 80px -28px rgba(0, 0, 0, 0.75);
  --shadow-md:  0 24px 60px -18px rgba(0, 0, 0, 0.8);
  --inset-hi:   inset 0 1px 0 rgba(255, 255, 255, 0.07);

  /* ── Ink */
  --ink:        #eef2f6;
  --ink-2:      #dfe6ee;
  --muted:      #93a1af;
  --muted-2:    rgba(147, 161, 175, 0.55);

  /* ── Accent */
  --accent:     #7aa5d2;
  --accent-dim: rgba(122, 165, 210, 0.16);
  --danger:     #e2695f;
  /* A FILLED accent needs a deeper blue than the text accent: white on #7aa5d2
     is ~2.3:1, so a white switch knob would blur into its own track. */
  --accent-solid: #3f7fbf;
  --on-accent:    #ffffff;

  /* ── The mountain ridge. Three layers, far to near. */
  --ridge-1: #8ea3ba; --ridge-1-o: 0.07;
  --ridge-2: #48596e; --ridge-2-o: 0.20;
  --ridge-3: #070b11; --ridge-3-o: 0.92;

  /* The wordmark exists as white line art only; on dark it is used as-is. */
  --mark-filter: none;

  /* Segmented control. Its OWN tokens, not --surface reused: on a light menu
     --surface and --surface-3 both resolve to near-white, so the selected
     segment vanished into its track. A selection must be unmistakable. */
  --seg-track:  rgba(255, 255, 255, 0.05);
  --seg-active: rgba(255, 255, 255, 0.20);
  --seg-active-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.12);

  /* ⚠️ Form fields need an OPAQUE background. A native <select>'s drop-down list
     is painted by the browser using the control's own background-color — an
     alpha overlay resolves against white there, so a translucent field with
     near-white text produced an unreadable white-on-white menu in dark mode. */
  --field-bg: #1a2331;
  --option-bg: #161f2c;

  /* ── Status pills. The SHAPE is a token, because the two themes want two
        different things (user, 2026-08-30):

          dark   crisp coloured text, no chip — a pastel chip on a near-black
                 card is a bright blob that outweighs the credential's name
          light  a filled pill — coloured text alone on white is weak, and the
                 chip is what carries the status at a glance

        One .pill rule reads these; no page restates either shape. */
  --pill-pad: 0;
  --pill-radius: 0;
  --pill-size: 12px;
  /* ⚠️ Light weight ON PURPOSE, and only in dark. Bold coloured type on a dark
     ground blooms — the strokes thicken and the word loses its edges. Thinner
     is crisper here, which is the opposite of the light theme, where the same
     word sits inside a pale chip and needs the weight to hold up. */
  --pill-weight: 500;
  --pill-exp-weight: 500;   /* expiry is a note ON a status, not a rival to it */

  --pill-missing-fg:   #ff9186;  --pill-missing-bg:   transparent;
  --pill-awaiting-fg:  #f0b849;  --pill-awaiting-bg:  transparent;
  --pill-validated-fg: #5ed39b;  --pill-validated-bg: transparent;
  /* Sent back by a validator. It shares the "you must act" family with Missing
     but must never be mistaken FOR it: Missing means nobody has done anything,
     Needs Attention means someone looked and said no. Orange sits between the
     red and the amber and is the only gap left on the state axis. It can read
     close to the 30-day expiry orange, which is fine — those are two different
     pills on two different axes and each carries its own words. */
  --pill-attention-fg: #ff8a4c;  --pill-attention-bg: transparent;
  --pill-exp60-fg:     #d8a75a;  --pill-exp60-bg:     transparent;
  --pill-exp60-ring:   transparent;
  --pill-exp30-fg:     #ff9e5e;  --pill-exp30-bg:     transparent;
  --pill-expired-fg:   #ff9186;  --pill-expired-bg:   transparent;
  /* On an assignment. The one availability state the credential set has no
     word for: it is neither owed nor wrong, it is busy — so it takes the one
     hue the axis has not used. */
  --pill-assigned-fg:  #cfa0ff;  --pill-assigned-bg:  transparent;

  /* ── Board tones. A FILLED BOX WANTS THE OPPOSITE OF A PILL ─────────
     ⚠️⚠️ THE TILES USED TO PAINT THEMSELVES WITH --pill-*-bg AND THAT WAS A
     BUG (user, 2026-09-03). In dark a pill is coloured TEXT with no chip, so
     every --pill-*-bg here is transparent — a tile took a transparent fill AND
     a transparent border, which also cancelled the neutral border it would
     otherwise have had, so the box vanished and left its label floating on the
     page. A pill wants its colour on the INK; a tile wants it on the GROUND.
     Two jobs, two sets of tokens.

     ⚠️ AND DUE MUST NOT BE THE SAME COLOUR AS EXCEPTION. Red is NOT CHECKED,
     amber is CHECKED AND SOMETHING IS WRONG — that ordering is the whole point
     of the board. They both resolved to the same red in BOTH themes, because
     --pill-missing-bg and --pill-expired-bg are the same value. */
  --tile-due-bg:   rgba(255, 145, 134, 0.15);
  --tile-due-line: rgba(255, 145, 134, 0.55);
  --tile-due-fg:   #ffa79d;
  --tile-exc-bg:   rgba(240, 184, 73, 0.14);
  --tile-exc-line: rgba(240, 184, 73, 0.50);
  --tile-exc-fg:   #f2c46a;
  --tile-ok-bg:    rgba(94, 211, 155, 0.13);
  --tile-ok-line:  rgba(94, 211, 155, 0.42);
  --tile-ok-fg:    #7dddae;

  /* ── App-icon tints ─────────────────────────────────────────────────────
     Eight hues, and eight is the point: the launcher grows to ~21 apps, and a
     hue per app stops being distinguishable long before that. Colour marks the
     FAMILY an app belongs to; the glyph says which app it is.

     Muted alpine, per the brand guide — "no neon, no camo, no tacticool", and
     the accent direction is muted rust / Alpine gold. A saturated Google-style
     rainbow would read as confetti on a near-black ground and fight the
     line-art wordmark.

     The plate behind each glyph is mixed from the same hue rather than being a
     second token, so a new hue is ONE line here and cannot fall out of step
     with its own background. */
  --tint-slate: #7d9dc4;
  --tint-sage:  #7fa88b;
  --tint-clay:  #c4886a;
  --tint-gold:  #c6a75f;
  --tint-stone: #9aa6b2;
  --tint-moss:  #93a86d;
  --tint-plum:  #a98aa6;
  --tint-teal:  #6fa8a8;
  /* Ninth hue, added 2026-09-23 for Fireline Conduct & Etiquette. Hotter and
     redder than clay on purpose: side by side at tile size the two were
     telling the same story, and clay already carries the personal family. */
  --tint-ember: #d96a4f;

  /* ── Type */
  --font: "Segoe UI", -apple-system, BlinkMacSystemFont, Roboto, Helvetica, Arial, sans-serif;

  /* ── Radius */
  --r-sm: 10px;
  --r-md: 14px;
  --r-lg: 22px;

  /* ── Touch targets. 48 satisfies Apple's 44pt AND Material's 48dp at once.
        --tap-field is for gloved use on the fireline — go larger, never smaller. */
  --tap:       48px;
  --tap-field: 56px;

  /* ── Safe areas. Every fixed/sticky element uses these, never a bare 0. */
  --sa-top:    env(safe-area-inset-top, 0px);
  --sa-bottom: env(safe-area-inset-bottom, 0px);
  --sa-left:   env(safe-area-inset-left, 0px);
  --sa-right:  env(safe-area-inset-right, 0px);

  /* ── One bottom-bar height everywhere (admin is 56, employee is 46 today) */
  --bar-h: 56px;

  /* ── The content column. The header and the page body BOTH use this, so the
        logo sits above the first word of the page and the avatar lines up with
        the right edge of the content — on a 27" monitor as well as a phone. */
  --col: 1180px;
  /* ⭐ THE WIDER COLUMN, FOR DATA RATHER THAN READING. 1180 is the width a
     form or a paragraph should be — past about 90 characters a line is measurably
     harder to read, and every app in this Console is mostly words. A dense table
     is the opposite problem: a row of thirteen columns clipped at 1180 makes
     somebody drag sideways to see a value that would have fitted on their screen.
     An app opts in with data-col="wide" (core/app.css) for the region that needs
     it, so nothing else in the Console is stretched to fix one table. */
  --col-wide: 1600px;
  --gutter: 16px;

  --ease: cubic-bezier(0.22, 1, 0.36, 1);

  /* ── Print. DELIBERATELY NOT THEME-AWARE, and not overridden below.
        Paper has one appearance. Printing the dark theme's near-black ground
        wastes a cartridge and prints pale grey text on it; printing the light
        theme's translucent surfaces over a white sheet renders them invisible.
        So print gets its own three values and every printed page reads ink on
        white whichever theme the person happens to be in.
        ⚠️ They are tokens rather than literals in the print block because
        check.js fails the build on a hard-coded colour — which is how it
        caught these on the way in. */
  --print-bg:   #ffffff;
  --print-ink:  #000000;
  --print-line: #bbbbbb;
}

/* ── LIGHT ──────────────────────────────────────────────────────────────────
   Daylight over the same range, not a generic white sheet — the gradient, the
   ridge and the glass all keep their roles so the two themes read as one product.
   Only the tokens change; no layout rule is theme-aware. */
:root[data-theme="light"] {
  color-scheme: light;
  --bg-deep:    #e9eef4;
  --bg-grad: radial-gradient(120% 85% at 50% -18%, #ffffff 0%, #f3f7fb 42%, #e8eef5 68%, #dde5ee 100%);
  --surface:    rgba(255, 255, 255, 0.72);
  --surface-2:  rgba(255, 255, 255, 0.92);
  --surface-3:  #ffffff;
  --line:       rgba(20, 38, 56, 0.13);
  --line-2:     rgba(20, 38, 56, 0.22);
  --bar-bg:     rgba(248, 251, 254, 0.78);
  --menu-bg:    #ffffff;
  --shadow-lg:  0 30px 64px -26px rgba(23, 43, 66, 0.28);
  --shadow-md:  0 18px 44px -16px rgba(23, 43, 66, 0.26);
  --inset-hi:   inset 0 1px 0 rgba(255, 255, 255, 0.9);

  --ink:        #16202b;
  --ink-2:      #26333f;
  --muted:      #5a6b7d;
  /* Higher alpha than the dark theme's: the footnote sits over the pale ridge,
     where a 0.55 grey all but disappears. */
  --muted-2:    rgba(66, 84, 103, 0.78);

  --accent:     #2f6ba3;
  --accent-dim: rgba(47, 107, 163, 0.12);
  --danger:     #b23a2e;
  --accent-solid: #2f6ba3;
  --on-accent:    #ffffff;

  /* Haze, not silhouette: a near-black foreground ridge is far too heavy in
     daylight, so the layers stay blue-grey and gain weight only slightly. */
  --ridge-1: #a4bcd3; --ridge-1-o: 0.38;
  --ridge-2: #83a0bd; --ridge-2-o: 0.40;
  --ridge-3: #63819f; --ridge-3-o: 0.42;

  /* No dark version of the wordmark asset exists. brightness() takes the white
     line art down to the same grey the full dark logo uses — a filter, not a
     new file to keep in sync. invert() would give a harsh pure black. */
  --mark-filter: brightness(0.34);

  /* A recessed track, and the selected chip lifted off it in white. */
  --seg-track:  rgba(20, 38, 56, 0.07);
  --seg-active: #ffffff;
  --seg-active-shadow: 0 1px 2px rgba(23, 43, 66, 0.20), 0 0 0 1px rgba(20, 38, 56, 0.07);

  --field-bg: #ffffff;
  --option-bg: #ffffff;

  /* Daylight needs the hues taken down, not just re-used: the same value that
     reads as a soft glow on near-black is washed out on a pale card. */
  --tint-slate: #4a7099;
  --tint-sage:  #4e7d61;
  --tint-clay:  #a05a34;
  --tint-gold:  #8a6d22;
  --tint-stone: #5d6a77;
  --tint-moss:  #5b7639;
  --tint-plum:  #7b5677;
  --tint-teal:  #3d7b7b;
  --tint-ember: #b84a2c;

  /* Filled chips, dark-on-pastel — legible on the pale card without a border. */
  --pill-pad: 4px 10px;
  --pill-radius: 10px;
  --pill-size: 11px;
  --pill-weight: 600;
  --pill-exp-weight: 600;

  --pill-missing-fg:   #6d211a;  --pill-missing-bg:   #f3d4d1;
  --pill-awaiting-fg:  #6b4708;  --pill-awaiting-bg:  #f5e3c3;
  --pill-validated-fg: #17492a;  --pill-validated-bg: #cfe6d4;
  --pill-attention-fg: #7a3510;  --pill-attention-bg: #f7ddcd;
  /* 60 days out is outlined rather than filled: it is a heads-up, not a
     problem, and a third filled chip competes with the status beside it. */
  --pill-exp60-fg:     #8a5c12;  --pill-exp60-bg:     transparent;
  --pill-exp60-ring:   #d8ab5e;
  --pill-exp30-fg:     #7a3d10;  --pill-exp30-bg:     #f6d9c0;
  --pill-expired-fg:   #6d211a;  --pill-expired-bg:   #f3d4d1;
  --pill-assigned-fg:  #4b2a6f;  --pill-assigned-bg:  #e9daf6;

  /* The same three tones on paper: a pastel ground, a deeper edge so the box
     still reads as a box on a near-white page, and dark ink. */
  --tile-due-bg:   #f7dedb;
  --tile-due-line: #dda9a3;
  --tile-due-fg:   #6d211a;
  --tile-exc-bg:   #f8e9cf;
  --tile-exc-line: #dcb86e;
  --tile-exc-fg:   #6b4708;
  --tile-ok-bg:    #d6e9db;
  --tile-ok-line:  #9cc4a8;
  --tile-ok-fg:    #17492a;
}

/* ── Form controls ──────────────────────────────────────────────────────────
   Declared here, once, so every page in the tree gets a field that is readable
   in BOTH themes without restating it — the native drop-down is the part that
   goes wrong silently, because it is painted outside the page. */
/* â â  THE SECOND LIST. app.css sizes these controls and THIS rule colours
   them, so a type named in one and missed in the other gets half a style â
   which is exactly what tel and email looked like: correct width and height,
   but the browser's own background and border sitting among fields that had
   ours. Add a type in both places or in neither. check.js reads BOTH files. */
select, input[type="text"], input[type="date"], input[type="search"],
input[type="tel"], input[type="email"], input[type="number"],
input[type="password"], input[type="datetime-local"], input[type="time"], textarea {
  background-color: var(--field-bg);
  color: var(--ink);
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
}
/* Chrome and Firefox paint these from the element's own colours; without them
   the list falls back to the platform default, which is white. */
option, optgroup {
  background-color: var(--option-bg);
  color: var(--ink);
}
optgroup { font-weight: 700; }
::placeholder { color: var(--muted); opacity: 1; }
/* The native date picker's calendar glyph is black-on-black in dark mode. */
:root:not([data-theme="light"]) ::-webkit-calendar-picker-indicator { filter: invert(1) opacity(.65); }

/* ── Status pills ───────────────────────────────────────────────────────────
   ONE rule for both themes. Everything that differs between them — padding,
   radius, weight and every colour — is a token above, so a page never has to
   know which theme it is in, and the admin credentials app inherits this
   automatically instead of hand-copying a third set of status colours. */
.pill {
  display: inline-block;
  padding: var(--pill-pad);
  border-radius: var(--pill-radius);
  font-size: var(--pill-size);
  font-weight: var(--pill-weight);
  line-height: 1.5;
  white-space: nowrap;
}
.pill.missing   { color: var(--pill-missing-fg);   background: var(--pill-missing-bg); }
.pill.awaiting  { color: var(--pill-awaiting-fg);  background: var(--pill-awaiting-bg); }
.pill.validated { color: var(--pill-validated-fg); background: var(--pill-validated-bg); }
.pill.attention { color: var(--pill-attention-fg); background: var(--pill-attention-bg); }
.pill.exp60, .pill.exp30, .pill.expired { font-weight: var(--pill-exp-weight); }
.pill.exp60   { color: var(--pill-exp60-fg);   background: var(--pill-exp60-bg);
                box-shadow: inset 0 0 0 1px var(--pill-exp60-ring); }
.pill.exp30   { color: var(--pill-exp30-fg);   background: var(--pill-exp30-bg); }
.pill.expired { color: var(--pill-expired-fg); background: var(--pill-expired-bg); }
.pill.assigned { color: var(--pill-assigned-fg); background: var(--pill-assigned-bg); }

/* ── The type chip ──────────────────────────────────────────────────────────
   ⚠️⚠️ IT MUST NOT REUSE THE PILL SHAPE TOKENS. The two themes give .pill two
   different shapes on purpose — dark is bare coloured text (--pill-pad: 0,
   --pill-radius: 0), light is a filled capsule — so a chip built from those
   tokens would be an unpadded outline in dark and indistinguishable from a
   status pill in light. Its own constant shape is the whole point: an OUTLINED
   NEUTRAL CAPSULE reads as "a category" against bare coloured text in dark and
   against filled pastel capsules in light, with one rule and no theme forks. */
/* Paper. WARNING: these are the ONLY colours in the Console that do not
   change with the theme, and that is the point -- a barcode label goes on a
   printer, and a dark-theme label would print white on black: unreadable,
   unscannable, and most of a toner cartridge. Declared here as tokens rather
   than written into a page as literals, because the rule that bans a literal
   colour is about a colour being WRONG in one theme, and these are wrong in
   neither. */
:root {
  --paper:       #ffffff;
  --paper-ink:   #000000;
  --paper-line:  #cccccc;
  --paper-muted: #444444;
}

/* Map symbology. NOT THEMED, ON PURPOSE, for the same reason as paper: these
   colours sit on the BASEMAP — a satellite image, a terrain tint — not on the
   Console's ground, and the basemap does not change with the theme. A person's
   tile has to be the same colour in the roster rail as on the map beside it,
   and the same colour on the admin's dark iPad as on the medic's light phone
   who is being pointed at. Five tiers, one hue each, carried over from the
   command map so nobody has to relearn who is gold and who is blue.
   ⚠️ Read by CSS only. Mapbox paints its own layers from literals inside
   core/opsmap.js and cannot read a CSS variable; those are the same hues. */
:root {
  --map-tier-md:    #f0b429;   /* medical directors — gold */
  --map-tier-rn:    #c56cf0;   /* nurses — purple */
  --map-tier-para:  #3aa0ff;   /* paramedics — blue */
  --map-tier-aemt:  #37c98b;   /* AEMTs — green */
  --map-tier-emt:   #7d9bb5;   /* EMTs — slate */
  --map-tier-other: #7d9bb5;
  --map-tile-ink:   #0b1c2d;   /* the glyph and border ON a tier tile */
  --map-flag-bg:    rgba(11, 28, 45, 0.92);
  --map-flag-fg:    #ffffff;
  --map-self:       #4285f4;   /* "you are here" */
  --map-self-halo:  rgba(66, 133, 244, 0.3);
  --map-crosshair:  rgba(0, 0, 0, 0.55);
}

.tchip {
  display: inline-block;
  padding: 3px 9px;
  border-radius: 999px;
  font-size: 11px;
  font-weight: 600;
  line-height: 1.45;
  white-space: nowrap;
  color: var(--ink-2);
  background: transparent;
  box-shadow: inset 0 0 0 1px var(--line-2);
}

/* ── Reset ───────────────────────────────────────────────────────────────── */
* { box-sizing: border-box; }
html, body { height: 100%; }
/* ⭐ RESERVE THE SCROLLBAR'S SPACE, ALWAYS. Measured at the nine target widths
   while checking a page that switches between a card layout and a table: the
   content column was 729px in one and 744px in the other on an iPad Mini, and
   nothing in either layout was responsible. The cards made the page taller, a
   vertical scrollbar appeared, and fifteen pixels of viewport went with it — so
   the whole page, header included, shifted every time the layout changed.

   It is not specific to that page. Any Console screen whose height crosses the
   viewport — a filter that hides rows, a drawer that opens, a list that loads —
   jumps sideways by the width of a scrollbar at the moment it does. Reserving
   the gutter costs those pixels permanently and makes the jump impossible.

   ⚠️ Desktop only in practice. iOS and Android use overlay scrollbars that take
   no space, so there is nothing to reserve and this is inert; older browsers
   that do not know the property ignore it and behave exactly as they do today. */
html { scrollbar-gutter: stable; }
body {
  margin: 0;
  font-family: var(--font);
  color: var(--ink);
  background: var(--bg-deep);
  /* Rule 6: the body NEVER scrolls sideways. Wide content scrolls in its own box. */
  overflow-x: hidden;
  -webkit-text-size-adjust: 100%;
  -webkit-tap-highlight-color: transparent;
}
img, svg { max-width: 100%; }
button { font-family: inherit; }

/* Rule 5: hover ADDS polish, it never carries function. Layout decisions are
   made by (pointer: coarse), never by hover. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration: 0.01ms !important; transition-duration: 0.01ms !important; }
}

/* Visible focus for keyboard users on Mac/PC, without a ring on every tap. */
:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }
