/* ============================================================================
   GuardM8 v2 — design tokens
   Register: CoreM8's Mission-Control SaaS family (dark field, violet chrome,
   rounded cards with real shadows) — with one hard exception carried over from
   the original art direction: THE STATUS RAMP. This is life-safety kit, so the
   priority colours are the loudest thing on screen and violet is chrome only —
   it may never indicate alarm state. If you need a colour or size, add it here;
   nothing else in the product hardcodes one.
   ========================================================================== */

/* ── Typefaces — self-hosted only; an ARC network cannot reach the internet.
      CoreM8 uses Inter; Geist reads in the same register at UI sizes and is
      already on disk, so no font is added. ──────────────────────────────────── */

@font-face {
  font-family: "Archivo";
  src: url("/static/fonts/Archivo-Variable.woff2") format("woff2-variations");
  font-weight: 100 900;
  font-stretch: 62% 125%;
  font-display: block;
}
@font-face {
  font-family: "Geist";
  src: url("/static/fonts/Geist-400.woff2") format("woff2");
  font-weight: 400; font-display: swap;
}
@font-face {
  font-family: "Geist";
  src: url("/static/fonts/Geist-500.woff2") format("woff2");
  font-weight: 500; font-display: swap;
}
@font-face {
  font-family: "Geist";
  src: url("/static/fonts/Geist-600.woff2") format("woff2");
  font-weight: 600; font-display: swap;
}
@font-face {
  font-family: "Geist Mono";
  src: url("/static/fonts/GeistMono-400.woff2") format("woff2");
  font-weight: 400; font-display: swap;
}
@font-face {
  font-family: "Geist Mono";
  src: url("/static/fonts/GeistMono-500.woff2") format("woff2");
  font-weight: 500; font-display: swap;
}

:root {
  /* Dark is the default: this is a 24/7 room and most shifts are night shifts.
     Light is opt-in, either by the toggle or by the operating system. */
  color-scheme: dark;

  --f-display: "Archivo", ui-sans-serif, sans-serif;
  --f-text: "Geist", ui-sans-serif, system-ui, sans-serif;
  --f-mono: "Geist Mono", ui-monospace, "SF Mono", monospace;

  /* ── Type scale ───────────────────────────────────────────────────────────
     Every size is derived from --ui-scale, so a text-size preference grows the
     whole console in proportion rather than inflating body copy inside chrome
     that stayed put. The stamp lives on <html>, applied server-side before the
     first paint — a size that arrives after render means a flash of unreadable
     text on every navigation.

     The base moved up from 13px. This is read for twelve hours at a stretch,
     sometimes by people who need it bigger, and 13px was chosen for density on
     a screen nobody was actually sitting in front of all night. Density is now
     its own preference, which is where that trade-off belongs. */
  --ui-scale: 1;
  --t-11: calc(0.75rem  * var(--ui-scale));
  --t-13: calc(0.9375rem * var(--ui-scale));   /* base UI text — was 0.8125 */
  --t-15: calc(1.0625rem * var(--ui-scale));
  --t-18: calc(1.1875rem * var(--ui-scale));
  --t-22: calc(1.4375rem * var(--ui-scale));
  --t-26: calc(1.6875rem * var(--ui-scale));
  --t-32: calc(2.0625rem * var(--ui-scale));
  --t-44: calc(2.875rem  * var(--ui-scale));   /* counters, age clock, the wall */

  /* ── Field & surfaces — the Cosmic register (see DESIGN.md) ───────────────
     The field is a deep indigo that graduates, not a flat near-black, with a
     violet bloom off the top-right and a cold blue counter-bloom bottom-left.
     Panels are translucent gradients over that field rather than opaque slabs,
     so the ground shows through and the console reads as one lit surface.

     The rule that makes it safe on life-safety kit: violet is FURNITURE. Confining
     it to chrome leaves the data surface a colour vacuum, so anything coloured
     inside a table is signal by definition. The rich register buys priority
     contrast rather than costing it. ─────────────────────────────────────── */
  --canvas: #0e0a24;
  --bg-grad: radial-gradient(1300px 850px at 84% -14%, rgba(150, 86, 255, 0.32), transparent 58%),
             radial-gradient(950px 750px at 4% 110%, rgba(66, 124, 255, 0.22), transparent 60%),
             linear-gradient(168deg, #1b1243 0%, #150f35 44%, #0e0a24 100%);
  /* Panels are a gradient, not a fill. --surface stays solid for the few places
     that need opacity: scrims, and sticky headers over scrolling content. */
  --panel: linear-gradient(158deg, rgba(255, 255, 255, 0.078), rgba(255, 255, 255, 0.022));
  --panel-rail: linear-gradient(180deg, rgba(255, 255, 255, 0.062), rgba(255, 255, 255, 0.015));
  --surface: #16102f;
  --surface-2: #1b1439;
  --surface-3: #221a45;
  /* Decision band: a neutral slate header strip, not a priority-hued wash.
     Priority is still carried by the coloured bottom border + the prio chip. */
  --band-bg: #1a1d2b;
  --hover: rgba(255, 255, 255, 0.055);
  --hairline: rgba(179, 150, 255, 0.15);
  --hairline-strong: rgba(179, 150, 255, 0.24);

  --ink: #f5f2ff;
  --ink-bright: #ffffff;
  /* Measured against the panel ground (#1a162f — the canvas under the panel
     gradient), not guessed. --ink-faint was #7e77aa at 4.26:1, under the 4.5
     AA floor, and it is the token behind nearly every meta line on the page.
     Both now clear AAA, because this is small text read for twelve hours by
     people who are not all twenty-five. */
  --ink-dim: #cdc7ec;    /* 10.8:1 */
  --ink-faint: #aea7d4;  /* 7.7:1  */

  /* Scrollbars — the default bars sit on the gradient as opaque grey slabs and
     break the surface. Inset, translucent and violet instead. */
  --sb: rgba(169, 123, 255, 0.30);
  --sb-hover: rgba(169, 123, 255, 0.62);

  /* ── Chrome accent — the product's own colour. NEVER a status. ────────────── */
  --accent: #7c6cff;
  --accent-2: #9d6cff;
  /* A FILL, not the line colour. White on #7c6cff is 3.86:1 and fails AA, so a
     filled primary button needs its own darker value. One flat colour — a
     gradient on a button is decoration the button did not ask for, and it is on
     the avoid-list in DESIGN.md. */
  --accent-fill: #6d4fe8;        /* white text: 5.34:1 */
  --accent-fill-hover: #5f43d6;  /* white text: 6.44:1 */
  --accent-soft: rgba(124, 108, 255, 0.14);
  --accent-line: rgba(124, 108, 255, 0.40);
  /* No --grad. A violet-to-pink gradient on a control is decoration the
     control did not ask for, and it is on DESIGN.md's avoid-list. Fills are
     one flat colour; the only gradients left are the field itself and the
     translucent panel wash that lets the field show through. */

  /* ── Status ramp — alarm state ONLY, and the loudest thing on screen.
        Deliberately NOT CoreM8's pos/warn/neg palette: this is life-safety,
        and priority is always colour AND glyph AND position, never hue alone. */
  --p1: #ff3b2f;
  --p2: #ff5d8f;   /* high — rose. Was #ff9a1f orange; the customer does not
                      want yellow or orange anywhere (same call that moved P3
                      off mustard). Not blue: P3 is blue, and two blues for
                      adjacent priorities is the one confusion a queue gutter
                      cannot afford. */
  --p3: #4fb0f5;   /* medium — light blue (was #f2d24b yellow, at Andy's request) */
  --p4: #8e97a6;
  --ok: #4fd08a;
  --test: #a78bfa;

  /* Tinted beds for the same ramp, for header bands and row washes */
  --p1-bed: rgba(255, 59, 47, 0.12);
  --p2-bed: rgba(255, 154, 31, 0.11);
  --p3-bed: rgba(79, 176, 245, 0.10);
  --p4-bed: rgba(142, 151, 166, 0.10);
  --ok-bed: rgba(79, 208, 138, 0.10);
  --test-bed: rgba(167, 139, 250, 0.12);

  /* Overlay laid ON a camera frame. Tied to the theme only in the sense that
     it must NOT follow it: a dark scrim under dark text is unreadable. */
  --scrim: rgba(0, 0, 0, 0.62);
  --scrim-ink: #ffffff;

  /* ── Geometry — Cosmic: generous radii, real depth, a lit top edge ────────
     The inset highlight is what stops a translucent panel reading as a flat
     wash: it is the light of the field catching the panel's top edge. */
  --r: 11px;         /* controls: buttons, inputs, chips */
  --r-lg: 20px;      /* cards / panels */
  --r-sm: 8px;
  --r-pill: 999px;
  --shadow: 0 18px 44px -14px rgba(6, 2, 24, 0.85),
            0 1px 0 rgba(255, 255, 255, 0.10) inset;
  --shadow-lg: 0 30px 70px -18px rgba(6, 2, 24, 0.92),
               0 1px 0 rgba(255, 255, 255, 0.12) inset;
  /* Chrome glow. Reserved for the accent — never applied to a status colour,
     or the ramp stops being the loudest thing on screen. */
  --glow: 0 0 26px rgba(169, 123, 255, 0.55);
  /* CoreM8's own token names, aliased onto ours. The page-level stylesheets
     (alarm.css, estate.css) were written against CoreM8's vocabulary; without
     these they would silently fall back to hardcoded literals and stop tracking
     any change made here — the exact way a design system quietly drifts apart. */
  --radius: var(--r-lg);
  --radius-sm: var(--r);
  --line: var(--hairline);
  --line-2: var(--hairline-strong);

  --rail-w: 250px;
  --rail-w-collapsed: 64px;
  /* The annunciator holds text, so its height has to follow the type scale.
     Pinned at 52px it stayed put while the bar itself grew, and the rail —
     which sizes itself as 100vh minus this — ended up taller than the viewport
     with its last items pushed off the bottom. */
  --annunciator-h: calc(52px * var(--ui-scale));
  --row-h: 38px;

  --sp-1: 4px;
  --sp-2: 8px;
  --sp-3: 12px;
  --sp-4: 16px;
  --sp-5: 24px;
  --sp-6: 32px;
  --sp-7: 48px;

  /* ── Motion: brief, one curve, and almost nothing uses it ─────────────────── */
  --ease: cubic-bezier(0.22, 0.61, 0.36, 1);
  --fast: 140ms;
  --enter: 160ms;
}

/* ============================================================================
   LIGHT MODE

   Same product, day shift. Two ways in, and they must agree: an explicit
   choice stamps `data-theme` on <html> and always wins; with no choice made,
   the operating system's preference decides.

   The status ramp is NOT simply reused. The dark ramp is tuned for a near-black
   field, and light-mode contrast on white is its own problem — the old P3
   yellow (#f2d24b) dropped to roughly 1.4:1 against #fff, invisible, and an
   invisible fault indicator on an alarm board is a safety defect, not a
   cosmetic one. So light gets its own ramp: same order, same meaning, each hue
   picked to carry its weight on paper (P3 is now a mid blue, #1f74c4, ~4.6:1).
   The glyphs do the rest, as they do everywhere.
   ========================================================================== */

:root[data-theme="light"] { color-scheme: light; }
@media (prefers-color-scheme: light) {
  :root:not([data-theme="dark"]) { color-scheme: light; }
}

:root[data-theme="light"] {
  /* Cosmic in daylight: the same bloom, the same graduated field, lifted onto
     paper. Panels stay translucent gradients so the ground still shows through
     — a flat white card on a tinted field is the exact moment the register
     breaks and it starts looking like an admin template again. */
  --canvas: #eae7fc;
  --bg-grad: radial-gradient(1300px 850px at 84% -14%, rgba(150, 86, 255, 0.17), transparent 58%),
             radial-gradient(950px 750px at 4% 110%, rgba(66, 124, 255, 0.12), transparent 60%),
             linear-gradient(168deg, #f6f3ff 0%, #efebff 50%, #eae7fc 100%);
  --panel: linear-gradient(158deg, rgba(255, 255, 255, 0.96), rgba(255, 255, 255, 0.74));
  --panel-rail: linear-gradient(180deg, rgba(255, 255, 255, 0.86), rgba(255, 255, 255, 0.56));
  --surface: #ffffff;
  --surface-2: #fbfaff;
  --surface-3: #f2eefc;
  --band-bg: #e7e9ee;   /* light neutral grey — no lavender, no yellow */
  --hover: rgba(27, 16, 64, 0.05);
  --hairline: rgba(108, 70, 200, 0.15);
  --hairline-strong: rgba(108, 70, 200, 0.26);

  --ink: #1b1040;
  --ink-bright: #000000;
  --ink-dim: #584e83;    /* 7.2:1 on paper */
  --ink-faint: #67608c;  /* was #8981af at 3.50:1 — under the AA floor */

  --sb: rgba(109, 63, 224, 0.28);
  --sb-hover: rgba(109, 63, 224, 0.55);
  --shadow: 0 16px 38px -16px rgba(58, 32, 120, 0.26),
            0 1px 0 rgba(255, 255, 255, 0.9) inset;
  --shadow-lg: 0 26px 58px -18px rgba(58, 32, 120, 0.30),
               0 1px 0 rgba(255, 255, 255, 0.95) inset;
  --glow: 0 0 20px rgba(109, 63, 224, 0.28);

  --accent: #6355e0;          /* nudged darker: #7c6cff on white is thin */
  --accent-2: #8354e8;
  --accent-soft: rgba(124, 108, 255, 0.12);
  --accent-line: rgba(124, 108, 255, 0.45);

  /* Status ramp, re-tuned for paper. Hue and order preserved; luminance cut
     until each reads at a glance against white. The P1/P2 beds also carry more
     tint than their dark counterparts: 10% reads fine on black but nearly
     vanishes on white, and the band behind a fire or holdup alarm is not the
     place to be subtle. */
  --p1: #d6200f;
  --p2: #c2185b;   /* high — rose, the light-theme pair of #ff5d8f. Was
                      #c2500a orange. 5.87:1 on white. */
  --p3: #1f74c4;   /* medium — light blue (was mustard, at Andy's request) */
  --p4: #5c6675;
  --ok: #06774a;
  --test: #6d28d9;

  --p1-bed: rgba(214, 32, 15, 0.15);
  --p2-bed: rgba(194, 80, 10, 0.14);
  --p3-bed: rgba(31, 116, 196, 0.10);
  --p4-bed: rgba(92, 102, 117, 0.09);
  --ok-bed: rgba(6, 119, 74, 0.10);
  --test-bed: rgba(109, 40, 217, 0.10);

  --shadow: 0 1px 2px rgba(20, 20, 50, 0.06), 0 14px 34px -16px rgba(30, 30, 70, 0.22);
  --shadow-lg: 0 24px 60px -22px rgba(30, 30, 70, 0.28);


  /* The scrim over a camera frame stays dark in both themes — it sits on video,
     not on the page — so its text stays white. */
  --scrim: rgba(0, 0, 0, 0.55);
  --scrim-ink: #ffffff;
}

/* No explicit choice: follow the operating system. Written as a copy of the
   block above rather than a shorthand, because custom properties cannot be
   spread — kept adjacent so any edit to one is obvious in review. */
@media (prefers-color-scheme: light) {
  :root:not([data-theme="dark"]) {
    /* Cosmic in daylight: the same bloom, the same graduated field, lifted onto
       paper. Panels stay translucent gradients so the ground still shows through
       — a flat white card on a tinted field is the exact moment the register
       breaks and it starts looking like an admin template again. */
    --canvas: #eae7fc;
    --bg-grad: radial-gradient(1300px 850px at 84% -14%, rgba(150, 86, 255, 0.17), transparent 58%),
               radial-gradient(950px 750px at 4% 110%, rgba(66, 124, 255, 0.12), transparent 60%),
               linear-gradient(168deg, #f6f3ff 0%, #efebff 50%, #eae7fc 100%);
    --panel: linear-gradient(158deg, rgba(255, 255, 255, 0.96), rgba(255, 255, 255, 0.74));
    --panel-rail: linear-gradient(180deg, rgba(255, 255, 255, 0.86), rgba(255, 255, 255, 0.56));
    --surface: #ffffff;
    --surface-2: #fbfaff;
    --surface-3: #f2eefc;
    --band-bg: #e7e9ee;   /* light neutral grey — no lavender, no yellow */
    --hover: rgba(27, 16, 64, 0.05);
    --hairline: rgba(108, 70, 200, 0.15);
    --hairline-strong: rgba(108, 70, 200, 0.26);

    --ink: #1b1040;
    --ink-bright: #000000;
    --ink-dim: #584e83;    /* 7.2:1 on paper */
    --ink-faint: #67608c;  /* was #8981af at 3.50:1 — under the AA floor */

    --sb: rgba(109, 63, 224, 0.28);
    --sb-hover: rgba(109, 63, 224, 0.55);
    --shadow: 0 16px 38px -16px rgba(58, 32, 120, 0.26),
              0 1px 0 rgba(255, 255, 255, 0.9) inset;
    --shadow-lg: 0 26px 58px -18px rgba(58, 32, 120, 0.30),
                 0 1px 0 rgba(255, 255, 255, 0.95) inset;
    --glow: 0 0 20px rgba(109, 63, 224, 0.28);

    --accent: #6355e0;          /* nudged darker: #7c6cff on white is thin */
    --accent-2: #8354e8;
    --accent-soft: rgba(124, 108, 255, 0.12);
    --accent-line: rgba(124, 108, 255, 0.45);

    /* Status ramp, re-tuned for paper. Hue and order preserved; luminance cut
       until each reads at a glance against white. The P1/P2 beds also carry more
       tint than their dark counterparts: 10% reads fine on black but nearly
       vanishes on white, and the band behind a fire or holdup alarm is not the
       place to be subtle. */
    --p1: #d6200f;
    --p2: #c2185b;   /* high — rose, the light-theme pair of #ff5d8f. Was
                      #c2500a orange. 5.87:1 on white. */
    --p3: #1f74c4;   /* medium — light blue (was mustard, at Andy's request) */
    --p4: #5c6675;
    --ok: #06774a;
    --test: #6d28d9;

    --p1-bed: rgba(214, 32, 15, 0.15);
    --p2-bed: rgba(194, 80, 10, 0.14);
    --p3-bed: rgba(31, 116, 196, 0.10);
    --p4-bed: rgba(92, 102, 117, 0.09);
    --ok-bed: rgba(6, 119, 74, 0.10);
    --test-bed: rgba(109, 40, 217, 0.10);

    --shadow: 0 1px 2px rgba(20, 20, 50, 0.06), 0 14px 34px -16px rgba(30, 30, 70, 0.22);
    --shadow-lg: 0 24px 60px -22px rgba(30, 30, 70, 0.28);


    /* The scrim over a camera frame stays dark in both themes — it sits on video,
       not on the page — so its text stays white. */
    --scrim: rgba(0, 0, 0, 0.55);
    --scrim-ink: #ffffff;
  }
}

/* ============================================================================
   TEXT SIZE & DENSITY — per-operator, stamped on <html> server-side.

   Scale multiplies the whole type ramp. Density moves row heights and padding
   independently, so someone who needs large text on a laptop can claw back the
   rows by going compact, and someone on a wallboard can have both.
   ========================================================================== */

:root[data-scale="large"]  { --ui-scale: 1.15; }
:root[data-scale="xlarge"] { --ui-scale: 1.30; }
:root[data-scale="huge"]   { --ui-scale: 1.50; }

:root[data-density="compact"] {
  --row-h: 32px;
  --sp-3: 10px; --sp-4: 13px; --sp-5: 19px; --sp-6: 26px;
}
:root[data-density="roomy"] {
  --row-h: 48px;
  --sp-3: 15px; --sp-4: 20px; --sp-5: 30px; --sp-6: 40px;
}

/* Row height has to track the text or large type overflows a 38px row and gets
   clipped — the exact failure this preference exists to prevent. */
:root[data-scale="large"]  { --row-h: calc(38px * 1.15); }
:root[data-scale="xlarge"] { --row-h: calc(38px * 1.30); }
:root[data-scale="huge"]   { --row-h: calc(38px * 1.50); }
:root[data-scale="large"][data-density="compact"]  { --row-h: calc(32px * 1.15); }
:root[data-scale="xlarge"][data-density="compact"] { --row-h: calc(32px * 1.30); }
:root[data-scale="huge"][data-density="compact"]   { --row-h: calc(32px * 1.50); }
:root[data-scale="large"][data-density="roomy"]    { --row-h: calc(48px * 1.15); }
:root[data-scale="xlarge"][data-density="roomy"]   { --row-h: calc(48px * 1.30); }
:root[data-scale="huge"][data-density="roomy"]     { --row-h: calc(48px * 1.50); }

/* A console that animates is a console that lies about latency. With motion
   reduced — the owner's own default — the P1 gutter is solid rather than
   mid-breath, hover lifts do not translate, and nothing is left depending on
   an animation having run. */
@media (prefers-reduced-motion: reduce) {
  :root {
    --fast: 0ms;
    --enter: 0ms;
  }
  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
    scroll-behavior: auto !important;
  }
}
