/*
 * Orion Central — operator UI stylesheet.
 *
 * Design tokens are declared once at :root so every component pulls from the
 * same palette. Change a value here and the whole UI follows. Kept narrow
 * and orthogonal (background / surface / text / accent) so future themes
 * (light mode, high-contrast) just override these tokens.
 *
 * Animations are sub-200ms by design — fast enough to feel snappy, slow
 * enough to register. All entrance animations check `prefers-reduced-motion`
 * and fall back to no-motion.
 */

:root {
  /* Surfaces */
  /* Pure black, chosen 2026-08-28 against the new wordmark: on #0a0a0d the
     navy ORION half sat at 1.46:1 and read muddy; true black gives the mark
     the ground it was drawn on and lets the elevated surfaces below read as
     genuinely raised. One token - revert to #0a0a0d if it ever looks flat. */
  --bg: #000000;
  --bg-elevated: #15161a;
  --bg-elevated-2: #1c1d22;
  --surface: #1a1b20;
  --surface-hover: #232429;

  /* Text */
  --text: #e8e8ec;
  --text-dim: #9092a0;
  --text-faint: #5a5d68;

  /* Borders */
  --border: rgba(255, 255, 255, 0.06);
  --border-strong: rgba(255, 255, 255, 0.12);
  --border-accent: rgba(7, 187, 241, 0.4);

  /* Brand — ORION HELIOS OS rebrand 2026-08-28. Cyan #07bbf1 is lifted
     straight from the wordmark's HELI glyphs and orbit swoosh; the deep
     navy #182a63 is the ORION half. Semantic warn/err below are NOT brand
     colours - they stayed amber/red on purpose so alarm meaning survives
     the rebrand. */
  --accent: #07bbf1;
  --accent-hover: #3fcaf6;
  --accent-active: #0592c2;
  --accent-glow: rgba(7, 187, 241, 0.4);
  --accent-faint: rgba(7, 187, 241, 0.08);

  /* Semantic — match the green machine livery in the photo */
  --ok: #3ddc97;
  --ok-faint: rgba(61, 220, 151, 0.12);
  --warn: #ffd23f;
  --warn-faint: rgba(255, 210, 63, 0.12);
  --err: #ff4d6d;
  --err-faint: rgba(255, 77, 109, 0.12);
  --info: #6cb6ff;

  /* Type */
  --font-ui: "Inter", system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  --font-mono: "JetBrains Mono", ui-monospace, SF Mono, Consolas, monospace;
  /* Page-title display face. Archivo Narrow is condensed like the ORION
     HELIOS OS wordmark (heavy, squared, tight fit) but is a working UI
     face, so it survives 1.4rem on an operator panel. It replaced a
     rounded geometric sci-fi face on 2026-08-30 that echoed nothing in
     the mark and sat on Home + Engineering ONLY - which is exactly why
     those two pages read as a different app. */
  --font-display: "Archivo Narrow", "Inter", system-ui, sans-serif;

  /* Radii + motion */
  --r-sm: 6px;
  --r-md: 10px;
  --r-lg: 14px;
  --r-xl: 22px;
  --t-fast: 120ms cubic-bezier(0.4, 0, 0.2, 1);
  --t-base: 180ms cubic-bezier(0.4, 0, 0.2, 1);
  --t-slow: 320ms cubic-bezier(0.4, 0, 0.2, 1);

  /* Shadows */
  --shadow-card: 0 8px 32px rgba(0, 0, 0, 0.4), 0 2px 6px rgba(0, 0, 0, 0.2);
  --shadow-button: 0 4px 14px var(--accent-glow);
}

/* ---- Reset + base -------------------------------------------------- */

*, *::before, *::after { box-sizing: border-box; }

html, body {
  margin: 0;
  padding: 0;
  background: var(--bg);
  color: var(--text);
  font-family: var(--font-ui);
  font-size: 14px;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

body::before {
  /* Subtle radial accent in the top-right — gives the dark page some life
     without being an overt gradient. */
  content: "";
  position: fixed;
  inset: 0;
  pointer-events: none;
  background:
    radial-gradient(ellipse 800px 600px at 90% -10%, rgba(7, 187, 241, 0.06), transparent 60%),
    radial-gradient(ellipse 600px 500px at 10% 110%, rgba(108, 182, 255, 0.04), transparent 60%);
  z-index: 0;
}

a { color: var(--accent); text-decoration: none; transition: color var(--t-fast); }
a:hover { color: var(--accent-hover); }

h1, h2, h3, h4 { font-weight: 600; letter-spacing: -0.01em; margin: 0; color: var(--text); }
/* The SCADA is showing history, not the machine. Loud, because the cost of
   missing it is an operator trusting a stale alarm list. */
.scada-link-banner {
  padding: 9px 14px;
  margin-bottom: 10px;
  border-radius: var(--r-sm);
  border: 1px solid var(--err);
  background: var(--err-faint);
  color: var(--err);
  font-size: 0.8rem;
  font-weight: 700;
  letter-spacing: 0.01em;
}
/* Desaturate the whole board while the link is down. The banner on its own
   is too easy to read past when you are looking for one number. */
/* NOTE the `scada-` prefix — the first version of this rule said
   `.alarm-table-wrap` and matched NOTHING, so the board stayed full colour
   while claiming to be desaturated. Also covers the side panels: Insights
   was confidently reporting sort accuracy from two-day-old data. */
body.scada-offline .scada-stage,
body.scada-offline .scada-alarm-table-wrap,
body.scada-offline .scada-side-section,
body.scada-offline #alarm-tbody {
  filter: grayscale(0.85) opacity(0.62);
  transition: filter var(--t-slow);
}

/* ---- Per-chute accuracy ------------------------------------------------
   A chute-local fault hides inside the site average: chute 5 ran at 81.5%
   for a week while the site read 91.4%. Two numbers per chute, and the
   SERIOUS one (a stray parcel in the wrong chute) gets the loud treatment
   while a delivery decline gets the quiet one. */
.chute-acc-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(210px, 1fr));
  gap: 12px;
}
.chute-acc {
  padding: 12px 14px;
  border: 1px solid var(--border);
  border-left: 3px solid var(--border);
  border-radius: var(--r-md);
  background: var(--bg-elevated);
}
/* A delivery decline is a trend — amber, quiet. */
.chute-acc.is-declining { border-left-color: var(--warn); }
/* A stray parcel is the wrong-chute case. One is significant. */
.chute-acc.is-stray {
  border-left-color: var(--err);
  background: linear-gradient(90deg, var(--err-faint), var(--bg-elevated) 60%);
}
/* Thin data must not read as a verdict either way. */
.chute-acc.is-thin { opacity: 0.72; }

.chute-acc-head {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  margin-bottom: 9px;
}
.chute-acc-name { font-size: 0.9rem; font-weight: 700; }
.chute-acc-flag {
  font-size: 0.62rem; font-weight: 700; letter-spacing: 0.06em;
  text-transform: uppercase; padding: 2px 7px; border-radius: 999px;
  background: rgba(255, 255, 255, 0.06); color: var(--text-dim);
}
.chute-acc-flag.is-warn { background: var(--warn-faint); color: var(--warn); }
.chute-acc-flag.is-bad  { background: var(--err-faint);  color: var(--err); }

.chute-acc-rows { display: flex; flex-direction: column; gap: 7px; }
.chute-acc-row {
  display: grid; grid-template-columns: 4.6em auto; column-gap: 8px;
  align-items: baseline;
}
.chute-acc-label {
  font-size: 0.64rem; font-weight: 600; letter-spacing: 0.06em;
  text-transform: uppercase; color: var(--text-faint);
}
.chute-acc-value {
  font-size: 1.15rem; font-weight: 700; font-variant-numeric: tabular-nums;
}
.chute-acc-value.is-bad { color: var(--err); }
.chute-acc-sub {
  grid-column: 2; font-size: 0.68rem; color: var(--text-dim);
}
.chute-acc-trend {
  margin-top: 9px; padding-top: 8px;
  border-top: 1px solid var(--border);
  font-size: 0.68rem; color: var(--text-faint);
}

/* ---- Page titles ------------------------------------------------------
   ONE treatment, every page. Until 2026-08-30 only Home and Engineering
   carried a display face, which is the mismatch Chris spotted: those two
   pages read as a different app. Condensed uppercase echoes the ORION
   HELIOS OS wordmark; uppercase wants POSITIVE tracking or the caps jam
   together at this weight. */
h1 {
  font-family: var(--font-display);
  font-size: 1.4rem;
  font-weight: 700;
  letter-spacing: 0.045em;
  text-transform: uppercase;
}
/* ...except where the title IS the data. A serial, an asset name, a
   customer name or a work-order title is content, not furniture:
   uppercasing it changes what it says, and a condensed face makes a long
   machine name harder to read, not easier. These keep the UI face and
   their own casing. */
/* ...and where the title is a SENTENCE rather than a label. "This feature
   lives on cloud central" set in condensed caps reads as shouting at
   someone who has just hit a dead end. */
h1.is-record,
h1.is-prose,
h1.mono {
  font-family: var(--font-ui);
  font-weight: 700;
  letter-spacing: 0;
  text-transform: none;
}
h1.mono { font-family: var(--font-mono); }
h2 { font-size: 1.25rem; }
h3 { font-size: 1.05rem; }

code, .mono { font-family: var(--font-mono); font-size: 0.92em; }

/* ---- Top header (when present on a page) --------------------------- */

.page-header {
  position: sticky;
  top: 0;
  z-index: 50;
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 10px 20px;
  background: rgba(10, 10, 13, 0.7);
  backdrop-filter: blur(20px) saturate(140%);
  -webkit-backdrop-filter: blur(20px) saturate(140%);
  border-bottom: 1px solid var(--border);
  /* Allow the bar to wrap to a second row when there isn't enough
     horizontal space — prevents the nav from overlapping the user
     panel on mid-width screens. */
  flex-wrap: wrap;
  row-gap: 8px;
}

.page-header .header-left {
  display: flex;
  align-items: center;
  gap: 22px;             /* applies between Orion mark + title + nav.
                            The per-tenant customer logo lives in
                            .header-right now (top-right anchor) so
                            this gap no longer needs any margin tricks. */
  flex: 1 1 auto;
  min-width: 0;          /* lets children shrink past their content size */
  flex-wrap: wrap;
  row-gap: 6px;
}
.page-header .header-right {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-shrink: 0;        /* user panel never shrinks; nav wraps instead */
}

/* White-piped Orion wordmark (same asset as the login screen).
   120px tall — 50% larger than the old 80px so it reads from across
   the warehouse floor. Negative top/bottom margins keep the flex
   line at the same ~80px height the banner was sized around, so the
   bar's overall height does NOT grow; the wordmark just visually
   overflows by ~10px above and below (drop-shadow softens this).
   The -8px right margin pulls "Warehouse Control System" in tighter
   to the wordmark; the previous 14px gap had the title sitting too
   far out. */
/* Header logos. Orion is bumped 20 % from baseline so the wordmark
   reads as the dominant element; the customer mark drops 20 % so
   it sits as a sidekick. The negative right-margin on the Orion
   logo closes the gap to the customer logo to half of the parent
   .header-left gap, keeping the title's spacing untouched. */
.header-logo {
  height: 130px;          /* +20 % from 108 → fills the banner space */
  width: auto;
  object-fit: contain;
  flex-shrink: 0;
  /* Negative top/bottom margins absorb the extra height inside the
     banner's existing 10 px padding so the bar height itself doesn't
     grow. Right margin removed — the parent .header-left now has a
     tight 10 px gap that handles spacing on its own. */
}

/* ORION | <product> wordmark — uses the PNG asset directly.
   The asset is alpha-keyed (transparent background) AND has the
   white piping outline baked in by scripts/alpha_key_logo.py with
   `--piping`. We don't need CSS drop-shadow tricks to add the
   outline — keep CSS to a soft warm halo only. */
.header-brand { display: inline-flex; align-items: center; flex-shrink: 0; }
.header-wordmark {
  height: 88px;
  width: auto;
  object-fit: contain;
  margin: -8px 0;
  /* No glow — the v2 wordmark has its own comet sweep that's
     visually busy enough; stacking a drop-shadow on top muddied
     it. Keep the asset as-shipped. */
}
@media (max-width: 1100px) {
  .header-wordmark { height: 64px; }
}
@media (max-width: 720px) {
  .header-wordmark { height: 50px; }
}
/* Customer identity block — pinned to the right cluster.
   Always present whenever a user is logged in; the logo is the
   conditional bit. The label sits to the LEFT of the logo so the
   eye lands on the wordmark/symbol last (the brighter visual
   weight wants the rightmost anchor). */
.header-customer {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-left: 6px;          /* breathing room from the logout button */
  padding-left: 12px;
  border-left: 1px solid var(--border);  /* visual divider from the user pane */
  flex-shrink: 0;
  min-width: 0;
}
.header-customer-name {
  color: var(--text-dim);
  font-size: 0.9rem;
  font-weight: 500;
  letter-spacing: 0.01em;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 220px;
}
.header-customer-logo {
  /* 50% of the previous size — operator feedback was that 65px
     read as competing with the Orion wordmark rather than
     subordinate to it. 32px is small enough to feel like
     attribution but still legible at control-room distance. */
  height: 32px;
  width: auto;
  object-fit: contain;
  flex-shrink: 0;
  filter: drop-shadow(0 0 18px rgba(7, 187, 241, 0.32));
}
@media (max-width: 1100px) {
  .header-logo          { height: 88px; margin: -8px 0; }
  .header-customer-logo { height: 26px; }
  .header-customer-name { max-width: 140px; font-size: 0.82rem; }
}
@media (max-width: 720px) {
  /* Phone width — drop the name to keep the right cluster
     compact; the logo alone still identifies the tenant. */
  .header-customer-name { display: none; }
  .header-customer { padding-left: 8px; gap: 6px; }
}
/* (Historic) the header once carried a Casio-80s display title. That face
   was retired app-wide on 2026-08-30 — see --font-display above. */
/* `.header-title` (Warehouse Control System / Command Center
   stacked-text monogram) was removed when the ORION | <product>
   wordmark replaced the single Orion stamp + tagline. Keeping the
   CSS deleted so future grep doesn't lead anyone astray. */

.header-nav {
  display: flex;
  align-items: center;
  gap: 2px;
  flex-wrap: wrap;
  row-gap: 4px;
}
.header-nav a {
  display: inline-block;
  padding: 5px 10px;
  border-radius: var(--r-md);
  color: var(--text-dim);
  font-size: 0.88rem;
  font-weight: 500;
  white-space: nowrap;
  transition: color var(--t-fast), background var(--t-fast);
}
.header-nav a:hover { color: var(--text); background: var(--surface-hover); }
.header-nav a.active { color: var(--text); background: var(--accent-faint); }

.header-user {
  display: flex;
  align-items: center;
  gap: 6px;
  color: var(--text-dim);
  font-size: 0.85rem;
  /* Long emails would push the badge + logout off-screen. Cap and
     ellipsis on the email span; full address shows in `title` tooltip. */
  max-width: 240px;
}
.header-user > span:first-child {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  min-width: 0;
}
/* Drop the email entirely below ~860px — the role badge alone is enough
   identity-disambiguation when space is at a premium. */
@media (max-width: 860px) {
  .header-user > span:first-child { display: none; }
}
.header-user .badge {
  display: inline-block; padding: 2px 8px; border-radius: 999px;
  font-size: 0.72rem; font-weight: 600; letter-spacing: 0.04em; text-transform: uppercase;
  background: var(--accent-faint); color: var(--accent); border: 1px solid var(--border-accent);
}

/* ---- Main content shell ------------------------------------------- */

.main { padding: 24px; max-width: 1600px; margin: 0 auto; position: relative; z-index: 1; }

/* ---- Buttons ------------------------------------------------------ */

.btn, button.primary, button[type="submit"] {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding: 10px 16px;
  border: 1px solid transparent;
  border-radius: var(--r-md);
  font-family: inherit;
  font-size: 0.95rem;
  font-weight: 600;
  cursor: pointer;
  background: var(--accent);
  color: #1a0a00;
  transition: background var(--t-fast), transform var(--t-fast), box-shadow var(--t-base);
  box-shadow: 0 0 0 0 var(--accent-glow);
}
.btn:hover, button.primary:hover, button[type="submit"]:hover {
  background: var(--accent-hover);
  box-shadow: var(--shadow-button);
  transform: translateY(-1px);
}
.btn:active, button.primary:active, button[type="submit"]:active {
  background: var(--accent-active);
  transform: translateY(0);
}
.btn.ghost {
  background: transparent;
  color: var(--text);
  border-color: var(--border-strong);
}
.btn.ghost:hover {
  background: var(--surface-hover);
  box-shadow: none;
}
/* Destructive ghost variant — used for hard-delete buttons in the
   user admin table. Stays calm at rest, turns red on hover so a
   misclick is visually obvious before commit. */
.btn.ghost.danger {
  color: #f87171;
  border-color: rgba(248, 113, 113, 0.35);
}
.btn.ghost.danger:hover {
  background: rgba(248, 113, 113, 0.10);
  border-color: rgba(248, 113, 113, 0.65);
  color: #fca5a5;
}

/* ---- Form inputs -------------------------------------------------- */

label { display: block; font-size: 0.82rem; font-weight: 500; color: var(--text-dim); margin: 14px 0 6px 0; letter-spacing: 0.02em; text-transform: uppercase; }

/* Cover every text-ish form control + select + textarea with one
   shared rule. Earlier the rule only listed text/email/password/
   search, which left number/date/tel/url plus selects and textareas
   to fall back to the browser default — white-on-white on the
   dark theme. The maintenance module surfaced this hard because
   its forms use selects + textareas heavily. */
input[type="text"],
input[type="email"],
input[type="password"],
input[type="search"],
input[type="number"],
input[type="tel"],
input[type="url"],
input[type="date"],
input[type="datetime-local"],
input[type="time"],
select,
textarea {
  width: 100%;
  padding: 11px 14px;
  font-family: inherit;
  font-size: 0.95rem;
  color: var(--text);
  background: var(--bg-elevated);
  border: 1px solid var(--border-strong);
  border-radius: var(--r-md);
  transition: border-color var(--t-fast), box-shadow var(--t-fast), background var(--t-fast);
  outline: none;
}
input[type="text"]:focus,
input[type="email"]:focus,
input[type="password"]:focus,
input[type="search"]:focus,
input[type="number"]:focus,
input[type="tel"]:focus,
input[type="url"]:focus,
input[type="date"]:focus,
input[type="datetime-local"]:focus,
input[type="time"]:focus,
select:focus,
textarea:focus {
  border-color: var(--accent);
  box-shadow: 0 0 0 4px var(--accent-faint);
  background: var(--bg-elevated-2);
}
input::placeholder, textarea::placeholder { color: var(--text-faint); }
textarea { resize: vertical; min-height: 64px; line-height: 1.5; }
/* Native date pickers in WebKit/Blink. `color-scheme: dark` on the input
   tells Chromium to render the dark-theme variant of the picker — that
   ALONE makes the calendar/clock indicator icon white. A `filter: invert`
   on top would re-invert it back to black, so we explicitly disable any
   inherited filter and let the native dark-theme glyph through. */
input[type="date"],
input[type="datetime-local"],
input[type="time"] {
  color-scheme: dark;
}
input[type="date"]::-webkit-calendar-picker-indicator,
input[type="datetime-local"]::-webkit-calendar-picker-indicator,
input[type="time"]::-webkit-calendar-picker-indicator {
  filter: none;
  opacity: 0.85;
  cursor: pointer;
}
/* Select dropdown chevron — Firefox styles via appearance; WebKit
   needs an explicit background-image. Keep it subtle. */
select {
  appearance: none;
  -webkit-appearance: none;
  background-image: linear-gradient(45deg, transparent 50%, var(--text-dim) 50%),
                    linear-gradient(135deg, var(--text-dim) 50%, transparent 50%);
  background-position: calc(100% - 18px) 52%, calc(100% - 13px) 52%;
  background-size: 5px 5px, 5px 5px;
  background-repeat: no-repeat;
  padding-right: 32px;
}
select option { background: var(--bg-elevated); color: var(--text); }

/* ---- Cards -------------------------------------------------------- */

.card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  padding: 24px;
  box-shadow: var(--shadow-card);
}
.card.large { padding: 32px; }

.error {
  margin-top: 12px;
  padding: 10px 14px;
  background: var(--err-faint);
  color: var(--err);
  border: 1px solid rgba(255, 77, 109, 0.3);
  border-radius: var(--r-sm);
  font-size: 0.9rem;
}
.hint { margin-top: 12px; color: var(--text-dim); font-size: 0.85rem; }

/* ---- Auth split-screen layout ------------------------------------- */

.auth-layout {
  display: grid;
  grid-template-columns: 1fr 1fr;
  min-height: 100vh;
  position: relative;
  z-index: 1;
}

.auth-hero {
  position: relative;
  background-image: url("/static/sorter_hero.jpg");
  background-size: cover;
  background-position: center;
  overflow: hidden;
}

.auth-hero::after {
  /* Gradient overlay so the brand text is readable over any photo. */
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(120deg, rgba(10, 10, 13, 0.85) 0%, rgba(10, 10, 13, 0.45) 60%, rgba(10, 10, 13, 0.6) 100%);
}

.auth-hero-content {
  position: relative;
  z-index: 1;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  height: 100%;
  padding: 48px;
  color: var(--text);
}

.auth-hero-content .brand {
  display: flex;
  align-items: center;
  gap: 6px;
  font-weight: 600;
  letter-spacing: -0.005em;
}
.auth-hero-content .brand img { height: 144px; filter: drop-shadow(0 0 28px rgba(7, 187, 241, 0.4)); }
.auth-hero-content .brand-text { display: flex; flex-direction: column; gap: 2px; }
.auth-hero-content .brand-name { font-size: 1.15rem; }
.auth-hero-content .brand-strapline { font-size: 0.95rem; font-weight: 400; font-style: italic; color: var(--text-dim); letter-spacing: 0.01em; }

.auth-hero-content .tagline { max-width: 520px; }
.auth-hero-content .tagline h1 {
  /* The sign-in hero is a GREETING ("Welcome back."), not a page title:
     sentence case, UI face, and its own tight negative tracking. It must
     opt out of the global uppercase title rule explicitly - inheriting the
     condensed caps while keeping -0.02em would jam the letters together. */
  font-family: var(--font-ui);
  text-transform: none;
  font-size: 2.6rem;
  font-weight: 700;
  letter-spacing: -0.02em;
  line-height: 1.1;
  margin-bottom: 16px;
}
.auth-hero-content .tagline p { color: var(--text-dim); font-size: 1.05rem; line-height: 1.55; max-width: 460px; }

.auth-hero-content .meta { color: var(--text); font-family: var(--font-mono); font-size: 0.85rem; letter-spacing: 0.04em; opacity: 0.92; }
.auth-hero-content .meta .live-dot {
  display: inline-block; width: 8px; height: 8px; border-radius: 50%;
  background: var(--ok); margin-right: 8px; vertical-align: middle;
  box-shadow: 0 0 12px var(--ok);
  animation: pulseDot 2s ease-in-out infinite;
}

.auth-form-wrap {
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 48px 24px;
  background: var(--bg);
}
.auth-form-wrap > .card {
  width: 100%;
  max-width: 420px;
  animation: fadeInUp 480ms cubic-bezier(0.16, 1, 0.3, 1);
}
.auth-form-wrap.wide > .card { max-width: 540px; }

.auth-form-wrap h2 { margin-bottom: 4px; font-size: 1.5rem; }
.auth-form-wrap .sub { color: var(--text-dim); font-size: 0.92rem; margin-bottom: 18px; }

/* When the auth layout is shown, hide the page-header (login pages don't need it). */
.auth-layout ~ .page-header, body.auth-page .page-header { display: none; }

/* Mobile — stack hero above form. */
@media (max-width: 880px) {
  .auth-layout { grid-template-columns: 1fr; }
  .auth-hero { min-height: 240px; }
  .auth-hero-content { padding: 32px; }
  .auth-hero-content .tagline h1 { font-size: 1.8rem; }
}

/* ---- Tables (parcel feed etc.) ----------------------------------- */

/* overflow-x auto, NOT hidden: on narrow screens a wide table must
   scroll sideways inside its own wrapper, never get clipped (Chris,
   2026-09-01: "we should be able to scroll to the right where needed"). */
.table-wrap { background: var(--surface); border: 1px solid var(--border); border-radius: var(--r-lg); overflow-x: auto; overflow-y: hidden; box-shadow: var(--shadow-card); }
.table-toolbar { display: flex; align-items: center; gap: 16px; padding: 14px 18px; border-bottom: 1px solid var(--border); flex-wrap: wrap; }
.table-toolbar .grow { flex: 1; }

/* Live indicator — green pulsing dot + uppercase mono label. Used on
   the parcel feed, stats page, audit page, anywhere we want a "this
   is updating in real time" affordance. Originally scoped to
   `.table-toolbar` but lifted to global so any element with the class
   gets the dot — the stats page's window-selector wasn't getting it. */
.live-indicator {
  display: inline-flex; align-items: center; gap: 8px;
  font-size: 0.82rem; color: var(--text-dim);
  font-family: var(--font-mono); letter-spacing: 0.04em; text-transform: uppercase;
}
.live-indicator::before {
  content: ""; width: 8px; height: 8px; border-radius: 50%;
  background: var(--ok); box-shadow: 0 0 10px var(--ok);
  animation: pulseDot 2s ease-in-out infinite;
}

table.data {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.9rem;
}
table.data thead th {
  position: sticky; top: 0;
  background: var(--bg-elevated);
  color: var(--text-dim);
  text-align: left;
  font-weight: 500;
  font-size: 0.78rem;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  padding: 12px 16px;
  border-bottom: 1px solid var(--border);
}
table.data tbody td {
  padding: 11px 16px;
  border-bottom: 1px solid var(--border);
  color: var(--text);
}
table.data tbody tr {
  transition: background var(--t-fast);
}
table.data tbody tr:hover { background: var(--surface-hover); }
table.data tbody tr:last-child td { border-bottom: none; }
table.data .col-mono { font-family: var(--font-mono); color: var(--text-dim); font-size: 0.85rem; }
table.data .col-strong { font-weight: 600; color: var(--text); }

/* Status pills */
.pill { display: inline-block; padding: 2px 10px; border-radius: 999px; font-size: 0.75rem; font-weight: 600; letter-spacing: 0.03em; border: 1px solid transparent; }
.pill.ok   { background: var(--ok-faint);   color: var(--ok);   border-color: rgba(61, 220, 151, 0.3); }
.pill.warn { background: var(--warn-faint); color: var(--warn); border-color: rgba(255, 210, 63, 0.3); }
.pill.err  { background: var(--err-faint);  color: var(--err);  border-color: rgba(255, 77, 109, 0.3); }
.pill.dim  { background: rgba(255, 255, 255, 0.04); color: var(--text-dim); border-color: var(--border); }
/* "Assumed" chute pill (issue #632): exit timeout expired with no
   PEC-confirmed divert — the number shown is where the parcel almost
   certainly ended up (end-of-line jackpot), NOT a measured landing.
   Dashed border + italic so it can never be read as the solid
   confirmed ok/err pills. */
.pill.assumed { background: var(--warn-faint); color: var(--warn);
                border: 1px dashed rgba(255, 210, 63, 0.55); font-style: italic; }

/* Search input lives inside the table toolbar — make it look like a proper search box */
.table-toolbar input[type="search"] {
  width: 360px;
  max-width: 100%;
  padding: 8px 12px;
  font-size: 0.9rem;
  background: var(--bg-elevated);
  border: 1px solid var(--border-strong);
  border-radius: var(--r-md);
  color: var(--text);
  transition: border-color var(--t-fast), box-shadow var(--t-fast);
}
.table-toolbar input[type="search"]:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 4px var(--accent-faint);
}

/* Live indicator — colour reflects polling state */
.live-indicator[data-state="paused"]::before { background: var(--text-dim); box-shadow: none; animation: none; }
.live-indicator[data-state="paused"] { color: var(--text-dim); }
.live-indicator[data-state="error"]::before { background: var(--err); box-shadow: 0 0 10px var(--err); animation: pulseDot 1s ease-in-out infinite; }
.live-indicator[data-state="error"] { color: var(--err); }
.live-indicator[data-state="search"]::before { background: var(--accent); box-shadow: 0 0 10px var(--accent-glow); animation: none; }
.live-indicator[data-state="search"] { color: var(--accent); }
/* "Connecting" is the state every live badge now STARTS in. The markup used to
   ship data-state="live", so the badge asserted the page was live before a
   single poll had succeeded — and kept asserting it if the script died. That
   is exactly how a dead parcel page went unnoticed on 2026-08-23: LIVE in
   green above a table that had stopped updating and a search box that did
   nothing. Amber, unglowing and slow, so it reads as "not yet" rather than
   "fine". Only a successful poll turns it green. */
.live-indicator[data-state="connecting"]::before {
  background: var(--warn); box-shadow: none;
  animation: pulseDot 2.6s ease-in-out infinite;
}
.live-indicator[data-state="connecting"] { color: var(--warn); }

/* Components panel — child assets nested inside the parent */
.component-row {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 10px 12px;
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  background: var(--bg-elevated, var(--surface));
  margin-bottom: 6px;
}
.component-row:last-child { margin-bottom: 0; }
.component-row-name { font-weight: 500; min-width: 200px; }
.component-row-type {
  padding: 2px 8px;
  background: var(--surface-hover);
  border-radius: 999px;
  font-size: 0.78rem;
}

/* SCADA-import preview tree */
.scada-import-tree {
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  background: var(--bg-elevated, var(--surface));
  padding: 10px 12px;
  font-size: 0.88rem;
}
.scada-import-row {
  display: grid;
  grid-template-columns: 20px 1fr auto auto;
  gap: 10px;
  align-items: center;
  padding: 4px 0;
}
.scada-import-bullet {
  color: var(--text-faint);
  font-family: var(--font-mono);
}
.scada-import-name { font-weight: 500; }
.scada-import-action {
  font-size: 0.72rem;
  font-weight: 600;
  text-transform: uppercase;
  padding: 2px 8px;
  border-radius: 999px;
}
.scada-import-action-create { background: var(--ok-faint, rgba(0,160,80,0.12)); color: #4caf50; }
.scada-import-action-update { background: var(--accent-faint, rgba(72,144,226,0.12)); color: var(--accent); }
.scada-import-action-skip   { background: var(--surface-hover); color: var(--text-dim); }

/* Bulk-select toolbar — appears when ≥1 parcel ticked */
.parcels-bulk-bar {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 10px 14px;
  margin-bottom: 8px;
  background: var(--accent-faint, rgba(72, 144, 226, 0.12));
  border: 1px solid var(--accent);
  border-radius: var(--r-md);
  position: sticky;
  top: 60px; /* clears the page header */
  z-index: 20;
}
.parcels-bulk-bar[hidden] { display: none; }
.parcels-bulk-count strong { color: var(--accent); font-size: 1.05rem; }

/* Bulk-select column — narrow, centered */
table.data .parcels-checkbox-col {
  width: 32px;
  text-align: center;
  padding-left: 8px;
  padding-right: 4px;
}
table.data .parcels-checkbox-col input[type="checkbox"] {
  cursor: pointer;
  margin: 0;
  vertical-align: middle;
}

/* Parcel rows + click-to-expand */
table.data tbody tr.parcel-row { cursor: pointer; }
table.data tbody tr.parcel-row.expanded { background: var(--surface-hover); }
table.data tbody tr.parcel-row.expanded td:first-child {
  box-shadow: inset 3px 0 0 var(--accent);
}

/* New rows fade in from the top */
@keyframes parcelEnter {
  from { background: var(--accent-faint); opacity: 0.7; }
  to   { background: transparent; opacity: 1; }
}
table.data tbody tr.parcel-row.entering { animation: parcelEnter 1500ms ease-out; }
@media (prefers-reduced-motion: reduce) {
  table.data tbody tr.parcel-row.entering { animation: none; }
}

/* Detail row — tabs + content */
table.data tbody tr.detail-row { background: var(--bg-elevated); }
table.data tbody tr.detail-row td { padding: 0; border-bottom: 1px solid var(--border); }
.detail-tabs {
  display: flex;
  gap: 0;
  border-bottom: 1px solid var(--border);
  background: var(--bg-elevated-2);
  padding: 0 16px;
}
.detail-tabs .tab {
  appearance: none;
  background: none;
  border: none;
  border-bottom: 2px solid transparent;
  color: var(--text-dim);
  padding: 12px 16px;
  font-family: inherit;
  font-size: 0.88rem;
  font-weight: 500;
  cursor: pointer;
  transition: color var(--t-fast), border-color var(--t-fast);
}
.detail-tabs .tab:hover { color: var(--text); }
.detail-tabs .tab.active { color: var(--accent); border-bottom-color: var(--accent); }

.detail-pane { display: none; padding: 20px 24px; }
.detail-pane.active { display: block; animation: fadeInUp 240ms cubic-bezier(0.16, 1, 0.3, 1); }

.detail-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: 14px 24px;
}
/* min-width:0 lets a grid item shrink below its content's intrinsic width,
   so a long value wraps inside its own column instead of overflowing into
   the neighbouring field. */
.detail-field { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.detail-field label { display: block; font-size: 0.7rem; font-weight: 500; color: var(--text-faint); margin: 0; letter-spacing: 0.06em; text-transform: uppercase; }
/* overflow-wrap:anywhere is the belt-and-braces — even a delimiter-less long
   token breaks within the cell rather than spilling over. The <wbr> after
   each pipe (see breakable() in app.js) makes the break land on a delimiter. */
.detail-field span { font-size: 0.95rem; overflow-wrap: anywhere; }
.detail-field span.small { font-size: 0.82rem; }
.detail-field .dim-W { color: #6cb6ff; font-weight: 600; }
.detail-field .dim-L { color: var(--ok);   font-weight: 600; }
.detail-field .dim-H { color: var(--err);  font-weight: 600; }

.detail-actions { margin-top: 18px; display: flex; gap: 10px; }

/* Image grid + tiles. Keyence delivers wide panoramic images
   (~3:1 aspect — typical sorter side-camera). The tile is sized
   to show the whole frame letterboxed, not cropped, so operators
   can see what's actually on the parcel without clicking through
   to the lightbox. Click for full-resolution lightbox view. */
.image-grid { display: flex; flex-wrap: wrap; gap: 10px; min-height: 60px; }
.image-tile {
  background: #000000; border: 1px solid var(--border-strong); padding: 0;
  border-radius: var(--r-sm); cursor: pointer;
  transition: transform var(--t-fast), border-color var(--t-fast);
  overflow: hidden;
}
.image-tile:hover { transform: translateY(-1px); border-color: var(--accent); }
.image-tile img {
  display: block; width: 100%; max-width: 480px;
  height: 160px; object-fit: contain;
  background: #000000;
  border-radius: var(--r-sm);
}
.image-tile-placeholder {
  padding: 32px 20px; color: var(--text-dim); font-size: 0.92rem;
  background: var(--bg-elevated-2); border: 1px dashed var(--border-strong);
  border-radius: var(--r-sm); text-align: center; flex: 1; min-width: 280px;
}
.image-tile-placeholder .small { font-size: 0.78rem; }

/* Lightbox */
.lightbox {
  position: fixed; inset: 0; z-index: 200;
  background: rgba(0, 0, 0, 0.92);
  display: flex; align-items: center; justify-content: center;
  cursor: zoom-out;
  animation: fadeInUp 180ms ease-out;
}
.lightbox[hidden] { display: none; }
.lightbox img { max-width: 92vw; max-height: 92vh; object-fit: contain; pointer-events: none; }

/* Print report (PDF): the per-parcel report is now a self-contained
   light document rendered into a hidden iframe (see printParcel in
   app.js) — no @media print theming needed here, and none should be
   added: restyling the dark SPA for print is how dark-on-dark
   reports happened. */

/* ---- Empty state -------------------------------------------------- */

.empty {
  padding: 64px 32px;
  text-align: center;
  color: var(--text-dim);
}
.empty code { color: var(--accent); }

/* ---- Recovery codes / secret box ---------------------------------- */

.secret-box {
  display: block;
  font-family: var(--font-mono);
  font-size: 0.92rem;
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  padding: 12px 14px;
  border-radius: var(--r-sm);
  word-break: break-all;
  user-select: all;
  color: var(--text);
}
.recovery-codes {
  font-family: var(--font-mono);
  font-size: 0.95rem;
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  padding: 14px;
  white-space: pre;
  user-select: all;
  line-height: 1.7;
  letter-spacing: 0.04em;
  color: var(--accent);
}

/* ---- Animations --------------------------------------------------- */

@keyframes fadeInUp {
  from { opacity: 0; transform: translateY(12px); }
  to   { opacity: 1; transform: translateY(0); }
}

@keyframes pulseDot {
  0%, 100% { opacity: 1; }
  50%      { opacity: 0.4; }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0ms !important;
  }
}

/* ---- Stats page ---------------------------------------------------- */

.stats-page { display: flex; flex-direction: column; gap: 18px; }

/* Window selector */
.window-selector {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  padding: 4px;
}
.window-btn {
  appearance: none;
  background: none;
  border: 1px solid transparent;
  color: var(--text-dim);
  font-family: var(--font-mono);
  font-size: 0.82rem;
  font-weight: 500;
  padding: 6px 12px;
  border-radius: var(--r-sm);
  cursor: pointer;
  transition: color var(--t-fast), background var(--t-fast);
}
.window-btn:hover { color: var(--text); background: var(--surface-hover); }
.window-btn.active {
  color: var(--accent);
  background: var(--accent-faint);
  border-color: var(--border-accent);
}
.window-divider {
  width: 1px; height: 18px; background: var(--border-strong); margin: 0 8px;
}

.window-selector-wrap { display: flex; flex-direction: column; align-items: flex-end; gap: 8px; }

.custom-range {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  padding: 10px 12px;
  font-size: 0.85rem;
  animation: fadeInUp 240ms cubic-bezier(0.16, 1, 0.3, 1);
}
.custom-range[hidden] { display: none; }
.custom-range label { display: inline-flex; align-items: center; gap: 6px; margin: 0; text-transform: none; letter-spacing: 0; color: var(--text-dim); font-weight: 500; font-size: 0.8rem; }
.custom-range input[type="datetime-local"] {
  font-family: var(--font-mono);
  font-size: 0.85rem;
  padding: 6px 8px;
  background: var(--bg-elevated);
  border: 1px solid var(--border-strong);
  border-radius: var(--r-sm);
  color: var(--text);
  color-scheme: dark;  /* makes the calendar picker chrome match the dark theme */
}
.custom-range input[type="datetime-local"]:focus { outline: none; border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-faint); }
.custom-range .btn { padding: 6px 12px; font-size: 0.85rem; }

/* Scope selector — pill buttons for site-wide vs per-sorter */
.scope-selector {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  padding: 4px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
}
.scope-btn {
  appearance: none;
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  color: var(--text-dim);
  font-family: inherit;
  font-size: 0.85rem;
  font-weight: 500;
  padding: 8px 14px;
  border-radius: var(--r-sm);
  cursor: pointer;
  transition: all var(--t-fast);
  display: inline-flex;
  align-items: center;
  gap: 8px;
}
.scope-btn:hover { color: var(--text); border-color: var(--border-strong); background: var(--surface-hover); }
.scope-btn.active {
  color: var(--accent);
  background: var(--accent-faint);
  border-color: var(--border-accent);
  box-shadow: 0 0 0 1px var(--border-accent);
}
.scope-btn .scope-icon { font-size: 0.85rem; opacity: 0.7; }
.scope-btn .scope-name { font-weight: 600; }
.scope-btn .scope-key { font-size: 0.72rem; color: var(--text-faint); margin-left: 2px; }
.scope-btn.active .scope-key { color: var(--accent); opacity: 0.7; }

/* KPI strip */
.kpi-strip {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(170px, 1fr));
  gap: 12px;
}
/* Accent card — used for the headline metric (Total in window) */
.kpi-card-accent { background: linear-gradient(135deg, var(--surface) 0%, rgba(7, 187, 241, 0.08) 100%); }
.kpi-card-accent::before { width: 4px; opacity: 1; }
.kpi-card-accent .kpi-value { color: var(--accent); }

/* PEAK CAPACITY — the hero (2026-08-30). Chris: the strip was "very plain".
   It was: every card carried the same accent rail at the same weight, so
   nothing led and the eye had nowhere to land. The rail on ordinary cards is
   now quiet (see .kpi-card::before below) and the two cards that deserve
   emphasis carry it — this one loudest, because the best sustained rate the
   machine has achieved is the number worth reading across a room. */
.kpi-card-peak {
  /* NOT span 2. This card leads on colour - accent value, accent label,
     cyan wash, 4px rail - and the extra column was what wrapped the
     9-card stats strip onto a second row (Chris, 2026-08-30). */
  background:
    radial-gradient(ellipse at top right, rgba(7, 187, 241, 0.18), transparent 60%),
    linear-gradient(135deg, var(--surface) 0%, rgba(7, 187, 241, 0.06) 100%);
  border-color: rgba(7, 187, 241, 0.30);
}
.kpi-card-peak::before { width: 4px; opacity: 1; }
.kpi-card-peak .kpi-value {
  color: var(--accent);
  font-size: 2.15rem;
  letter-spacing: -0.02em;
}
.kpi-card-peak .kpi-label { color: var(--accent); opacity: 0.85; }
/* Empty state must never look like a reading. */
.kpi-card-peak.is-empty {
  background: var(--surface);
  border-color: var(--border);
}
.kpi-card-peak.is-empty .kpi-value,
.kpi-card-peak.is-empty .kpi-label { color: var(--text-faint); opacity: 1; }
.kpi-card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  padding: 16px 20px;
  display: flex;
  flex-direction: column;
  gap: 4px;
  position: relative;
  overflow: hidden;
  transition: border-color var(--t-fast), transform var(--t-fast);
}
.kpi-card:hover { border-color: var(--border-strong); transform: translateY(-1px); }
.kpi-card::before {
  content: "";
  position: absolute;
  inset: 0 auto 0 0;
  width: 3px;
  background: var(--accent);
  /* Quiet by default so the emphasised cards (peak, total-in-window) can
     actually lead. At 0.6 on every card the strip had no hierarchy at all. */
  opacity: 0.22;
}
.kpi-label {
  font-size: 0.7rem;
  color: var(--text-faint);
  font-weight: 500;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}
.kpi-value {
  font-size: 1.9rem;
  font-weight: 600;
  color: var(--text);
  letter-spacing: -0.02em;
  line-height: 1;
}
.kpi-value.ok { color: var(--ok); }
.kpi-value.warn { color: var(--warn); }
.kpi-value.err { color: var(--err); }
.kpi-unit { font-size: 0.95rem; font-weight: 500; color: var(--text-dim); margin-left: 2px; }
.kpi-sub { font-size: 0.75rem; }

/* Generic panel */
.panel {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  overflow: hidden;
}
.panel-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 14px 20px;
  border-bottom: 1px solid var(--border);
  background: var(--bg-elevated);
}
.panel-title { font-size: 0.95rem; font-weight: 600; letter-spacing: -0.005em; color: var(--text); }
.panel-body { padding: 18px 20px; }

/* ---- Collapsible panel modifier ----------------------------------
   Opt-in via `.panel-collapsible` on the .panel element. The header
   becomes a click target (also keyboard-accessible via tabindex +
   Enter/Space handled in JS) and a small chevron rotates to mirror
   the expanded / collapsed state. `.collapsed` is the closed state;
   omit it (or remove via JS) to start expanded.

   Why CSS-not-<details>: the existing panel layout uses a sibling
   .panel-header + .panel-body pair with their own padding + border
   rules. Wrapping in <details>/<summary> would force a rewrite of
   every other panel's CSS. The class-toggle approach is two lines
   and zero collateral damage. */
.panel-collapsible .panel-header {
  cursor: pointer;
  user-select: none;
}
.panel-collapsible .panel-header:hover { background: var(--surface-hover); }
.panel-collapsible .panel-header:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: -2px;
}
.panel-collapse-chevron {
  display: inline-block;
  margin-left: 8px;
  font-size: 0.9rem;
  color: var(--text-dim);
  transition: transform 140ms ease;
}
.panel-collapsible.collapsed .panel-collapse-chevron {
  transform: rotate(-90deg);
}
.panel-collapsible.collapsed .panel-header {
  border-bottom-color: transparent;
}
.panel-collapsible.collapsed .panel-body {
  display: none;
}

.chart-wrap { height: 280px; position: relative; }

.grid-2col { display: grid; grid-template-columns: 1fr 1fr; gap: 18px; }
@media (max-width: 980px) { .grid-2col { grid-template-columns: 1fr; } }

/* Quality breakdown — donut + custom legend */
.quality-doughnut-wrap {
  display: grid;
  grid-template-columns: 200px 1fr;
  gap: 24px;
  align-items: center;
}
@media (max-width: 720px) {
  .quality-doughnut-wrap { grid-template-columns: 1fr; }
}
.quality-chart-box {
  position: relative;
  width: 200px;
  height: 200px;
}
.quality-chart-box canvas { width: 100% !important; height: 100% !important; }
.quality-chart-center {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  pointer-events: none;
  text-align: center;
}
.quality-center-value {
  font-size: 1.7rem;
  font-weight: 700;
  color: var(--ok);
  letter-spacing: -0.01em;
  line-height: 1;
}
.quality-center-label {
  font-size: 0.7rem;
  color: var(--text-faint);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  margin-top: 4px;
}
.quality-legend {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.quality-legend-row {
  display: grid;
  grid-template-columns: 14px 1fr auto auto;
  align-items: center;
  gap: 10px;
  padding: 6px 10px;
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
}
.quality-legend-dot {
  width: 12px;
  height: 12px;
  border-radius: 50%;
  flex-shrink: 0;
  box-shadow: 0 0 8px rgba(255,255,255,0.04);
}
.quality-legend-name { font-size: 0.92rem; color: var(--text); }
.quality-legend-pct { font-size: 0.95rem; font-weight: 600; color: var(--text); }
.quality-legend-count { font-size: 0.82rem; }

/* Old quality bars — kept for any legacy markup; the donut is the
   default but these rules don't hurt anything if a partial cache
   serves an old template. */
.quality-bars { display: flex; flex-direction: column; gap: 12px; }
.quality-row {
  display: grid;
  grid-template-columns: 130px 1fr 70px 70px;
  align-items: center;
  gap: 12px;
  font-size: 0.92rem;
}
.quality-label { color: var(--text-dim); }
.quality-bar {
  height: 8px;
  background: var(--bg-elevated);
  border-radius: 999px;
  overflow: hidden;
  position: relative;
}
.quality-bar-fill {
  height: 100%;
  border-radius: 999px;
  transition: width var(--t-slow);
}
.quality-bar-ok   { background: linear-gradient(90deg, var(--ok), #56e6aa); box-shadow: 0 0 12px rgba(61,220,151,0.4); }
.quality-bar-warn { background: linear-gradient(90deg, var(--warn), #ffe17a); box-shadow: 0 0 12px rgba(255,210,63,0.4); }
.quality-bar-err  { background: linear-gradient(90deg, var(--err), #ff7a92); box-shadow: 0 0 12px rgba(255,77,109,0.4); }
.quality-bar-dim  { background: var(--text-faint); }
.quality-pct { color: var(--text); font-weight: 600; text-align: right; }
.quality-count { color: var(--text-dim); text-align: right; font-size: 0.85rem; }

/* Top chutes */
.top-chutes { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 8px; }
.chute-row {
  display: grid;
  /* Auto-width on the label column so "Sorter1 #13" doesn't wrap; bar
     stretches with the row; count fixed-width on the right. */
  grid-template-columns: minmax(110px, max-content) 1fr 60px;
  align-items: center;
  gap: 12px;
}
.chute-num {
  color: var(--accent);
  font-weight: 600;
  white-space: nowrap;
  display: inline-flex;
  align-items: center;
  gap: 6px;
}
/* Small sorter chip in front of the chute number on the site-wide view. */
.chute-sorter {
  display: inline-block;
  padding: 1px 7px;
  border-radius: 999px;
  background: var(--surface);
  color: var(--text-dim);
  border: 1px solid var(--border);
  font-size: 0.72rem;
  font-weight: 500;
  letter-spacing: 0.02em;
}
.chute-hash { color: var(--accent); }
.chute-bar { height: 6px; background: var(--bg-elevated); border-radius: 999px; overflow: hidden; }
.chute-bar-fill {
  height: 100%;
  background: linear-gradient(90deg, var(--accent), #ffaf6e);
  box-shadow: 0 0 10px var(--accent-glow);
  transition: width var(--t-slow);
}
.chute-count { text-align: right; color: var(--text); font-weight: 600; }

/* Per-sorter cards */
.sorter-grid {
  /* Five sorters must sit on one row. Two things were wrapping: this
     320px track (which seats four on a laptop) and the five stat blocks
     inside each card being laid into a four-column grid, which made every
     card two rows taller than it needed to be. Both fixed - Chris,
     2026-08-30: "just make the cards smaller so they dont spill into
     second row". auto-fill keeps it responsive; it simply seats more. */
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(236px, 1fr));
  gap: 12px;
}
.sorter-card { padding: 13px; gap: 9px; }
/* Five metrics, five columns - Uptime was falling to its own line. */
.sorter-stats { grid-template-columns: repeat(5, minmax(0, 1fr)); gap: 7px; }
.sorter-card .stat-value { font-size: 15px; margin-top: 1px; }
.sorter-card .stat-label { font-size: 9.5px; letter-spacing: 0.4px; }
.sorter-card .sorter-name { font-size: 0.9rem; }
.sorter-card {
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  padding: 16px;
  display: flex;
  flex-direction: column;
  gap: 12px;
  transition: border-color var(--t-fast), transform var(--t-fast);
}
.sorter-card:hover { border-color: var(--border-strong); transform: translateY(-1px); }
.sorter-card-head { display: flex; align-items: flex-start; gap: 10px; }
.stack-tight { display: flex; flex-direction: column; gap: 2px; min-width: 0; flex: 1; }
.sorter-name { font-size: 1rem; font-weight: 600; color: var(--text); }

.status-dot {
  width: 10px; height: 10px; border-radius: 50%;
  background: var(--text-faint);
  flex-shrink: 0;
  margin-top: 4px;
}
.status-dot.online {
  background: var(--ok);
  box-shadow: 0 0 12px var(--ok);
  animation: pulseDot 2s ease-in-out infinite;
}
.status-dot.offline { background: var(--text-faint); }

.sorter-stats {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 10px;
}
.stat-block { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.stat-label { font-size: 0.65rem; color: var(--text-faint); text-transform: uppercase; letter-spacing: 0.06em; }
.stat-value { font-size: 1.05rem; font-weight: 600; color: var(--text); }
.stat-value.ok   { color: var(--ok); }
.stat-value.warn { color: var(--warn); }
.stat-value.err  { color: var(--err); }

.sorter-quality { display: flex; flex-wrap: wrap; gap: 6px; }
.pill.small { padding: 1px 8px; font-size: 0.7rem; }

.sorter-spark { height: 40px; }
.sparkline { width: 100%; height: 40px; display: block; }
.sparkline path { stroke-width: 1.4; stroke-linejoin: round; stroke-linecap: round; }

/* KPI delta — small chip below value. Good = green, bad = red, neutral = grey.
   Inverted-semantic metrics (e.g. no-read rate) flip green/red so "down is
   good" reads correctly. */
.kpi-sub { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.kpi-delta {
  display: inline-flex;
  align-items: center;
  gap: 2px;
  font-family: var(--font-mono);
  font-size: 0.7rem;
  font-weight: 600;
  padding: 1px 6px;
  border-radius: 999px;
  letter-spacing: 0.02em;
  white-space: nowrap;
}
.kpi-delta.good    { background: var(--ok-faint);   color: var(--ok); }
.kpi-delta.bad     { background: var(--err-faint);  color: var(--err); }
.kpi-delta.neutral { background: rgba(255,255,255,0.04); color: var(--text-faint); }

/* Clickable KPI card (no-read drilldown) — subtle cursor + hover hint */
.kpi-card-clickable { cursor: pointer; }
.kpi-card-clickable:hover { border-color: var(--border-strong); }
.kpi-card-clickable:focus-visible {
  outline: none; border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-faint);
}

/* Throughput heatmap (last 7 days × 30-min cells by default).
   --heatmap-cols set by stats.js based on bucket size; defaults to 48
   so the rule is correct even if JS hasn't run yet. */
.heatmap {
  display: grid;
  grid-template-columns: 90px repeat(var(--heatmap-cols, 48), 1fr);
  gap: 2px;
  font-family: var(--font-mono);
  font-size: 0.7rem;
  align-items: center;
}
.heatmap-corner { /* empty top-left */ }
.heatmap-col-label {
  color: var(--text-faint);
  text-align: center;
  font-size: 0.65rem;
  padding-bottom: 2px;
}
.heatmap-row-label {
  color: var(--text-dim);
  text-align: right;
  padding-right: 8px;
  font-size: 0.7rem;
}
.heatmap-cell {
  display: block;
  height: 16px;
  border-radius: 2px;
  background: var(--bg-elevated);
  cursor: pointer;
  transition: transform var(--t-fast);
}
.heatmap-cell:hover {
  transform: scale(1.6);
  z-index: 2;
  position: relative;
  box-shadow: 0 0 0 1px var(--border-strong);
}

/* Click-drilldown popover */
.heatmap-popover {
  position: absolute;
  z-index: 50;
  min-width: 240px;
  max-width: 300px;
  padding: 10px 12px;
  background: var(--bg-elevated);
  border: 1px solid var(--border-strong);
  border-radius: 8px;
  box-shadow: 0 12px 32px rgba(0, 0, 0, 0.45);
  color: var(--text);
  font-family: var(--font-ui);
  font-size: 12.5px;
}
.heatmap-popover .hp-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 4px;
}
.heatmap-popover .hp-when {
  font-weight: 600;
  font-size: 12.5px;
  color: var(--text);
}
.heatmap-popover .hp-close {
  background: transparent;
  border: 0;
  color: var(--text-dim);
  font-size: 16px;
  line-height: 1;
  cursor: pointer;
  padding: 0 4px;
  border-radius: 4px;
}
.heatmap-popover .hp-close:hover { color: var(--text); background: var(--surface-hover); }
.heatmap-popover .hp-scope {
  color: var(--text-dim);
  font-size: 10.5px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  margin-bottom: 8px;
}
.heatmap-popover .hp-grid {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 4px 12px;
  margin: 0;
}
.heatmap-popover .hp-grid dt {
  color: var(--text-dim);
  font-size: 11.5px;
}
.heatmap-popover .hp-grid dd {
  margin: 0;
  font-variant-numeric: tabular-nums;
}
/* 5 levels of orange — l0 = no data, l4 = peak. Mixed with the brand
   accent so the heatmap visually ties to the rest of the page. */
.heatmap-l0 { background: var(--bg-elevated); }
.heatmap-l1 { background: rgba(7, 187, 241, 0.18); }
.heatmap-l2 { background: rgba(7, 187, 241, 0.38); }
.heatmap-l3 { background: rgba(7, 187, 241, 0.62); }
.heatmap-l4 { background: rgba(7, 187, 241, 0.92); box-shadow: 0 0 8px var(--accent-glow); }

.heatmap-legend {
  display: flex;
  align-items: center;
  gap: 6px;
  margin-top: 12px;
  font-size: 0.78rem;
  color: var(--text-dim);
}
.heatmap-legend .heatmap-cell {
  width: 14px;
  height: 14px;
}
.heatmap-legend .small { font-size: 0.7rem; }

/* Modal — used by the no-read drilldown */
.modal {
  position: fixed; inset: 0; z-index: 300;
  display: flex; align-items: center; justify-content: center;
  animation: fadeInUp 200ms ease-out;
}
.modal[hidden] { display: none; }
.modal-backdrop {
  position: absolute; inset: 0;
  background: rgba(0, 0, 0, 0.72);
  backdrop-filter: blur(4px);
  -webkit-backdrop-filter: blur(4px);
  cursor: pointer;
}
.modal-panel {
  position: relative;
  z-index: 1;
  width: min(800px, 92vw);
  max-height: 88vh;
  background: var(--surface);
  border: 1px solid var(--border-strong);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-card);
  display: flex;
  flex-direction: column;
}
.modal-header {
  display: flex; align-items: center; justify-content: space-between;
  padding: 16px 20px;
  border-bottom: 1px solid var(--border);
}
.modal-header h2 { font-size: 1.1rem; }
.modal-sub { padding: 8px 20px; font-size: 0.85rem; }
.modal-body { padding: 16px 20px; overflow-y: auto; }
.modal-close {
  appearance: none;
  background: none;
  border: none;
  color: var(--text-dim);
  font-size: 1.6rem;
  line-height: 1;
  padding: 4px 10px;
  cursor: pointer;
  border-radius: var(--r-sm);
  transition: color var(--t-fast), background var(--t-fast);
}
.modal-close:hover { color: var(--text); background: var(--surface-hover); }

/* No-read row inside the drilldown modal */
.noread-list { display: flex; flex-direction: column; gap: 10px; }
.noread-row {
  display: grid;
  grid-template-columns: 96px 1fr;
  gap: 14px;
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  padding: 10px;
  transition: border-color var(--t-fast);
}
.noread-row:hover { border-color: var(--border-strong); }
.noread-thumb {
  width: 96px; height: 72px; object-fit: cover;
  border-radius: var(--r-sm);
  background: var(--bg-elevated-2);
  border: 1px solid var(--border);
}
.noread-thumb-empty {
  display: flex; align-items: center; justify-content: center;
  color: var(--text-faint);
  font-size: 0.78rem;
  font-family: var(--font-mono);
}
.noread-meta { display: flex; flex-direction: column; gap: 4px; min-width: 0; }
.noread-meta-top { display: flex; gap: 12px; align-items: baseline; }
.noread-meta-mid { display: flex; gap: 6px; align-items: baseline; flex-wrap: wrap; font-size: 0.88rem; }
.noread-meta-bot { font-size: 0.78rem; }

/* ---- PM-builder modal -------------------------------------------- */

/* Wider panel — the checklist builder needs more horizontal room than
   the no-read drilldown. */
.modal-panel.pm-builder-panel {
  width: min(960px, 94vw);
  max-height: 92vh;
}

.pm-mode-tiles {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 12px;
}
.pm-mode-tile {
  appearance: none;
  text-align: left;
  font: inherit;
  color: var(--text);
  background: var(--bg-elevated, var(--surface));
  border: 2px solid var(--border);
  border-radius: var(--r-md);
  padding: 14px 14px 12px;
  cursor: pointer;
  display: flex; flex-direction: column; gap: 4px;
  transition: border-color var(--t-fast), background var(--t-fast),
              transform var(--t-fast);
}
.pm-mode-tile:hover {
  border-color: var(--border-strong);
  background: var(--surface-hover, var(--bg-elevated));
}
.pm-mode-tile.is-active {
  border-color: var(--accent);
  background: var(--accent-faint, rgba(72, 144, 226, 0.08));
}
.pm-mode-icon {
  font-size: 1.6rem;
  line-height: 1;
}
.pm-mode-title {
  font-weight: 600;
  font-size: 1rem;
}
.pm-mode-desc {
  font-size: 0.82rem;
  color: var(--text-dim);
  line-height: 1.3;
}
@media (max-width: 720px) {
  .pm-mode-tiles { grid-template-columns: 1fr; }
}

.pm-builder-section {
  padding: 14px 0;
  border-top: 1px solid var(--border);
}
.pm-builder-section:first-of-type { border-top: none; padding-top: 12px; }
.pm-section-label {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  align-items: baseline;
  margin-bottom: 10px;
}
.pm-section-label strong { font-size: 0.95rem; }

.pm-builder-footer {
  display: flex;
  gap: 8px;
  align-items: center;
  border-top: 1px solid var(--border);
  padding-top: 12px;
  margin-top: 16px;
}

/* Checklist builder rows ------------------------------------------ */

.pm-items-list {
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.pm-item-row {
  display: grid;
  grid-template-columns: 22px 110px 1fr 28px;
  gap: 8px;
  align-items: start;
  padding: 10px;
  background: var(--bg-elevated, var(--surface));
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  transition: border-color var(--t-fast);
}
.pm-item-row:hover { border-color: var(--border-strong); }
.pm-item-row.is-dragging { opacity: 0.5; }
.pm-item-handle {
  cursor: grab;
  color: var(--text-faint);
  font-size: 1.1rem;
  user-select: none;
  display: flex;
  align-items: center;
  justify-content: center;
  height: 32px;
}
.pm-item-handle:active { cursor: grabbing; }
.pm-item-type-pill {
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.pm-item-type-pill select {
  width: 100%;
  font-size: 0.82rem;
}
.pm-item-body {
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.pm-item-body input[type="text"],
.pm-item-body input[type="number"],
.pm-item-body select {
  width: 100%;
}
.pm-item-meta {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
  align-items: center;
  font-size: 0.78rem;
}
.pm-item-meta label {
  display: flex; gap: 4px; align-items: center;
  color: var(--text-dim);
}
.pm-item-meta-range {
  display: flex;
  gap: 4px;
  align-items: center;
  flex-wrap: wrap;
}
.pm-item-meta-range input[type="number"] {
  width: 78px;
  font-size: 0.78rem;
}
.pm-item-meta-range input[type="text"] {
  width: 70px;
  font-size: 0.78rem;
}
.pm-item-remove {
  appearance: none;
  background: none;
  border: none;
  color: var(--text-faint);
  cursor: pointer;
  font-size: 1.1rem;
  padding: 4px 6px;
  border-radius: var(--r-sm);
}
.pm-item-remove:hover {
  color: var(--bad, #c0392b);
  background: var(--surface-hover);
}

/* Item type pill colours — picked to match the SCADA palette so the
   colour language is consistent across the app. */
.pm-item-type-visual      select { border-left: 3px solid #4f7eff; }
.pm-item-type-reading     select { border-left: 3px solid #16a085; }
.pm-item-type-measurement select { border-left: 3px solid #2c8eb5; }
.pm-item-type-lube        select { border-left: 3px solid #c39b2c; }
.pm-item-type-torque      select { border-left: 3px solid #b15030; }
.pm-item-type-note        select { border-left: 3px solid #888; }

.pm-extract-result {
  margin-top: 8px;
}

/* Drag-and-drop file zone for the From-manual mode */
.pm-dropzone {
  border: 2px dashed var(--border-strong);
  border-radius: var(--r-md);
  background: var(--bg-elevated, var(--surface));
  padding: 22px 16px;
  text-align: center;
  cursor: pointer;
  transition: border-color var(--t-fast), background var(--t-fast);
}
.pm-dropzone:hover {
  border-color: var(--accent);
  background: var(--surface-hover, var(--bg-elevated));
}
.pm-dropzone.is-dragover {
  border-color: var(--accent);
  background: var(--accent-faint, rgba(72, 144, 226, 0.12));
}
.pm-dropzone.is-uploading {
  opacity: 0.7;
  cursor: progress;
}
.pm-dropzone-icon {
  font-size: 2rem;
  line-height: 1;
  margin-bottom: 4px;
}
.pm-dropzone-text {
  font-size: 0.92rem;
  color: var(--text);
  margin-bottom: 4px;
}
.pm-dropzone-browse {
  appearance: none;
  background: none;
  border: none;
  color: var(--accent);
  cursor: pointer;
  padding: 0;
  font: inherit;
  text-decoration: underline;
}
.pm-dropzone-browse:hover { color: var(--accent-hover, var(--accent)); }

/* Subtle divider between the dropzone and the existing-doc picker */
.pm-extract-or {
  display: flex;
  align-items: center;
  gap: 10px;
  margin: 14px 0;
  color: var(--text-dim);
  font-size: 0.82rem;
}
.pm-extract-or::before,
.pm-extract-or::after {
  content: "";
  flex: 1;
  height: 1px;
  background: var(--border);
}

/* PM-list item count badge on the asset page */
.pm-row {
  display: flex;
  gap: 10px;
  align-items: center;
  padding: 10px 12px;
  background: var(--bg-elevated, var(--surface));
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  margin-bottom: 6px;
}
.pm-row-summary { flex: 1; min-width: 0; }
.pm-row-name { font-weight: 600; }
.pm-row-count {
  display: inline-block;
  padding: 2px 8px;
  font-size: 0.75rem;
  background: var(--surface-hover);
  color: var(--text-dim);
  border-radius: 999px;
  margin-left: 6px;
}
.pm-row-items {
  margin-top: 8px;
  padding-top: 8px;
  border-top: 1px dashed var(--border);
}
.pm-row-item {
  /* Migration 0047 — added inline checkbox + photo controls.
     Columns: check, icon, text (flex), spec, photo-btn, "by" badge. */
  display: grid;
  grid-template-columns: 26px 24px 1fr auto auto auto;
  gap: 8px;
  padding: 6px 0;
  font-size: 0.85rem;
  align-items: center;
  border-radius: 4px;
}
.pm-item-check {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
}
.pm-item-check input[type="checkbox"] {
  width: 18px;
  height: 18px;
  accent-color: #2cc28b;
  cursor: pointer;
}
.pm-item-done {
  background: rgba(44, 194, 139, 0.07);
}
.pm-item-done .pm-item-text {
  text-decoration: line-through;
  color: var(--text-dim);
}
.pm-item-by {
  white-space: nowrap;
  color: #2cc28b !important;
  font-size: 0.78rem;
}
.pm-photo-badge {
  background: var(--card-bg, #14161a);
  border: 1px solid var(--card-border, #262830);
  color: inherit;
  padding: 3px 8px;
  border-radius: 4px;
  font-size: 0.78rem;
  cursor: pointer;
  white-space: nowrap;
}
.pm-photo-badge:hover {
  background: #1c2027;
  border-color: #3a3e48;
}
.pm-photo-badge-required {
  border-color: rgba(255, 145, 0, 0.5);
  color: #ffae54;
  background: rgba(255, 145, 0, 0.08);
}
.pm-progress-pill {
  display: inline-block;
  padding: 1px 8px;
  margin-left: 8px;
  border-radius: 10px;
  background: rgba(44, 194, 139, 0.15);
  color: #2cc28b;
  font-size: 0.72rem;
  font-family: var(--font-mono);
}
.pm-row-cycle-bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-top: 10px;
  padding-top: 8px;
  border-top: 1px dashed var(--border);
}
.pm-complete-cycle-btn[disabled] {
  opacity: 0.5;
  cursor: not-allowed;
}
.pm-row-item-icon {
  width: 22px; height: 22px;
  display: flex; align-items: center; justify-content: center;
  font-size: 0.7rem;
  font-weight: 600;
  border-radius: 4px;
  color: #fff;
  text-transform: uppercase;
}
.pm-row-item-icon-visual      { background: #4f7eff; }
.pm-row-item-icon-reading     { background: #16a085; }
.pm-row-item-icon-measurement { background: #2c8eb5; }
.pm-row-item-icon-lube        { background: #c39b2c; }
.pm-row-item-icon-torque      { background: #b15030; }
.pm-row-item-icon-note        { background: #888; }
.pm-row-item-spec {
  color: var(--text-dim);
  font-size: 0.78rem;
  font-family: var(--font-mono);
}

/* CSV download button — anchor styled like a window-btn */
a#csv-export-btn {
  text-decoration: none;
}
a#csv-export-btn:hover {
  color: var(--accent);
  background: var(--accent-faint);
}

/* ---- Reports page ------------------------------------------------- */

.reports-page { display: flex; flex-direction: column; gap: 18px; }

/* Period selector — tight enough to fit on one line at typical viewport
   widths. Kept wrap-friendly via flex-wrap so it still degrades gracefully
   on narrow screens. */
.period-selector-wrap { display: flex; flex-direction: column; align-items: flex-end; gap: 8px; }
.period-selector {
  display: inline-flex; align-items: center; gap: 2px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  padding: 3px;
  flex-wrap: wrap;
  max-width: 100%;
}
.period-btn {
  appearance: none;
  background: none;
  border: 1px solid transparent;
  color: var(--text-dim);
  font-family: inherit;
  font-size: 0.78rem;
  font-weight: 500;
  padding: 5px 9px;
  border-radius: var(--r-sm);
  cursor: pointer;
  transition: color var(--t-fast), background var(--t-fast);
  text-decoration: none;
  white-space: nowrap;
  line-height: 1.3;
}
.period-btn:hover { color: var(--text); background: var(--surface-hover); }
.period-btn.active {
  color: var(--accent);
  background: var(--accent-faint);
  border-color: var(--border-accent);
}

/* KPI strip — bigger variant for reports headline */
/* Nine cards need to sit on one row on a laptop. Scoped to the strips that
   actually carry that many - every other kpi-strip in the app has 2-5 and
   looks better at the roomier default. */
.kpi-strip-compact { grid-template-columns: repeat(auto-fit, minmax(146px, 1fr)); gap: 10px; }
.kpi-strip-compact .kpi-card { padding: 12px 14px; gap: 3px; }
.kpi-strip-compact .kpi-value { font-size: 1.5rem; }
.kpi-strip-compact .kpi-card-peak .kpi-value { font-size: 1.5rem; }
.kpi-strip-compact .kpi-label { font-size: 0.64rem; letter-spacing: 0.07em; }
.kpi-strip-compact .kpi-unit { font-size: 0.78rem; }
.kpi-strip-compact .kpi-sub { font-size: 0.68rem; gap: 6px; }

.kpi-strip-large .kpi-card { padding: 22px 24px; }
.kpi-strip-large .kpi-value { font-size: 2.4rem; }
.kpi-strip-large .kpi-card-accent .kpi-value { font-size: 2.6rem; }

/* Insights panel */
.insights-panel { border-color: var(--border-accent); }
.insights-panel .panel-header {
  background: linear-gradient(90deg, var(--bg-elevated) 0%, var(--accent-faint) 100%);
}
.insights-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.insight {
  padding: 10px 14px;
  border-radius: var(--r-sm);
  font-size: 0.95rem;
  border-left: 3px solid var(--text-faint);
  background: var(--bg-elevated);
  transition: transform var(--t-fast);
}
.insight:hover { transform: translateX(2px); }
.insight-good    { border-left-color: var(--ok);     background: linear-gradient(90deg, var(--ok-faint), transparent 70%); }
.insight-bad     { border-left-color: var(--err);    background: linear-gradient(90deg, var(--err-faint), transparent 70%); }
.insight-neutral { border-left-color: var(--text-faint); }
.insight-fact    { border-left-color: var(--info);   background: linear-gradient(90deg, rgba(108,182,255,0.10), transparent 70%); }

/* Best-day / best-hour callout strip */
.callout-strip {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 12px;
}
@media (max-width: 880px) {
  .callout-strip { grid-template-columns: 1fr; }
}
.callout {
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding: 18px 22px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  position: relative;
  overflow: hidden;
}
.callout::before {
  content: "";
  position: absolute;
  inset: auto auto -40% -20%;
  width: 200px; height: 200px;
  background: radial-gradient(circle, var(--accent-faint), transparent 70%);
  pointer-events: none;
}
.callout-label {
  font-size: 0.7rem; color: var(--text-faint);
  font-weight: 500; letter-spacing: 0.08em; text-transform: uppercase;
  position: relative; z-index: 1;
}
.callout-value { font-size: 1.5rem; font-weight: 600; color: var(--text); position: relative; z-index: 1; }
.callout-sub   { font-size: 0.85rem; color: var(--text-dim); position: relative; z-index: 1; }

/* Daily chart — slightly taller than other panels but not so tall it
   dominates. 240px keeps a 30-day bar series readable without stretching
   each bar across half the page. */
.chart-wrap-tall { height: 240px; }

/* Sorter leaderboard */
.leaderboard-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 18px;
}
@media (max-width: 1100px) {
  .leaderboard-grid { grid-template-columns: 1fr; }
}
.leaderboard-cat {
  font-size: 0.85rem;
  color: var(--text-dim);
  font-weight: 500;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  margin-bottom: 12px;
  padding-bottom: 8px;
  border-bottom: 1px solid var(--border);
}
.leaderboard-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.lb-row {
  display: grid;
  grid-template-columns: 36px 1fr 1fr auto;
  align-items: center;
  gap: 10px;
  padding: 10px 12px;
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  transition: border-color var(--t-fast), transform var(--t-fast);
}
.lb-row:hover { border-color: var(--border-strong); transform: translateX(2px); }
.lb-rank-1 { border-color: rgba(255,210,63,0.4); background: linear-gradient(90deg, rgba(255,210,63,0.10), var(--bg-elevated) 60%); }
.lb-rank-2 { border-color: rgba(192,192,192,0.3); background: linear-gradient(90deg, rgba(192,192,192,0.06), var(--bg-elevated) 60%); }
.lb-rank-3 { border-color: rgba(205,127,50,0.3);  background: linear-gradient(90deg, rgba(205,127,50,0.08), var(--bg-elevated) 60%); }
.lb-medal { font-size: 1.4rem; line-height: 1; text-align: center; }
.lb-meta { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.lb-name { font-size: 0.92rem; font-weight: 600; color: var(--text); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.lb-key  { font-size: 0.72rem; }
.lb-bar  { height: 6px; background: var(--bg-elevated-2); border-radius: 999px; overflow: hidden; }
.lb-bar-fill {
  height: 100%;
  background: linear-gradient(90deg, var(--accent), #ffaf6e);
  box-shadow: 0 0 8px var(--accent-glow);
  transition: width var(--t-slow);
}
.lb-rank-1 .lb-bar-fill { background: linear-gradient(90deg, #ffd23f, #ffe57a); box-shadow: 0 0 8px rgba(255,210,63,0.5); }
.lb-value { font-size: 0.95rem; font-weight: 600; color: var(--text); }
.lb-sub   { display: block; font-size: 0.7rem; }
/* 0-value rows (idle sorters in the volume board) — dimmed so they read
   as "present but inactive" without dropping off the leaderboard entirely. */
.lb-row-empty { opacity: 0.55; }
.lb-row-empty .lb-bar-fill { background: var(--text-faint); box-shadow: none; }

/* Shift breakdown */
.shift-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 14px;
}
@media (max-width: 980px) {
  .shift-grid { grid-template-columns: 1fr; }
}
.shift-card {
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  padding: 16px;
  display: flex;
  flex-direction: column;
  gap: 14px;
}
.shift-card-head { display: flex; align-items: center; gap: 10px; }
.shift-icon { font-size: 1.4rem; line-height: 1; }
.shift-label { font-size: 0.95rem; font-weight: 600; color: var(--text); }
.shift-stats {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 10px;
}

/* Year heatmap (GitHub-style 53-week × 7-day grid) */
/* 365-day heatmap on /reports — PORTRAIT layout that fills its
   panel completely. 7 columns × ~53 rows; no internal scroll.
   To get 53 rows into a panel height of ~660 px, cells become
   short-rectangular (12 px tall) rather than square. The width
   stretches to fill — each cell is ~80 px wide × 12 px tall in a
   half-width panel, which reads as "weeks as horizontal bars".
   Width-stretching is the right trade-off for "show me the whole
   year at once without scrolling". */
.year-heatmap {
  width: 100%;
  padding: 4px 0 8px 0;
  display: flex;
  flex-direction: column;
}
.yh-dowrow,
.yh-row {
  display: grid;
  grid-template-columns: 36px repeat(7, minmax(0, 1fr));
  gap: 2px;
  align-items: center;
  width: 100%;
}
.yh-dowrow {
  margin-bottom: 6px;
  padding-bottom: 4px;
  border-bottom: 1px solid var(--border);
}
.yh-body {
  display: flex;
  flex-direction: column;
  gap: 2px;
  flex: 1 1 auto;
}
.yh-dow {
  font-size: 0.7rem;
  letter-spacing: 0.04em;
  color: var(--text-dim);
  font-family: var(--font-mono);
  text-align: center;
  text-transform: uppercase;
  font-weight: 600;
}
.yh-mlabel {
  font-size: 0.62rem;
  font-family: var(--font-mono);
  color: var(--text-dim);
  text-align: right;
  padding-right: 6px;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  white-space: nowrap;
}
.yh-cell {
  /* Width = full column (stretches); height fixed so 53 rows fit
     in ~660 px of panel height without scroll. The cell aspect
     ratio ends up ~7:1 in a half-width panel — landscape bars. */
  width: 100%;
  height: 11px;
  border-radius: 2px;
  background: var(--bg-elevated);
  transition: transform var(--t-fast), box-shadow var(--t-fast);
  cursor: default;
}
.yh-cell:hover:not(.yh-empty),
.yh-cell:focus-visible:not(.yh-empty) {
  transform: scaleY(1.8);     /* expand vertically on hover so the
                                  enlarged cell doesn't overflow into
                                  neighbouring weeks. */
  z-index: 2;
  position: relative;
  outline: 1px solid var(--accent);
  box-shadow: 0 0 0 1px var(--surface), 0 0 6px var(--accent-glow);
}
.yh-empty { visibility: hidden; }

/* Live readout above the heatmap — fills in on hover/focus, blank
   placeholder when not interacting so the panel height is stable. */
.yh-readout {
  display: flex;
  align-items: baseline;
  gap: 12px;
  flex-wrap: wrap;
  padding: 6px 10px;
  margin-bottom: 12px;
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  background: var(--surface-hover);
  min-height: 30px;
}
.yh-readout-date { color: var(--text); font-weight: 600; }
.yh-readout-count { color: var(--accent); font-weight: 600; }
.yh-readout-rel { flex: 1 1 auto; }

/* Parcel search & export panel */
.parcel-filter-grid {
  display: grid;
  grid-template-columns: 160px 1fr 140px auto;
  gap: 14px;
  align-items: end;
  margin-bottom: 14px;
}
@media (max-width: 880px) {
  .parcel-filter-grid {
    grid-template-columns: 1fr 1fr;
  }
  .parcel-filter-actions { grid-column: 1 / -1; }
}
.parcel-filter { display: flex; flex-direction: column; gap: 6px; min-width: 0; }
.parcel-filter label {
  margin: 0;
  font-size: 0.7rem;
  color: var(--text-faint);
  font-weight: 500;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}
.filter-input,
.parcel-filter input[type="text"],
.parcel-filter input[type="search"],
.parcel-filter select {
  width: 100%;
  padding: 8px 12px;
  font-family: inherit;
  font-size: 0.9rem;
  color: var(--text);
  background: var(--bg-elevated);
  border: 1px solid var(--border-strong);
  border-radius: var(--r-sm);
  transition: border-color var(--t-fast), box-shadow var(--t-fast);
  outline: none;
}
.parcel-filter select { appearance: none; cursor: pointer; padding-right: 30px;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'><path d='M2 4l4 4 4-4' stroke='%239092a0' stroke-width='1.6' fill='none'/></svg>");
  background-repeat: no-repeat;
  background-position: right 10px center;
}
.parcel-filter input:focus,
.parcel-filter select:focus {
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-faint);
}
.parcel-filter-actions { display: flex; gap: 8px; flex-wrap: wrap; }
.parcel-filter-actions .btn { padding: 8px 14px; font-size: 0.85rem; white-space: nowrap; }

.parcel-search-meta {
  padding: 10px 14px;
  background: var(--bg-elevated);
  border-radius: var(--r-sm);
  margin-bottom: 14px;
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
}
.parcel-search-meta.err { color: var(--err); background: var(--err-faint); }
.match-badge {
  display: inline-block;
  padding: 2px 10px;
  border-radius: 999px;
  background: var(--accent-faint);
  color: var(--accent);
  border: 1px solid var(--border-accent);
  font-weight: 600;
  letter-spacing: 0.02em;
  font-family: var(--font-mono);
}

.parcel-preview-wrap {
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  overflow: hidden;
  animation: fadeInUp 240ms cubic-bezier(0.16, 1, 0.3, 1);
}
.parcel-preview-header {
  padding: 8px 12px;
  background: var(--bg-elevated);
  border-bottom: 1px solid var(--border);
}
.parcel-preview-table-wrap { max-height: 520px; overflow-y: auto; }
.parcel-preview-table { font-size: 0.85rem; }
.parcel-preview-table thead th {
  position: sticky;
  top: 0;
  z-index: 1;
}
/* Expand-on-click rows: thumbnail + click-to-expand UX. */
.parcel-row { cursor: pointer; transition: background 80ms ease; }
.parcel-row:hover { background: var(--surface-hover, rgba(0, 0, 0, 0.04)); }
.parcel-row.expanded { background: var(--surface-hover, rgba(0, 0, 0, 0.05)); }
.parcel-thumb {
  width: 44px; height: 44px; object-fit: cover;
  border-radius: 4px;
  border: 1px solid var(--border, rgba(0, 0, 0, 0.12));
  display: block;
}
.parcel-thumb-placeholder {
  display: inline-block; width: 44px; text-align: center;
  color: var(--ink-muted, #aaa); font-size: 12px;
}
.parcel-row-expand > td {
  background: var(--surface-hover, rgba(0, 0, 0, 0.03));
  padding: 12px 14px;
}
.parcel-expand-grid {
  display: grid;
  grid-template-columns: minmax(220px, 320px) 1fr;
  gap: 16px;
}
@media (max-width: 720px) {
  .parcel-expand-grid { grid-template-columns: 1fr; }
}
.parcel-expand-row {
  display: flex; justify-content: space-between; gap: 12px;
  padding: 4px 0; border-bottom: 1px solid var(--border, rgba(0, 0, 0, 0.06));
}
.parcel-expand-row:last-child { border-bottom: 0; }
.parcel-img-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(110px, 1fr));
  gap: 8px;
}
.parcel-img-tile img {
  width: 100%; aspect-ratio: 1; object-fit: cover;
  border-radius: 4px; display: block;
  border: 1px solid var(--border, rgba(0, 0, 0, 0.12));
}
.parcel-img-tile:hover img { border-color: var(--accent, #4a90e2); }

/* ---- OOG panel on Reports ------------------------------------------ */
.oog-panel { border-left: 4px solid #f59e0b; }
.oog-grid {
  display: grid;
  grid-template-columns: 160px 1fr 240px;
  gap: 18px;
  align-items: start;
}
@media (max-width: 900px) {
  .oog-grid { grid-template-columns: 1fr; }
}
.oog-kpi-cell {
  background: rgba(254, 243, 199, 0.4);
  border-radius: 8px;
  padding: 14px 16px;
  text-align: center;
}
.oog-kpi-value { font-size: 32px; font-weight: 700; color: #92400e; }
.oog-kpi-label { font-size: 12px; margin-top: 2px; }
.oog-reasons-list { display: flex; flex-direction: column; gap: 4px; }
.oog-reason-row {
  display: grid;
  grid-template-columns: 200px 1fr 60px;
  gap: 10px;
  align-items: center;
  padding: 4px 6px;
  border: none;
  background: transparent;
  text-align: left;
  cursor: pointer;
  border-radius: 4px;
  font-size: 12px;
  color: inherit;
}
.oog-reason-row:hover { background: var(--surface-hover, rgba(0, 0, 0, 0.04)); }
.oog-reason-label { font-family: var(--font-mono, monospace); font-size: 11px; }
.oog-reason-bar-wrap {
  background: var(--surface-hover, rgba(0, 0, 0, 0.06));
  height: 8px; border-radius: 4px; overflow: hidden;
}
.oog-reason-bar { display: block; height: 100%; background: #f59e0b; border-radius: 4px; }
.oog-reason-count { text-align: right; }
.oog-sorter-row {
  display: flex; justify-content: space-between;
  padding: 3px 4px; font-size: 12px;
  border-bottom: 1px solid var(--border, rgba(0, 0, 0, 0.06));
}
.oog-sorter-row:last-child { border-bottom: 0; }

/* ---- SCADA page ----------------------------------------------------- */

.scada-page { display: flex; flex-direction: column; gap: 12px; }

.scada-stage {
  position: relative;
  width: 100%;
  height: 70vh;
  min-height: 480px;
  background: #000000;
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  overflow: hidden;
  box-shadow: var(--shadow-card);
}
.scada-stage canvas {
  display: block;
  width: 100%;
  height: 100%;
}

/* HUD pinned top-left of the canvas */
.scada-hud {
  position: absolute;
  top: 14px;
  left: 14px;
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding: 10px 14px;
  background: rgba(15, 16, 20, 0.7);
  backdrop-filter: blur(10px) saturate(140%);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  font-size: 0.85rem;
  pointer-events: none;
}
.scada-hud-row {
  display: flex;
  gap: 12px;
  align-items: baseline;
}
.scada-hud-row > span:first-child { min-width: 90px; }

/* Centered overlay shown for loading / empty / error states */
.scada-overlay {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(10, 10, 13, 0.7);
  backdrop-filter: blur(6px);
  z-index: 2;
  animation: fadeInUp 240ms ease-out;
}
.scada-overlay-card {
  max-width: 480px;
  padding: 26px 32px;
  background: var(--surface);
  border: 1px solid var(--border-strong);
  border-radius: var(--r-lg);
  text-align: center;
  box-shadow: var(--shadow-card);
}
.scada-overlay-icon { font-size: 2.5rem; margin-bottom: 8px; }
.scada-overlay-title { margin: 6px 0 8px 0; font-size: 1.15rem; }
.scada-overlay-text { margin: 0; color: var(--text-dim); font-size: 0.92rem; line-height: 1.55; }
.scada-overlay-empty .scada-overlay-card  { border-color: rgba(108, 182, 255, 0.3); }
.scada-overlay-error .scada-overlay-card  { border-color: rgba(255, 77, 109, 0.3); }
.scada-overlay-error .scada-overlay-title { color: var(--err); }

.scada-tagged-table { font-size: 0.9rem; }

/* SCADA main: the 3D viewer, full width. The insights/throughput
   column that used to sit beside it (320px) moved DOWN into
   .scada-lower next to the alarm table (2026-07-16 — it made the 3D
   view too narrow). */
.scada-main {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 320px;
  gap: 12px;
}

/* Desktop: the ORIGINAL layout — 3D stage + 320px insights column
   side by side; the alarm table full-width below. The three-box band
   is KIOSK-EXCLUSIVE (scoped under .page-scada-embed-alarms further
   down) — Chris 2026-07-17: desktop keeps its original shape. */
.scada-page > .scada-alarms-panel { margin-top: 12px; }

/* Kiosk lower band — THREE boxes side by side:
   Alarms (half) · Insights (quarter) · Throughput+per-chute (quarter).
   Each scrolls INSIDE itself. */
.page-scada-embed-alarms .scada-lower {
  display: grid;
  grid-template-columns: minmax(0, 2fr) minmax(0, 1fr) minmax(0, 1fr);
  gap: 12px;
  margin-top: 12px;
  align-items: stretch;
}
.page-scada-embed-alarms .scada-lower-box {
  overflow-y: auto;
  padding: 12px 14px;
}

/* Alarm-state pulse on the Alarms box — BOTH desktop and kiosk (the
   one piece of the kiosk look that goes everywhere). Active alarms
   demand a reset even when acknowledged, so ack state is deliberately
   IGNORED. Critical outranks warning. The glow bleeds INTO the box
   (inset shadow + breathing background tint) so it can't be missed. */
@keyframes scada-box-pulse-crit {
  0%, 100% { box-shadow: 0 0 0 0 rgba(242, 88, 93, 0.18),
                         inset 0 0 18px 2px rgba(242, 88, 93, 0.10);
             border-color: rgba(242, 88, 93, 0.5);
             background-color: rgba(242, 88, 93, 0.03); }
  50%      { box-shadow: 0 0 30px 6px rgba(242, 88, 93, 0.65),
                         inset 0 0 48px 10px rgba(242, 88, 93, 0.28);
             border-color: rgba(255, 80, 86, 1);
             background-color: rgba(242, 88, 93, 0.10); }
}
@keyframes scada-box-pulse-warn {
  0%, 100% { box-shadow: 0 0 0 0 rgba(242, 162, 60, 0.14),
                         inset 0 0 16px 2px rgba(242, 162, 60, 0.08);
             border-color: rgba(242, 162, 60, 0.45);
             background-color: rgba(242, 162, 60, 0.03); }
  50%      { box-shadow: 0 0 26px 5px rgba(242, 162, 60, 0.55),
                         inset 0 0 42px 8px rgba(242, 162, 60, 0.22);
             border-color: rgba(255, 170, 60, 0.95);
             background-color: rgba(242, 162, 60, 0.08); }
}
.scada-alarms-panel.alarm-pulse-crit { animation: scada-box-pulse-crit 1.2s ease-in-out infinite; }
.scada-alarms-panel.alarm-pulse-warn { animation: scada-box-pulse-warn 2s ease-in-out infinite; }
@media (prefers-reduced-motion: reduce) {
  .scada-alarms-panel.alarm-pulse-crit,
  .scada-alarms-panel.alarm-pulse-warn { animation: none; }
}

/* Embed mode — the SCADA page is loaded inside the Home widget's
 * iframe. We strip ALL surrounding chrome so just the 3D viewport
 * fills the iframe. Real class names from base.html:
 *   .page-header        top nav bar (ORION|HELIOS + tabs)
 *   .orion-imp-banner   yellow impersonation banner (god-mode only)
 *   .main               the page-content wrapper
 *   .ow-launcher / .ow-panel  Ask-Orion chat launcher (rendered
 *                       by chat-with-orion.js into <body>)
 * Hiding everything except the SCADA viewport leaves the iframe
 * showing only the 3D canvas. */
.page-scada-embed .page-header,
.page-scada-embed .orion-imp-banner,
.page-scada-embed .ow-launcher,
.page-scada-embed .ow-panel {
  display: none !important;
}

/* KIOSK exception (?embed=1&alarms=1): the operator panel is a full
   working surface, not a dashboard tile — Ask Orion comes back. The
   launcher restores its natural display; the chat panel only when
   open (its closed state is display:none by design). */
.page-scada-embed-alarms .ow-launcher { display: inline-flex !important; }
.page-scada-embed-alarms .ow-panel.ow-open { display: flex !important; }

/* Inside ANY iframe (the kiosk shell's section frames, home-dashboard
   widgets) the page must not carry its own top nav — the surrounding
   shell/widget provides navigation, and a second nav bar inside the
   frame reads as a confusing duplicate. The `in-frame` class is set on
   <html> by an inline <head> script in base.html before first paint. */
html.in-frame .page-header,
html.in-frame .orion-imp-banner {
  display: none !important;
}
.page-scada-embed body,
.page-scada-embed html {
  height: 100%;
  margin: 0;
  padding: 0;
}
.page-scada-embed .main {
  padding: 0 !important;
  margin: 0 !important;
  max-width: none !important;
  height: 100vh !important;
  min-height: 0 !important;
}
.page-scada-embed .scada-page {
  /* Exact-fit column: toolbar row (auto) + stage (remaining space).
     Without this the stage's own 100vh PLUS the toolbar height
     overflowed the iframe, so the kiosk shell showed a page that ran
     past the bottom of the panel glass. */
  display: flex;
  flex-direction: column;
  height: 100%;
  overflow: hidden;
  padding: 0;
  margin: 0;
}
.page-scada-embed .scada-main {
  grid-template-columns: 1fr;       /* sidebar is hidden, collapse the column */
  /* flex-basis 0 (not height:100%) — the toolbar row sits above this
     in the column, so a 100% height would over-fill the page again. */
  flex: 1 1 0;
  min-height: 0;
  height: auto;
  gap: 0;
}
.page-scada-embed .scada-stage {
  /* Fill the flex column's remaining space exactly — a hard 100vh here
     ignored the toolbar above and pushed the stage past the viewport. */
  height: 100%;
  min-height: 0;
}

/* Embed + alarms (?embed=1&alarms=1 — the kiosk shell's SCADA tab).
   Exact-fit column: toolbar (auto) + full-width 3D stage (remaining) +
   the .scada-lower band (fixed 34vh: alarms left, insights right, each
   scrolling INSIDE itself). The page itself never scrolls on glass. */
.page-scada-embed-alarms .scada-lower {
  flex: 0 0 26vh;  /* was 34vh — Chris: boxes shorter, 3D bigger */
  min-height: 0;
  margin: 10px;
  overflow: hidden;
  align-items: stretch;
}
.page-scada-embed-alarms .scada-alarms-panel {
  min-height: 0;
  display: flex;
  flex-direction: column;
  overflow: hidden;
}
.page-scada-embed-alarms .scada-alarm-table-wrap {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
}
.page-scada-embed-alarms .scada-lower-box {
  max-height: none;
  min-height: 0;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
}
/* Kiosk keeps the toolbar row (asset picker / device jump / Run-Stop-
   Fault / Reset view) — it was hidden briefly on a misread of "lose a
   row" (Chris wants it, 2026-07-17). The Game button is the one thing
   that has no business on a customer panel. */
.page-scada-embed-alarms .scada-toolbar { display: flex; }
.page-scada-embed-alarms #scada-game-toggle { display: none !important; }
/* Viewer-role kiosk can't acknowledge — hide the button, not disable:
   a dead control on a touch panel reads as broken. */
.page-scada-embed-alarms #alarm-ack-all { display: none; }

.scada-side {
  /* The LAYOUT column, not a card: two separate boxes live inside it
     (Chris 2026-08-30 - throughput must stay fixed while insights scroll).
     Height tracks the 3D stage exactly (70vh, min 480px - lockstep with
     .scada-stage) so the column bottom always lands on the stage bottom. */
  display: flex;
  flex-direction: column;
  gap: 12px;
  height: 70vh;
  min-height: 480px;
}
.scada-side-box {
  background: var(--surface, #1a1b20);
  border: 1px solid var(--border, rgba(255, 255, 255, 0.08));
  border-radius: 8px;
  padding: 12px;
}
.scada-side-box-scroll {
  /* Insights: take whatever height throughput leaves, scroll inside.
     min-height keeps the box present even against a huge chute list. */
  flex: 1 1 auto;
  min-height: 120px;
  overflow-y: auto;
}
.scada-side-box-fixed {
  /* Throughput + per-chute: natural size, never pushed around by insight
     length. Safety valve: if the per-chute list alone outgrows 60% of the
     column it scrolls internally rather than crushing insights away. */
  flex: 0 1 auto;
  max-height: 60%;
  overflow-y: auto;
}
.scada-side-section {
  padding-bottom: 12px;
  border-bottom: 1px dashed var(--border, rgba(255, 255, 255, 0.08));
}
.scada-side-section:last-child { border-bottom: 0; padding-bottom: 0; }
.scada-side-title {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  margin-bottom: 8px;
  font-weight: 600;
  font-size: 13px;
}
.scada-kpi-row {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 8px;
  margin-bottom: 8px;
}
.scada-kpi {
  display: flex;
  flex-direction: column;
  padding: 8px 10px;
  background: var(--bg-elevated-2, #1c1d22);
  border-radius: 6px;
}
.scada-kpi-value {
  font-size: 18px;
  font-weight: 700;
  line-height: 1.1;
}
.scada-kpi-label { line-height: 1.2; margin-top: 2px; }

/* Peak-capacity hero card. The one number worth reading across the room:
   the best sustained burst the machine has ACHIEVED, measured, not specified.
   Deliberately the only place on this panel with an accent gradient - it is
   the headline, everything else is supporting detail. */
.scada-peak {
  position: relative;
  padding: 12px 14px;
  margin-bottom: 10px;
  border-radius: 8px;
  border: 1px solid rgba(7, 187, 241, 0.28);
  background:
    radial-gradient(ellipse at top right, rgba(7, 187, 241, 0.16), transparent 62%),
    var(--bg-elevated-2, #1c1d22);
  overflow: hidden;
}
.scada-peak-label {
  font-size: 10px; letter-spacing: .12em; text-transform: uppercase;
  color: var(--text-dim, #9092a0); font-weight: 600;
}
.scada-peak-value {
  display: flex; align-items: baseline; gap: 3px;
  font-size: 30px; font-weight: 700; line-height: 1.05;
  color: var(--accent, #07bbf1);
  font-variant-numeric: tabular-nums;
  margin: 2px 0 1px;
}
.scada-peak-unit { font-size: 14px; font-weight: 600; opacity: .75; }
.scada-peak-sub { line-height: 1.35; }
/* No sustained run recorded yet - drop the accent so an empty state never
   masquerades as a reading. */
.scada-peak.is-empty {
  border-color: var(--border, rgba(255, 255, 255, 0.08));
  background: var(--bg-elevated-2, #1c1d22);
}
.scada-peak.is-empty .scada-peak-value { color: var(--text-faint, #5a5d68); }

/* Insights cards — coloured left border by severity. */
.scada-insight {
  padding: 8px 10px 8px 12px;
  background: var(--bg-elevated, #15161a);
  border-left: 3px solid var(--accent, #4a90e2);
  border-radius: 4px;
  margin-bottom: 6px;
  font-size: 12px;
  line-height: 1.45;
}
.scada-insight strong { display: block; margin-bottom: 2px; }
.scada-insight.sev-warning  { border-left-color: #f59e0b; }
.scada-insight.sev-critical { border-left-color: #ef4444; }
.scada-insight.sev-info     { border-left-color: #4a90e2; }

/* Per-chute rows — compact list with a utilisation bar. */
.scada-chutes {
  display: flex;
  flex-direction: column;
  gap: 4px;
  font-size: 12px;
}
.scada-chute-row {
  display: grid;
  grid-template-columns: 56px 1fr 60px;
  align-items: center;
  gap: 8px;
  padding: 4px 6px;
  border-radius: 4px;
}
.scada-chute-row:hover { background: var(--bg-elevated, #15161a); }
.scada-chute-num { font-weight: 600; }
.scada-chute-bar-wrap {
  height: 8px;
  background: var(--bg-elevated, #15161a);
  border-radius: 4px;
  overflow: hidden;
}
.scada-chute-bar { height: 100%; background: var(--accent, #4a90e2); }
.scada-chute-count { text-align: right; }

/* Alarm tabs */
.scada-alarm-tabs {
  display: inline-flex;
  border: 1px solid var(--border-strong, rgba(255, 255, 255, 0.14));
  border-radius: 6px;
  overflow: hidden;
}
.scada-alarm-tab {
  border: 0;
  background: var(--bg-elevated, #15161a);
  color: var(--text-dim, #9092a0);
  padding: 6px 14px;
  font-weight: 600;
  font-size: 13px;
  cursor: pointer;
}
.scada-alarm-tab + .scada-alarm-tab {
  border-left: 1px solid var(--border-strong, rgba(255, 255, 255, 0.12));
}
.scada-alarm-tab:hover { background: var(--bg-elevated-2, #1c1d22); color: var(--text); }
.scada-alarm-tab.active { background: var(--accent, #07bbf1); color: #fff; }
.scada-alarm-tab-count {
  margin-left: 6px;
  font-size: 11px;
  padding: 1px 6px;
  background: rgba(255, 255, 255, 0.16);
  border-radius: 8px;
}
/* Severity split inside the Active tab: critical / warning / info. */
#alarm-count-active { background: transparent; padding: 0; display: inline-flex; gap: 4px; }
.scada-sev-chip {
  display: inline-block;
  padding: 1px 7px;
  border-radius: 8px;
  font-size: 11px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  background: rgba(255, 255, 255, 0.16);
  color: inherit;
}
.scada-sev-chip.crit { background: var(--err, #ef4444); color: #fff; }
.scada-sev-chip.warn { background: var(--warn, #f59e0b); color: #1a1200; }
.scada-sev-chip.info { background: rgba(255, 255, 255, 0.14); color: var(--text-dim, #c7c9d3); }
.scada-sev-chip.none { background: rgba(255, 255, 255, 0.10); }
.scada-alarm-tab.active .scada-sev-chip.info { background: rgba(0, 0, 0, 0.25); color: #fff; }
.scada-sev-chip-l { font-weight: 500; opacity: 0.85; }
@media (max-width: 900px) { .scada-sev-chip-l { display: none; } }

/* Chips are controls now: click one to show only that severity, click it
   again (or "show all") to clear. They live INSIDE the Active tab button, so
   they carry role="button" rather than being real buttons — the focus ring
   and pressed state have to be explicit here. */
.scada-sev-chip[data-sev] { cursor: pointer; user-select: none; }
.scada-sev-chip[data-sev]:hover { filter: brightness(1.15); }
.scada-sev-chip[data-sev]:focus-visible {
  outline: 2px solid var(--accent, #4f8cff);
  outline-offset: 2px;
}
/* The pressed chip is the only one that reads as "on" — the others dim, so
   at a glance it is obvious the list below is filtered and not just empty. */
.scada-sev-chip.on {
  box-shadow: 0 0 0 2px var(--surface, #14161c), 0 0 0 4px currentColor;
}
#alarm-count-active:has(.scada-sev-chip.on) .scada-sev-chip[data-sev]:not(.on):not(.all) {
  opacity: 0.45;
}
.scada-sev-chip.all {
  background: transparent;
  border: 1px dashed var(--border, #3a3f4b);
  color: var(--text-dim, #c7c9d3);
  font-weight: 500;
}
.scada-sev-chip.all:hover { border-style: solid; color: var(--text, #eef0f6); }
@media (prefers-reduced-motion: no-preference) {
  .scada-sev-chip[data-sev] { transition: opacity var(--t-fast, .12s), filter var(--t-fast, .12s); }
}

/* Alarm table — scrollable, tight rows. */
.scada-alarm-table-wrap {
  max-height: 260px;
  overflow-y: auto;
}
.scada-alarm-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 13px;
}
.scada-alarm-table th,
.scada-alarm-table td {
  padding: 7px 12px;
  text-align: left;
  border-bottom: 1px solid var(--border, rgba(255, 255, 255, 0.06));
  vertical-align: top;
}
/* Click-to-focus alarm rows — the JS adds this class to rows whose
   device string maps to a tagged 3D node. Hover highlight + pointer
   cursor make the affordance obvious. Non-clickable rows (e.g. an
   alarm on a device the current asset doesn't model) stay plain. */
.scada-alarm-table tr.scada-alarm-row-clickable {
  cursor: pointer;
  transition: background-color 120ms ease;
}
.scada-alarm-table tr.scada-alarm-row-clickable:hover {
  background-color: rgba(74, 222, 128, 0.06);
}
.scada-alarm-table tr.scada-alarm-row-clickable:hover td:first-child {
  box-shadow: inset 3px 0 0 var(--accent, #4ade80);
}
/* Acked-but-still-active rows: operator has seen the alarm, fault may
   not be cleared yet. Dim the row so it stops demanding attention but
   leave it in the active table (per ISA-18.2 — ack != clear). */
.scada-alarm-table tr.scada-alarm-row-acked {
  opacity: 0.55;
}
.scada-alarm-table tr.scada-alarm-row-acked:hover {
  opacity: 0.85;
}
.scada-alarm-table tr.scada-alarm-row-acked .scada-sev-pill {
  filter: saturate(0.35);
}
.scada-alarm-table th {
  font-size: 11px;
  font-weight: 600;
  color: var(--text-dim, #9092a0);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  background: var(--bg-elevated, #15161a);
  position: sticky;
  top: 0;
  z-index: 1;
}
.scada-sev-pill {
  display: inline-block;
  font-size: 11px;
  font-weight: 700;
  padding: 2px 8px;
  border-radius: 8px;
  text-transform: uppercase;
  letter-spacing: 0.04em;
}
.scada-sev-pill.sev-critical { background: rgba(239, 68, 68, 0.2); color: #fca5a5; }
.scada-sev-pill.sev-warning  { background: rgba(245, 158, 11, 0.18); color: #fde68a; }
.scada-sev-pill.sev-info     { background: rgba(74, 144, 226, 0.18); color: #bfdbfe; }

@media (max-width: 1100px) {
  .scada-main { grid-template-columns: 1fr; }
  .scada-side { height: auto; min-height: 0; max-height: none; }  /* stacked: no stage to match */
  .scada-side-box-scroll, .scada-side-box-fixed { max-height: none; overflow-y: visible; }
}

/* Stats page range-clamp / error banner. Sits just below the
   time-range bar; warn (orange) for clamped ranges, error (red)
   for actual failures. */
.stats-range-banner {
  margin: 8px 0 12px;
  padding: 8px 12px;
  border-radius: 6px;
  font-size: 13px;
  line-height: 1.4;
}
.stats-range-banner[data-kind="warn"] {
  background: rgba(245, 158, 11, 0.14);
  border: 1px solid rgba(245, 158, 11, 0.4);
  color: #fde68a;
}
.stats-range-banner[data-kind="error"] {
  background: rgba(239, 68, 68, 0.14);
  border: 1px solid rgba(239, 68, 68, 0.4);
  color: #fca5a5;
}

/* SCADA hover tooltip — follows the cursor when over a tagged
   mesh. Pointer-events: none so it doesn't steal hovers/clicks
   from the canvas. */
.scada-tooltip {
  position: absolute;
  pointer-events: none;
  background: rgba(20, 22, 28, 0.96);
  border: 1px solid var(--border-strong, rgba(255, 255, 255, 0.18));
  border-radius: 6px;
  padding: 6px 10px;
  font-size: 12px;
  color: var(--text);
  box-shadow: 0 6px 18px rgba(0, 0, 0, 0.4);
  z-index: 50;
  white-space: nowrap;
}
.scada-tooltip-name { font-weight: 600; }
.scada-tooltip-role {
  color: var(--text-dim);
  font-size: 11px;
  margin-left: 6px;
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

/* SCADA selection stats panel — anchored to the right edge of the
   3D viewport. Opens on click; close button hides it. */
.scada-stats {
  position: absolute;
  top: 16px;
  right: 16px;
  width: 320px;
  max-width: 90%;
  background: var(--surface, #1a1b20);
  border: 1px solid var(--border-strong, rgba(255, 255, 255, 0.14));
  border-radius: 8px;
  padding: 0;
  box-shadow: 0 10px 32px rgba(0, 0, 0, 0.45);
  z-index: 60;
}
.scada-stats-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 10px 14px;
  border-bottom: 1px solid var(--border, rgba(255, 255, 255, 0.08));
}
.scada-stats-title { font-weight: 600; font-size: 14px; }
.scada-stats-body { padding: 12px 14px; font-size: 13px; }
.scada-stats-row {
  display: flex;
  justify-content: space-between;
  padding: 4px 0;
  border-bottom: 1px dashed var(--border, rgba(255, 255, 255, 0.06));
}
.scada-stats-row:last-child { border-bottom: 0; }
.scada-stats-key { color: var(--text-dim); }
.scada-stats-val { font-weight: 500; }
/* Sub-section divider inside the motor card (Live telemetry / Reliability
   / Maintenance). */
.scada-stats-section {
  margin: 10px 0 4px;
  font-size: 0.7rem;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-faint);
}
.scada-stats-section:first-child { margin-top: 0; }
/* Active error/warning chips on the motor card. */
.scada-err-chip {
  display: inline-block;
  padding: 1px 7px;
  margin: 1px 0;
  border-radius: 999px;
  font-size: 0.72rem;
  font-weight: 600;
  background: var(--bg-elevated, rgba(255,255,255,0.06));
}
.scada-err-chip.err  { color: var(--err);  border: 1px solid var(--err); }
.scada-err-chip.warn { color: var(--warn); border: 1px solid var(--warn); }
.scada-err-chip.muted { color: var(--text-dim); border: 1px solid var(--border); }
.scada-maint-link {
  color: var(--accent);
  text-decoration: none;
  font-weight: 500;
}
.scada-maint-link:hover { text-decoration: underline; }
.scada-stats-badge {
  display: inline-block;
  padding: 1px 6px;
  background: var(--surface-hover);
  border-radius: 999px;
  font-size: 0.7rem;
  text-transform: uppercase;
  color: var(--text-dim);
  margin-right: 4px;
}
.scada-stats-section { margin-top: 12px; }
.scada-stats-section-title {
  font-size: 11px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--text-dim);
  margin-bottom: 6px;
}
.scada-stats-fault {
  background: rgba(255, 50, 50, 0.12);
  border: 1px solid rgba(255, 50, 50, 0.4);
  border-radius: 6px;
  padding: 8px 10px;
  margin-top: 6px;
  font-size: 12px;
  line-height: 1.45;
}
.scada-stats-fault strong { color: #ff8f8f; }

/* Per-asset active-alarm rows inside the stats card (real feed,
   2026-07-20 — replaces the demo-era mock line). */
.scada-stats-alarm {
  display: flex; align-items: baseline; gap: 8px;
  padding: 4px 0; border-top: 1px solid rgba(255, 255, 255, 0.06);
}
.scada-stats-alarm:first-of-type { margin-top: 6px; }
.scada-stats-alarm-chip {
  flex: 0 0 auto; font-size: 10px; font-weight: 700; letter-spacing: .06em;
  padding: 1px 7px; border-radius: 9px;
}
.scada-stats-alarm.sev-crit .scada-stats-alarm-chip { color: #ff8f8f; background: rgba(255, 50, 50, 0.18); }
.scada-stats-alarm.sev-warn .scada-stats-alarm-chip { color: #ffc46b; background: rgba(242, 162, 60, 0.16); }
.scada-stats-alarm.sev-info .scada-stats-alarm-chip { color: #9fb6d0; background: rgba(120, 160, 200, 0.14); }
.scada-stats-alarm-msg { flex: 1 1 auto; min-width: 0; }
.scada-stats-alarm-age { flex: 0 0 auto; white-space: nowrap; }

/* Fault alarm banner — fixed across the top of the 3D viewport
   while SCADA state === "fault". Pulsing red border draws the eye. */
.scada-alarm {
  position: absolute;
  top: 16px;
  left: 50%;
  transform: translateX(-50%);
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 8px 14px;
  background: rgba(120, 20, 20, 0.92);
  border: 1px solid #ff4d4d;
  border-radius: 8px;
  font-size: 13px;
  font-weight: 500;
  color: #fff;
  box-shadow: 0 0 0 1px rgba(255, 77, 77, 0.5),
              0 6px 22px rgba(0, 0, 0, 0.45);
  z-index: 65;
  animation: scada-alarm-pulse 1.6s ease-in-out infinite;
}
.scada-alarm-icon { font-size: 18px; }
.scada-alarm .btn { padding: 4px 10px; font-size: 12px; }
@keyframes scada-alarm-pulse {
  0%, 100% { box-shadow: 0 0 0 1px rgba(255, 77, 77, 0.5), 0 6px 22px rgba(0, 0, 0, 0.45); }
  50%      { box-shadow: 0 0 0 4px rgba(255, 77, 77, 0.55), 0 6px 22px rgba(255, 77, 77, 0.35); }
}

/* ---- Admin pages (audit + user management) ---------------------------- */

.header-admin-link {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 5px 10px;
  border-radius: var(--r-md);
  color: var(--text-dim);
  font-size: 0.85rem;
  font-weight: 500;
  text-decoration: none;
  white-space: nowrap;
  transition: color var(--t-fast), background var(--t-fast);
}
.header-admin-link:hover { color: var(--text); background: var(--surface-hover); }
.header-admin-link.active { color: var(--text); background: var(--accent-faint); }

/* "Update available" — deliberately the one warm thing in a cool header, so
   an operator notices it without it shouting. Hidden entirely until there is
   actually a newer build (see base.html). */
.header-update {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 5px 12px;
  border: 1px solid var(--warn, #b7791f);
  border-radius: var(--r-md);
  background: color-mix(in srgb, var(--warn, #b7791f) 14%, transparent);
  color: var(--warn, #b7791f);
  font-size: 0.85rem;
  font-weight: 600;
  white-space: nowrap;
  cursor: pointer;
  transition: background var(--t-fast), color var(--t-fast);
}
.header-update:hover {
  background: var(--warn, #b7791f);
  color: #fff;
}
.header-update[hidden] { display: none; }

.audit-page, .users-page, .backups-page { display: flex; flex-direction: column; gap: 16px; }

/* Backup drills — output excerpt cell renders as a small monospace block */
.backups-table .backup-excerpt {
  margin: 0;
  padding: 6px 10px;
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  white-space: pre-wrap;
  word-break: break-word;
  max-height: 120px;
  overflow: hidden;
  color: var(--text-dim);
  font-size: 0.78rem;
  line-height: 1.4;
}

.audit-toolbar {
  display: grid;
  grid-template-columns: 1fr 1fr 120px auto;
  gap: 14px;
  align-items: end;
  margin-bottom: 14px;
}
@media (max-width: 880px) {
  .audit-toolbar { grid-template-columns: 1fr 1fr; }
  .audit-toolbar .parcel-filter-actions { grid-column: 1 / -1; }
}

.audit-table-wrap { max-height: 70vh; overflow-y: auto; border: 1px solid var(--border); border-radius: var(--r-sm); }
.audit-table { width: 100%; }
.audit-table thead th { position: sticky; top: 0; z-index: 1; }
.audit-action {
  font-size: 0.78rem;
  font-weight: 600;
  padding: 2px 8px;
  border-radius: var(--r-sm);
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  color: var(--text);
  white-space: nowrap;
}
.audit-detail {
  word-break: break-word;
  overflow-wrap: break-word;
  max-width: 580px;
  color: var(--text);
  font-size: 0.92rem;
}
.audit-detail .ok-text  { color: var(--ok); }
.audit-detail .err-text { color: var(--err); }
.audit-detail .muted    { color: var(--text-dim); }
.audit-detail .small    { font-size: 0.82rem; }

/* User management table */
.users-table { width: 100%; }
.users-table .pill { white-space: nowrap; }
.users-table .role-pill-admin    { background: var(--accent-faint); color: var(--accent); border: 1px solid var(--border-accent); }
.users-table .role-pill-engineer { background: var(--ok-faint);     color: var(--ok); }
.users-table .role-pill-operator { background: var(--warn-faint);   color: var(--warn); }
.users-table .role-pill-viewer   { background: rgba(255,255,255,0.04); color: var(--text-dim); }
.users-table .row-actions { display: flex; gap: 6px; flex-wrap: wrap; }
.users-table .row-actions .btn { padding: 4px 10px; font-size: 0.78rem; }
.user-name-row {
  display: flex;
  align-items: center;
  gap: 6px;
  margin-bottom: 1px;
}
.user-name {
  color: var(--text);
  font-weight: 600;
  font-size: 0.95rem;
}
.btn.icon-only {
  padding: 2px 6px;
  font-size: 12px;
  line-height: 1;
  opacity: 0.6;
  background: transparent;
  border: 1px solid transparent;
}
.btn.icon-only:hover {
  opacity: 1;
  border-color: var(--border);
  background: var(--surface-hover);
}
.users-table tr.deactivated { opacity: 0.55; }

.add-user-form {
  display: grid;
  grid-template-columns: 1fr 1fr 140px auto;
  gap: 10px;
  align-items: end;
  margin-bottom: 18px;
}
@media (max-width: 880px) {
  .add-user-form { grid-template-columns: 1fr 1fr; }
  .add-user-form > .btn { grid-column: 1 / -1; }
}

.password-banner {
  margin-top: 12px;
  padding: 12px 14px;
  background: var(--ok-faint);
  border: 1px solid rgba(61, 220, 151, 0.3);
  border-radius: var(--r-sm);
  display: flex;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
}
.password-banner .pwd-value {
  font-family: var(--font-mono);
  font-size: 1rem;
  font-weight: 600;
  color: var(--ok);
  background: var(--bg);
  padding: 6px 12px;
  border-radius: var(--r-sm);
  border: 1px solid var(--border);
  user-select: all;
}
.password-banner-warn {
  background: var(--warn-faint);
  border-color: rgba(255, 210, 63, 0.3);
}
.password-banner-warn .pwd-value { color: var(--warn); }

/* Edge-agent status pill — used on /routing96 to show whether the
   docker agent for the active sorter is heartbeating central. Three
   states (online / offline / unknown) with a left-side dot. */
.agent-status {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 5px 12px;
  border-radius: 999px;
  font-size: 0.82rem;
  font-weight: 500;
  font-family: var(--font-mono);
  letter-spacing: 0.02em;
  white-space: nowrap;
  border: 1px solid var(--border);
  background: var(--surface);
  color: var(--text-dim);
  transition: border-color var(--t-fast), background var(--t-fast), color var(--t-fast);
}
.agent-status .agent-status-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--text-faint);
  flex-shrink: 0;
}
.agent-status-online {
  color: var(--ok);
  border-color: rgba(61, 220, 151, 0.3);
  background: var(--ok-faint);
}
.agent-status-online .agent-status-dot {
  background: var(--ok);
  box-shadow: 0 0 8px var(--ok);
  animation: pulseDot 2s ease-in-out infinite;
}
.agent-status-offline {
  color: var(--err);
  border-color: rgba(255, 77, 109, 0.3);
  background: var(--err-faint);
}
.agent-status-offline .agent-status-dot {
  background: var(--err);
  box-shadow: 0 0 8px var(--err);
}
.agent-status-unknown {
  color: var(--text-faint);
}

/* ---- Depot Routing page ------------------------------------------------ */

.routing-page { display: flex; flex-direction: column; gap: 16px; }

/* Load-rebalance banner — quiet amber strip, only shown when imbalanced. */
.rebalance-banner {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 12px 16px;
  border-radius: var(--r-md);
  background: linear-gradient(90deg, rgba(245, 158, 11, 0.14), rgba(245, 158, 11, 0.05));
  border: 1px solid rgba(245, 158, 11, 0.4);
  animation: fadeInUp 240ms cubic-bezier(0.16, 1, 0.3, 1);
}
.rebalance-banner[hidden] { display: none; }
.rebalance-banner-icon { font-size: 1.3rem; line-height: 1; }
.rebalance-banner-text { flex: 1; font-size: 0.92rem; color: var(--text); }
.rebalance-banner-text strong { color: #f59e0b; font-weight: 600; }

/* Rebalance modal contents */
.rebalance-effect {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 12px 14px;
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  margin-bottom: 16px;
}
.rebalance-effect-row { display: flex; justify-content: space-between; align-items: baseline; gap: 12px; }
.rebalance-effect-label { color: var(--text-dim); font-size: 0.85rem; }
.rebalance-effect-val { font-family: var(--font-mono); font-weight: 600; }
.rebalance-arrow { color: var(--text-faint); margin: 0 4px; }
.rebalance-moves-head { font-weight: 600; margin-bottom: 8px; font-size: 0.9rem; }
/* Reserve space so the modal doesn't collapse/jump while a new window's
   suggestion is being fetched (≈ header + up to 3 move rows). */
.rebalance-moves { min-height: 150px; }
/* Dim-in-place during recompute — no layout shift, just a soft fade. */
.rebalance-loading { opacity: 0.4; transition: opacity 0.12s ease; pointer-events: none; }
.rebalance-move-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 6px; }
.rebalance-move {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 10px 12px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
}
.rebalance-move-route { font-weight: 600; min-width: 84px; }
.rebalance-move-path { flex: 1; font-family: var(--font-mono); font-size: 0.9rem; }
.rebalance-move-vol { white-space: nowrap; }
.rebalance-confirm {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 16px 0 4px 0;
  padding: 10px 12px;
  border-radius: var(--r-sm);
  background: rgba(245, 158, 11, 0.08);
  border: 1px solid rgba(245, 158, 11, 0.3);
  font-size: 0.88rem;
  cursor: pointer;
}
.rebalance-confirm input { margin: 0; }
.rebalance-confirm:has(input:disabled) { opacity: 0.45; cursor: not-allowed; }
.rebalance-apply-status { min-height: 18px; font-size: 0.85rem; margin-top: 6px; }

/* Adjustable analysis-window selector in the rebalance modal. */
.rebalance-window {
  display: flex;
  align-items: center;
  gap: 6px;
  flex-wrap: wrap;
  margin-bottom: 14px;
}
.rebalance-win-btn {
  padding: 4px 10px;
  border-radius: var(--r-sm);
  border: 1px solid var(--border-strong);
  background: var(--bg-elevated);
  color: var(--text-dim);
  font-size: 0.82rem;
  font-family: var(--font-mono);
  cursor: pointer;
}
.rebalance-win-btn:hover { color: var(--text); border-color: var(--accent); }
.rebalance-win-btn.active {
  background: var(--accent-faint);
  border-color: var(--accent);
  color: var(--accent);
  font-weight: 600;
}
.rebalance-win-sep { color: var(--border-strong); margin: 0 2px; }
.rebalance-win-step {
  width: 26px; height: 26px;
  display: inline-flex; align-items: center; justify-content: center;
  border-radius: var(--r-sm);
  border: 1px solid var(--border-strong);
  background: var(--bg-elevated);
  color: var(--text);
  font-size: 1.05rem; line-height: 1; cursor: pointer;
  user-select: none;
}
.rebalance-win-step:hover { border-color: var(--accent); color: var(--accent); }
.rebalance-win-value {
  min-width: 84px; text-align: center;
  font-family: var(--font-mono); font-weight: 600; font-size: 0.85rem;
  color: var(--accent);
}

/* Tab bar */
.routing-tabs {
  display: flex;
  gap: 4px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  padding: 4px;
  width: max-content;
}
.routing-tab {
  appearance: none;
  background: none;
  border: 1px solid transparent;
  color: var(--text-dim);
  font-family: inherit;
  font-size: 0.9rem;
  font-weight: 500;
  padding: 8px 16px;
  border-radius: var(--r-sm);
  cursor: pointer;
  transition: color var(--t-fast), background var(--t-fast);
}
.routing-tab:hover { color: var(--text); background: var(--surface-hover); }
.routing-tab.active {
  color: var(--accent);
  background: var(--accent-faint);
  border-color: var(--border-accent);
}
.routing-pane { display: none; }
.routing-pane.active { display: block; animation: fadeInUp 240ms cubic-bezier(0.16, 1, 0.3, 1); }

/* Assign toolbar */
.assign-toolbar {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-bottom: 14px;
  flex-wrap: wrap;
}
.assign-toolbar input[type="search"] {
  flex: 1;
  min-width: 240px;
  max-width: 480px;
}
.assign-toolbar-toggle {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 0.82rem;
  color: var(--text-dim);
  margin: 0;
  text-transform: none;
  letter-spacing: 0;
  font-weight: 500;
  cursor: pointer;
  user-select: none;
}
.assign-toolbar-toggle input { margin: 0; cursor: pointer; }
.assign-toolbar-spacer { flex: 1; }
#assign-pending {
  font-size: 0.78rem;
  transition: color var(--t-base);
}
#assign-pending.saved { color: var(--ok); }

/* Chute card grid — bigger cards now we're showing a multi-route spec */
.assign-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
  gap: 12px;
  padding: 4px;
}
.chute-card {
  appearance: none;
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 14px 16px;
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  cursor: pointer;
  transition: border-color var(--t-fast), background var(--t-fast), transform var(--t-fast);
  text-align: left;
  font-family: inherit;
  color: var(--text);
}
.chute-card:hover {
  border-color: var(--accent);
  background: var(--surface-hover);
  transform: translateY(-1px);
}
.chute-card-empty {
  border-style: dashed;
  opacity: 0.7;
}
.chute-card-empty:hover { opacity: 1; }
.chute-card-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
}
.chute-card-num {
  font-size: 0.95rem;
  font-weight: 600;
  color: var(--text);
  letter-spacing: 0.02em;
}
.chute-card-count {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 32px;
  padding: 2px 8px;
  font-size: 0.8rem;
  font-weight: 600;
  color: var(--accent);
  background: var(--accent-faint);
  border: 1px solid var(--border-accent);
  border-radius: 999px;
}
.chute-card-count-empty {
  color: var(--text-faint);
  background: transparent;
  border-color: var(--border);
}
.chute-card-spec {
  font-size: 0.85rem;
  line-height: 1.4;
  color: var(--text);
  word-break: break-word;
  overflow-wrap: break-word;
  /* Cap at ~3 lines so a chute with 200 routes doesn't stretch the card. */
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

/* Chute editor modal — spec input + live status + conflicts list */
.chute-spec-help {
  margin-top: 6px;
  margin-bottom: 12px;
}
.chute-spec-status {
  padding: 8px 12px;
  background: var(--bg-elevated);
  border-radius: var(--r-sm);
  font-size: 0.88rem;
  margin-bottom: 12px;
}
.chute-spec-status .err-text { color: var(--err); }
.chute-spec-status .ok-text  { color: var(--ok); }
.chute-spec-conflicts {
  padding: 10px 14px;
  background: var(--err-faint);
  border: 1px solid rgba(255, 77, 109, 0.3);
  border-radius: var(--r-sm);
  margin-bottom: 12px;
}
.chute-spec-conflicts strong { display: block; color: var(--err); margin-bottom: 6px; }
.conflict-list { list-style: none; margin: 0 0 6px 0; padding: 0; }
.conflict-list li { font-size: 0.88rem; padding: 2px 0; }
.conflict-list .err-text { color: var(--err); }
#chute-spec-input { font-family: var(--font-mono); }

/* Assign modal — route picker */
.route-pick-list {
  margin: 14px 0;
  max-height: 380px;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.route-pick-row {
  display: grid;
  grid-template-columns: 50px auto 1fr;
  align-items: center;
  gap: 12px;
  padding: 8px 12px;
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  cursor: pointer;
  text-align: left;
  font-family: inherit;
  color: var(--text);
  transition: border-color var(--t-fast), background var(--t-fast);
}
.route-pick-row:hover {
  border-color: var(--accent);
  background: var(--surface-hover);
}
.route-pick-num {
  font-weight: 600;
  color: var(--accent);
  background: var(--accent-faint);
  border-radius: var(--r-sm);
  padding: 2px 8px;
  text-align: center;
}
.route-pick-name { font-size: 0.95rem; }
.route-pick-desc { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
#assign-route-filter { margin-top: 8px; }
.modal-actions {
  display: flex;
  justify-content: space-between;
  gap: 10px;
  padding-top: 10px;
  border-top: 1px solid var(--border);
}

/* Routes catalogue */
.route-toolbar {
  display: grid;
  grid-template-columns: 100px 1fr 2fr auto;
  gap: 10px;
  margin-bottom: 18px;
  align-items: center;
}
@media (max-width: 880px) {
  .route-toolbar { grid-template-columns: 1fr 1fr; }
  .route-toolbar > .btn { grid-column: 1 / -1; }
}
.routes-table .pill { white-space: nowrap; }

/* Chute monitor severity colours on the bar lists */
.chute-bar-fill-overloaded { background: linear-gradient(90deg, var(--err), #ff7a92); box-shadow: 0 0 10px rgba(255,77,109,0.4); }
.chute-bar-fill-heavy      { background: linear-gradient(90deg, var(--warn), #ffe17a); box-shadow: 0 0 8px rgba(255,210,63,0.3); }
.chute-bar-fill-balanced   { background: linear-gradient(90deg, var(--ok), #56e6aa); box-shadow: 0 0 8px rgba(61,220,151,0.3); }
.chute-bar-fill-light      { background: linear-gradient(90deg, var(--info), #9ed1ff); }
.chute-bar-fill-idle       { background: var(--text-faint); }
.chute-row-route { display: block; grid-column: 1 / -1; padding-left: 64px; padding-top: 2px; }

/* ---- Print stylesheet for Reports ----
   Triggered by body.printing-report (set by reports.js when "Print / PDF"
   is clicked). Hides nav chrome, makes the layout single-column, forces
   a white background so the PDF is readable. */
@media print {
  body.printing-report > *:not(main) { display: none !important; }
  body.printing-report .page-header { display: none !important; }
  body.printing-report .period-selector-wrap,
  body.printing-report .scope-selector { display: none !important; }
  body.printing-report .grid-2col,
  body.printing-report .leaderboard-grid,
  body.printing-report .shift-grid,
  body.printing-report .callout-strip,
  body.printing-report .kpi-strip { display: block !important; }
  body.printing-report .grid-2col > .panel,
  body.printing-report .leaderboard-grid > .leaderboard-col,
  body.printing-report .shift-grid > .shift-card,
  body.printing-report .callout-strip > .callout,
  body.printing-report .kpi-strip > .kpi-card {
    page-break-inside: avoid;
    margin-bottom: 12px;
  }
  body.printing-report {
    background: white !important;
    color: #111 !important;
  }
  body.printing-report .panel,
  body.printing-report .kpi-card,
  body.printing-report .callout,
  body.printing-report .lb-row,
  body.printing-report .shift-card,
  body.printing-report .insight {
    background: white !important;
    color: #111 !important;
    border-color: #ddd !important;
    box-shadow: none !important;
  }
  body.printing-report .panel-header { background: #f5f5f5 !important; color: #111 !important; }
  body.printing-report * { color: #111 !important; }
  body.printing-report .muted, body.printing-report .small { color: #555 !important; }
  body.printing-report .kpi-value.ok   { color: #1a7d4a !important; }
  body.printing-report .kpi-value.warn { color: #a17c00 !important; }
  body.printing-report .kpi-value.err  { color: #b00020 !important; }
  body.printing-report canvas { max-width: 100% !important; height: auto !important; }
  body.printing-report .yh-cell { border: 1px solid #ccc; }
  body.printing-report .heatmap-l0 { background: #fafafa !important; }
  body.printing-report .heatmap-l1 { background: #ffe0c2 !important; }
  body.printing-report .heatmap-l2 { background: #ffb066 !important; }
  body.printing-report .heatmap-l3 { background: #ff8a1f !important; }
  body.printing-report .heatmap-l4 { background: #cc5a00 !important; }
}

/* Search-state live indicator class re-used; nothing extra needed */

/* ---- Utilities ---------------------------------------------------- */

.row { display: flex; gap: 12px; align-items: center; flex-wrap: wrap; }
.row.between { justify-content: space-between; }
.stack { display: flex; flex-direction: column; gap: 12px; }
.muted { color: var(--text-dim); }
.mt-3 { margin-top: 24px; }
.mt-2 { margin-top: 16px; }
.mt-1 { margin-top: 8px; }
.fade-in { animation: fadeInUp 480ms cubic-bezier(0.16, 1, 0.3, 1); }


/* ---- Engineering page (ePlan viewer) ----------------------------- */
/*
 * Sidebar + iframe split. Sidebar takes a fixed width; iframe fills
 * everything else and stretches vertically to the available space.
 * On narrow screens the sidebar collapses above the viewer.
 */
/* ---- Engineering page shell ---------------------------------------
 * The page is now a stack of collapsible <details> panels. Each
 * panel owns its own height: the EPLAN panel uses a fixed 720px
 * inner viewport so the embedded PDF.js viewer has room without
 * forcing the rest of the page to scroll-jail. Others size to
 * content.
 */
.page-engineering .main {
  padding: 20px 24px 40px;
  background: var(--bg);
  /* Disable the inherited flex centering — the engineering page is a
     stacked list, not a centered tile. */
  align-items: stretch;
}

.eng-page {
  display: flex;
  flex-direction: column;
  gap: 12px;
  width: 100%;
  max-width: 1600px;
  margin: 0 auto;
}

.eng-section {
  border: 1px solid var(--border);
  background: var(--bg-elevated);
  border-radius: 10px;
  overflow: hidden;
  transition: border-color 120ms ease, box-shadow 120ms ease;
}
.eng-section[open] {
  border-color: var(--border-strong);
  box-shadow: 0 6px 24px rgba(0, 0, 0, 0.18);
}

.eng-section-head {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 12px 16px;
  cursor: pointer;
  user-select: none;
  background: linear-gradient(180deg, rgba(255, 255, 255, 0.025), rgba(255, 255, 255, 0));
  list-style: none;       /* hide the default ▶ marker */
}
.eng-section-head::-webkit-details-marker { display: none; }
.eng-section-head:hover { background: var(--surface-hover); }

.eng-section-caret {
  width: 14px;
  flex-shrink: 0;
  font-size: 12px;
  color: var(--accent);
  transition: transform 120ms ease;
}
.eng-section[open] > .eng-section-head .eng-section-caret {
  /* Caret already reads as ▾ when open via the template, but
     give it the rotation polish for browsers that ignore the text. */
  transform: rotate(0deg);
}

.eng-section-title {
  font-family: var(--font-ui);
  font-size: 0.9rem;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text);
  flex-shrink: 0;
}

.eng-section-sub {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: 12.5px;
  color: var(--text-dim);
  margin-left: 4px;
}

.eng-section-tag {
  flex-shrink: 0;
  font-size: 10.5px;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  padding: 3px 8px;
  border-radius: 999px;
  border: 1px solid transparent;
}
.eng-section-tag-ok {
  color: #6ee7b7;
  background: rgba(16, 185, 129, 0.12);
  border-color: rgba(16, 185, 129, 0.35);
}
.eng-section-tag-warn {
  color: #fcd34d;
  background: rgba(245, 158, 11, 0.12);
  border-color: rgba(245, 158, 11, 0.35);
}
.eng-section-tag-pending {
  color: var(--text-dim);
  background: rgba(255, 255, 255, 0.04);
  border-color: var(--border);
}
.eng-section-tag-shared {
  color: #93c5fd;
  background: rgba(59, 130, 246, 0.10);
  border-color: rgba(59, 130, 246, 0.35);
}

/* ---- Scope tab strip ---------------------------------------------- */
.eng-scope-tabs {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  padding: 4px;
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  border-radius: 10px;
}
.eng-scope-tab {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 8px 14px;
  background: transparent;
  border: 1px solid transparent;
  border-radius: 7px;
  color: var(--text-dim);
  font: inherit;
  font-size: 12.5px;
  font-weight: 600;
  letter-spacing: 0.03em;
  cursor: pointer;
  transition: background 100ms ease, color 100ms ease, border-color 100ms ease;
}
.eng-scope-tab:hover {
  background: var(--surface-hover);
  color: var(--text);
}
.eng-scope-tab.active {
  background: var(--accent-faint);
  color: var(--text);
  border-color: var(--border-accent);
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.04);
}
.eng-scope-label {
  white-space: nowrap;
}
.eng-scope-dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  flex-shrink: 0;
}
.eng-scope-dot-ok { background: #34d399; }
.eng-scope-dot-empty { background: var(--border-strong); }

/* The dynamic section sub-label that shows the active scope name —
   slightly different colour so it visually links to the tabs. */
.eng-section-sub-dynamic {
  color: var(--accent);
  font-weight: 600;
}

/* CAD panes — only the active one is rendered. Inactive panes are
   `hidden` from the template so display:none happens automatically. */
.eng-cad-pane.eng-pane-active {
  display: block;
}

/* ---- Documents panel (deep-docs Phase 1) -------------------------- */
.eng-docs {
  padding: 16px 20px;
}
.eng-docs-upload {
  margin-bottom: 14px;
  padding: 14px;
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  border-radius: 8px;
}
.eng-docs-upload-row {
  display: grid;
  grid-template-columns: minmax(0, 1.5fr) minmax(0, 2fr) minmax(0, 1fr) auto;
  gap: 8px;
  align-items: center;
}
.eng-docs-upload-row input[type="file"],
.eng-docs-upload-row input[type="text"] {
  padding: 7px 10px;
  background: var(--bg);
  border: 1px solid var(--border-strong);
  border-radius: 6px;
  color: var(--text);
  font: inherit;
  font-size: 13px;
}
.eng-docs-upload-row input[type="text"]:focus,
.eng-docs-upload-row input[type="file"]:focus {
  outline: none;
  border-color: var(--accent);
}
.eng-docs-upload-hint {
  margin: 8px 0 0;
  font-size: 11.5px;
}

.eng-docs-empty {
  padding: 18px;
  background: var(--bg);
  border: 1px dashed var(--border);
  border-radius: 8px;
  font-size: 13px;
}

.eng-docs-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 12.5px;
}
.eng-docs-table th {
  text-align: left;
  font-weight: 600;
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--text-dim);
  padding: 6px 10px;
  border-bottom: 1px solid var(--border);
}
.eng-docs-table td {
  padding: 8px 10px;
  border-bottom: 1px solid var(--border);
  vertical-align: top;
}
.eng-docs-title {
  color: var(--text);
  font-weight: 500;
}

.eng-docs-status-pill {
  display: inline-block;
  font-size: 10.5px;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  padding: 2px 8px;
  border-radius: 999px;
  border: 1px solid var(--border);
  background: rgba(255, 255, 255, 0.04);
  color: var(--text-dim);
}
.eng-docs-status-queued .eng-docs-status-pill {
  color: #fcd34d;
  background: rgba(245, 158, 11, 0.10);
  border-color: rgba(245, 158, 11, 0.35);
}
.eng-docs-status-indexing .eng-docs-status-pill {
  color: #93c5fd;
  background: rgba(59, 130, 246, 0.10);
  border-color: rgba(59, 130, 246, 0.35);
}
.eng-docs-status-indexed .eng-docs-status-pill {
  color: #6ee7b7;
  background: rgba(16, 185, 129, 0.12);
  border-color: rgba(16, 185, 129, 0.35);
}
.eng-docs-status-error .eng-docs-status-pill {
  color: #fca5a5;
  background: rgba(239, 68, 68, 0.10);
  border-color: rgba(239, 68, 68, 0.35);
}

.eng-section-body {
  border-top: 1px solid var(--border);
  background: var(--bg);
}

/* The EPLAN body gets a fixed inner-shell height so the PDF.js iframe
   has somewhere to render. Without this the iframe collapses to 0px
   because <details> children are intrinsic-sized. */
.eng-eplan-body { height: 720px; }
.eng-eplan-body .engineering-shell { height: 100%; }

.eng-placeholder {
  padding: 24px;
  color: var(--text-dim);
  font-size: 13.5px;
  line-height: 1.55;
}
.eng-placeholder strong { color: var(--text); font-weight: 600; }
.eng-placeholder-todo {
  margin-top: 14px;
  padding: 10px 14px;
  border-left: 3px solid var(--accent);
  background: rgba(255, 255, 255, 0.025);
  font-size: 12.5px;
  color: var(--text-dim);
  border-radius: 0 6px 6px 0;
}
.eng-placeholder code {
  font-family: var(--font-mono);
  font-size: 12px;
  background: rgba(255, 255, 255, 0.06);
  padding: 1px 5px;
  border-radius: 4px;
  color: var(--text);
}

/* ---- CAD viewer ---------------------------------------------------- */
.eng-cad-viewer {
  height: 720px;
  background: var(--bg);
  display: flex;
  align-items: stretch;
  justify-content: stretch;
}
.eng-cad-viewer iframe,
.eng-cad-viewer object {
  width: 100%;
  height: 100%;
  border: 0;
  background: #fff;       /* CAD drawings are typically white-bg */
}
.eng-cad-png-wrap {
  overflow: auto;         /* large CAD PNGs may exceed the panel */
  padding: 12px;
}
.eng-cad-png-wrap img {
  max-width: 100%;
  height: auto;
  display: block;
  margin: 0 auto;
  background: #fff;
}
.eng-cad-footer {
  margin: 0;
  padding: 10px 16px;
  border-top: 1px solid var(--border);
  font-size: 12px;
  color: var(--text-dim);
}
.eng-cad-footer a {
  color: var(--accent);
  text-decoration: none;
}
.eng-cad-footer a:hover { text-decoration: underline; }

.eng-cad-empty {
  padding: 24px;
  line-height: 1.6;
}
.eng-cad-empty ul {
  margin: 8px 0 0;
  padding-left: 20px;
}
.eng-cad-empty li { margin-bottom: 8px; }
.eng-cad-empty a {
  color: var(--accent);
  text-decoration: none;
}
.eng-cad-empty a:hover { text-decoration: underline; }

.engineering-shell {
  display: grid;
  grid-template-columns: 320px 1fr;
  gap: 0;
  height: 100%;
  background: var(--bg);
}
.eng-sidebar {
  display: flex;
  flex-direction: column;
  border-right: 1px solid var(--border);
  background: var(--bg-elevated);
  min-height: 0;
}
.eng-sidebar-head {
  padding: 14px 16px 8px;
  border-bottom: 1px solid var(--border);
}
.eng-sidebar-head h2 {
  margin: 0;
  font-family: var(--font-ui);
  font-size: 0.95rem;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text);
}
.eng-sidebar-sub {
  margin: 4px 0 0;
  font-size: 12px;
  color: var(--text-dim);
}
.eng-search {
  margin: 10px 12px 6px;
  padding: 8px 12px;
  border: 1px solid var(--border-strong);
  border-radius: 8px;
  background: var(--bg);
  color: var(--text);
  font: inherit;
  font-size: 13px;
  outline: none;
  transition: border-color 120ms ease;
}
.eng-search:focus { border-color: var(--accent); }
.eng-list {
  list-style: none;
  margin: 0;
  padding: 0 4px 12px;
  overflow-y: auto;
  flex: 1;
  min-height: 0;
}
.eng-list li { margin: 0; }

/* ---- Tree-style outline (mirrors PDF.js's bookmarks panel) ----- */
.eng-tree, .eng-tree-children {
  list-style: none;
  margin: 0;
  padding: 0;
}
.eng-tree-node { margin: 0; }

/* Each row is indented in proportion to its tree depth — 12px per
   level, capped via padding so deep trees don't push everything off
   the right edge. */
.eng-tree-row {
  display: flex;
  width: 100%;
  align-items: center;
  gap: 4px;
  padding: 4px 8px 4px calc(8px + var(--depth, 0) * 12px);
  background: transparent;
  border: 1px solid transparent;
  border-radius: 5px;
  color: var(--text-dim);
  text-align: left;
  font: inherit;
  font-size: 12px;
  line-height: 1.3;
  cursor: pointer;
  transition: background 90ms ease, color 90ms ease, border-color 90ms ease;
}
.eng-tree-row:hover {
  background: var(--surface-hover);
  color: var(--text);
}
.eng-tree-row.active {
  background: var(--accent-faint);
  color: var(--text);
  border-color: var(--border-accent);
}

.eng-tree-caret {
  width: 10px;
  flex-shrink: 0;
  color: var(--text-faint);
  font-size: 9px;
  transition: color 80ms ease;
}
.eng-tree-row:hover .eng-tree-caret { color: var(--accent); }
.eng-tree-caret.ow-hidden { visibility: hidden; }

.eng-tree-label {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.eng-tree-page {
  flex-shrink: 0;
  font-size: 10.5px;
  color: var(--text-faint);
  font-variant-numeric: tabular-nums;
  margin-left: 4px;
}

/* Collapsed state: hide children. Caret still shows ▶ (set in JS). */
.eng-tree-node.eng-tree-collapsed > .eng-tree-children {
  display: none;
}

.eng-tree-empty {
  padding: 12px;
  color: var(--text-dim);
  font-size: 12px;
  font-style: italic;
}

/* In search mode the rows are flat (no tree depth indent). */
.eng-tree-search .eng-tree-row {
  padding-left: 8px;
}
.eng-empty {
  padding: 14px 16px;
  font-size: 12.5px;
  color: var(--text-dim);
  line-height: 1.5;
}
.eng-empty code {
  font-family: var(--font-mono);
  font-size: 11.5px;
  background: rgba(255, 255, 255, 0.06);
  padding: 1px 4px;
  border-radius: 4px;
}
.eng-viewer {
  position: relative;
  background: var(--bg);
  min-height: 0;
}
.eng-viewer iframe,
.eng-viewer object,
.eng-viewer embed {
  display: block;
  width: 100%;
  height: 100%;
  border: 0;
  background: var(--bg);
}
.eng-viewer-empty {
  padding: 40px;
  color: var(--text-dim);
  font-size: 14px;
  text-align: center;
}

@media (max-width: 900px) {
  .engineering-shell {
    grid-template-columns: 1fr;
    grid-template-rows: 280px 1fr;
  }
  .eng-sidebar {
    border-right: 0;
    border-bottom: 1px solid var(--border);
  }
}


/* ---- Orion chat widget (Stage 5 of the AI rollout) ---------------- */
/*
 * Class prefix `ow-` (orion-widget) so we never collide with existing
 * page styles. Pulls colours from the design tokens at :root so it
 * matches future theme work for free.
 */

/*
 * Launcher — chat-bubble-shaped pill in the bottom-right corner.
 * Speech-bubble corner radius (bottom-right corner pulled in to a
 * smaller radius) so the silhouette reads as "this is a chat thing"
 * before the user even reads the label. Two-line text composition
 * — "Ask Orion / AI assistant" — invites action; the gold star icon
 * is the brand mark (Orion = the constellation).
 */
.ow-launcher {
  position: fixed;
  bottom: 24px;
  right: 24px;
  z-index: 9000;
  display: inline-flex;
  align-items: center;
  gap: 12px;
  padding: 12px 18px 12px 14px;
  /* Chat-bubble corner — bottom-right tucks in tighter. */
  border-radius: 18px 18px 4px 18px;
  border: 1px solid var(--border-strong);
  background:
    radial-gradient(120% 140% at 0% 0%, var(--accent-faint) 0%, transparent 60%),
    linear-gradient(180deg, var(--bg-elevated), var(--bg-elevated-2));
  color: var(--text);
  font-family: var(--font-ui);
  cursor: pointer;
  /*
   * Layered glow:
   *   1. depth shadow keeps it grounded against the page
   *   2. inner-edge highlight (1px) for crisp boundary
   *   3. gold halo matching the star colour — close-in, ~32px
   *   4. orange accent aura — wide, ~64px, ties it to brand
   * The keyframes below breathe the gold + orange layers in sync
   * with the star's own pulse so the whole pill feels alive
   * rather than just the icon.
   */
  box-shadow:
    0 10px 28px rgba(0, 0, 0, 0.45),
    0 0 0 1px var(--border),
    0 0 32px rgba(7, 187, 241, 0.28),
    0 0 64px var(--accent-glow);
  animation: ow-launcher-glow 3s ease-in-out infinite;
  transition:
    transform 160ms cubic-bezier(0.16, 1, 0.3, 1),
    border-color 160ms ease;
}
@keyframes ow-launcher-glow {
  0%, 100% {
    box-shadow:
      0 10px 28px rgba(0, 0, 0, 0.45),
      0 0 0 1px var(--border),
      0 0 32px rgba(7, 187, 241, 0.28),
      0 0 64px rgba(7, 187, 241, 0.35);
  }
  50% {
    box-shadow:
      0 12px 32px rgba(0, 0, 0, 0.50),
      0 0 0 1px var(--border-strong),
      0 0 44px rgba(7, 187, 241, 0.50),
      0 0 88px rgba(7, 187, 241, 0.55);
  }
}
.ow-launcher:hover {
  transform: translateY(-2px);
  border-color: var(--accent);
  /* Hover trumps the breathing: hold a brighter steady glow so the
     user gets clear feedback that their cursor is in the right place. */
  animation: none;
  box-shadow:
    0 18px 44px rgba(0, 0, 0, 0.55),
    0 0 0 1px var(--accent),
    0 0 56px rgba(7, 187, 241, 0.55),
    0 0 110px rgba(7, 187, 241, 0.65);
}
.ow-launcher:active {
  transform: translateY(0);
  animation: none;
}
.ow-launcher:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
}

.ow-launcher-star {
  width: 32px;
  height: 32px;
  display: inline-grid;
  place-items: center;
  /* Cyan, matching the star in the ORION HELIOS OS orbit swoosh (the
     mark's own star sits on the wordmark's tail). Was gold-amber when the
     accent was orange; kept in step with the accent so the launcher never
     reads as a warning badge. 32px gives both rings and the star body
     enough room to read at a glance. */
  color: #07bbf1;
  filter: drop-shadow(0 0 6px rgba(7, 187, 241, 0.55));
  animation: ow-star-pulse 3s ease-in-out infinite;
}
.ow-launcher-star svg {
  width: 100%;
  height: 100%;
  display: block;
}
@keyframes ow-star-pulse {
  0%, 100% {
    transform: scale(1);
    filter: drop-shadow(0 0 6px rgba(7, 187, 241, 0.55));
  }
  50% {
    transform: scale(1.08);
    filter: drop-shadow(0 0 12px rgba(7, 187, 241, 0.85));
  }
}

/* ---- Unread-notification badge ----------------------------------------
 * Slack/Insta-style red counter pinned to the top-right of the launcher.
 * Animates in via a soft pulse so a new arrival is noticed without being
 * obnoxious. Capped at "99+" by the JS so the pill width is bounded.
 * The badge sits ABOVE the launcher's border so it survives the chip
 * radius without clipping.
 */
.ow-launcher-badge {
  position: absolute;
  top: -6px;
  right: -6px;
  min-width: 20px;
  height: 20px;
  padding: 0 6px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: #ef4444;          /* red-500 — feedreader convention */
  color: #fff;
  font-family: "Inter", system-ui, sans-serif;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.02em;
  border-radius: 999px;
  border: 2px solid var(--bg, #000000);   /* "lifts" the badge off the launcher */
  box-shadow: 0 0 0 0 rgba(239, 68, 68, 0.55);
  animation: ow-badge-pulse 2.4s ease-out infinite;
  z-index: 1;
  pointer-events: none;        /* clicks pass through to the launcher */
  user-select: none;
}
.ow-launcher-badge[hidden] {
  display: none;
}
@keyframes ow-badge-pulse {
  0% {
    box-shadow: 0 0 0 0 rgba(239, 68, 68, 0.55);
  }
  60% {
    box-shadow: 0 0 0 8px rgba(239, 68, 68, 0);
  }
  100% {
    box-shadow: 0 0 0 0 rgba(239, 68, 68, 0);
  }
}

.ow-launcher-text {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 1px;
  line-height: 1.15;
  text-align: left;
}
.ow-launcher-title {
  font-weight: 700;
  font-size: 14px;
  letter-spacing: 0.005em;
}
.ow-launcher-sub {
  font-size: 10.5px;
  font-weight: 600;
  color: var(--text-dim);
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

/*
 * Panel header — small star reuses the same icon, gives the open
 * panel the same brand mark as the closed launcher.
 */
.ow-panel-title {
  display: inline-flex;
  align-items: center;
  gap: 8px;
}
.ow-panel-star {
  display: inline-grid;
  place-items: center;
  /* 22px so the concentric rings remain legible — at the 18px we
     used before, the outer ring crowded the title text. */
  width: 22px;
  height: 22px;
  color: #07bbf1;
  filter: drop-shadow(0 0 4px rgba(7, 187, 241, 0.4));
}
.ow-panel-star svg { width: 100%; height: 100%; display: block; }
.ow-panel-status {
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ok);
  padding: 2px 8px;
  border-radius: 999px;
  background: var(--ok-faint);
  margin-left: 4px;
}

.ow-panel {
  position: fixed;
  bottom: 20px;
  right: 20px;
  z-index: 9001;
  width: 420px;
  max-width: calc(100vw - 40px);
  height: min(640px, calc(100vh - 80px));
  background: var(--bg-elevated);
  border: 1px solid var(--border-strong);
  border-radius: 14px;
  box-shadow: 0 16px 48px rgba(0, 0, 0, 0.55);
  display: none;
  flex-direction: column;
  overflow: hidden;
  font-family: var(--font-ui);
  color: var(--text);
}
.ow-panel.ow-open {
  display: flex;
  animation: ow-slide-in 200ms cubic-bezier(0.16, 1, 0.3, 1);
}
@keyframes ow-slide-in {
  from { opacity: 0; transform: translateY(8px) scale(0.99); }
  to   { opacity: 1; transform: translateY(0)   scale(1); }
}
@media (prefers-reduced-motion: reduce) {
  .ow-panel.ow-open { animation: none; }
}

.ow-panel-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  padding: 10px 12px;
  border-bottom: 1px solid var(--border);
  background: var(--bg-elevated-2);
  /* Header sticks to the panel width — never overflows. */
  min-width: 0;
}
.ow-panel-head .ow-panel-title {
  /* Title group can shrink ("ONLINE" pill collapses if needed)
     so the action group has room. */
  min-width: 0;
  overflow: hidden;
}
.ow-panel-title {
  font-weight: 700;
  font-size: 15px;
  letter-spacing: 0.02em;
}
.ow-panel-actions {
  display: inline-flex;
  align-items: center;
  gap: 2px;
  /* Critical: don't let the actions row push past the panel's
     right edge. min-width:0 lets children shrink; flex-shrink
     ensures the close button below stays pinned even if the
     voice picker has to give up space. */
  min-width: 0;
  flex: 0 1 auto;
}
.ow-close,
.ow-clear,
.ow-speaker {
  background: transparent;
  border: none;
  color: var(--text-dim);
  font-size: 16px;
  line-height: 1;
  cursor: pointer;
  padding: 4px 8px;
  border-radius: 6px;
  flex-shrink: 0;        /* never collapse — these are the always-visible controls */
}
/* The close ✕ in particular MUST always be reachable. Bump
   contrast slightly so it reads as an interactive control. */
.ow-close {
  color: var(--text);
  font-weight: 600;
}
.ow-clear { font-size: 15px; }
.ow-speaker { font-size: 14px; }
.ow-speaker.ow-active { color: var(--accent); }

/* Voice picker — small dropdown in the panel header. Width-capped so
   long voice names don't push the close button off-screen. The
   leading 🎙 in the option text makes the picker more discoverable
   in the corner. */
.ow-voice-picker {
  /* Voice name can be long ("Microsoft Sonia Online (Natural)…"),
     but we'd rather it ellipsis than shove the close button off
     the right edge. flex-shrink lets it give back space; min-width
     keeps it tappable. */
  flex: 0 1 140px;
  min-width: 60px;
  max-width: 160px;
  padding: 3px 8px 3px 22px;
  border-radius: 6px;
  border: 1px solid var(--border-strong);
  background:
    url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="%23ffc857"><path d="M8 1a3 3 0 0 0-3 3v4a3 3 0 0 0 6 0V4a3 3 0 0 0-3-3zm5 7a5 5 0 0 1-10 0H1.5a6.5 6.5 0 0 0 5.75 6.46V15h1.5v-.54A6.5 6.5 0 0 0 14.5 8H13z"/></svg>') no-repeat 5px 50% / 12px,
    var(--bg-elevated-2);
  color: var(--text);
  font: inherit;
  font-size: 11px;
  font-weight: 600;
  cursor: pointer;
  text-overflow: ellipsis;
  margin-right: 4px;
  appearance: none;
  -webkit-appearance: none;
}
.ow-voice-picker:hover { border-color: var(--accent); }
.ow-voice-picker:focus { outline: 1px solid var(--accent); }
@media (max-width: 600px) {
  .ow-voice-picker { max-width: 120px; }
}
.ow-close:hover,
.ow-clear:hover,
.ow-speaker:hover { color: var(--text); background: var(--surface-hover); }

/* Speaker toggle — accent colour when ON so the engineer can see at a
   glance whether typed replies will be read aloud. */
.ow-speaker {
  background: transparent;
  border: 1px solid transparent;
  color: var(--text-faint);
  font-size: 14px;
  width: 28px;
  height: 28px;
  border-radius: 6px;
  cursor: pointer;
  transition: color 100ms ease, background 100ms ease, border-color 100ms ease;
}
.ow-speaker.ow-speaker-on {
  color: var(--accent);
  background: var(--accent-faint);
  border-color: var(--border-accent);
}

/* ---- Notification rail (between panel head and chat list) ------------ */
.ow-notifs {
  border-bottom: 1px solid var(--border);
  background: var(--bg-elevated-2);
  max-height: 40%;        /* never more than 40% of panel */
  overflow-y: auto;
  padding: 8px 10px;
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.ow-notifs[hidden] { display: none; }

.ow-notifs-head {
  font-size: 10.5px;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-dim);
  padding: 2px 4px;
}

.ow-notif-card {
  position: relative;
  display: grid;
  grid-template-columns: 1fr auto;
  grid-template-areas:
    "title   actions"
    "body    body"
    "meta    meta";
  gap: 2px 8px;
  padding: 8px 10px 8px 12px;
  border-left: 3px solid var(--border);
  border-radius: 4px;
  background: var(--bg);
  font-size: 12px;
}
.ow-notif-sev-critical { border-left-color: #ef4444; }
.ow-notif-sev-warning  { border-left-color: #f59e0b; }
.ow-notif-sev-info     { border-left-color: #3b82f6; }
.ow-notif-unread {
  background: linear-gradient(
    90deg,
    rgba(255, 255, 255, 0.03) 0%, transparent 100%
  );
}

.ow-notif-title {
  grid-area: title;
  color: var(--text);
  font-weight: 600;
  font-size: 12.5px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  min-width: 0;
}
.ow-notif-body {
  grid-area: body;
  color: var(--text-dim);
  font-size: 11.5px;
  line-height: 1.35;
  /* line-clamp to 2 so dense notifs don't blow the rail height */
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.ow-notif-meta {
  grid-area: meta;
  color: var(--text-faint);
  font-size: 10.5px;
  margin-top: 2px;
}
.ow-notif-actions {
  grid-area: actions;
  display: flex;
  align-items: center;
  gap: 4px;
}
.ow-notif-investigate {
  background: var(--accent);
  color: #000;
  border: 0;
  font: inherit;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.02em;
  padding: 4px 9px;
  border-radius: 6px;
  cursor: pointer;
  white-space: nowrap;
  transition: filter 100ms ease;
}
.ow-notif-investigate:hover { filter: brightness(1.1); }
.ow-notif-dismiss {
  width: 22px;
  height: 22px;
  display: inline-grid;
  place-items: center;
  background: transparent;
  border: 0;
  border-radius: 4px;
  color: var(--text-faint);
  font-size: 13px;
  cursor: pointer;
}
.ow-notif-dismiss:hover {
  background: var(--surface-hover);
  color: var(--text);
}

.ow-list {
  flex: 1;
  overflow-y: auto;
  padding: 14px;
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.ow-msg {
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.ow-msg.ow-user { align-items: flex-end; }
.ow-msg.ow-assistant { align-items: flex-start; }
.ow-msg.ow-error .ow-msg-bubble {
  background: var(--err-faint);
  color: var(--err);
  border-color: rgba(255, 77, 109, 0.3);
}

.ow-msg-bubble {
  max-width: 85%;
  padding: 8px 12px;
  border-radius: 12px;
  background: var(--surface);
  border: 1px solid var(--border);
  font-size: 14px;
  line-height: 1.45;
  word-wrap: break-word;
  white-space: normal;
}
.ow-user .ow-msg-bubble {
  background: var(--accent);
  color: #001a22;
  border-color: transparent;
  border-radius: 12px 12px 2px 12px;
}
.ow-assistant .ow-msg-bubble {
  border-radius: 12px 12px 12px 2px;
}
.ow-msg-bubble code {
  font-family: var(--font-mono);
  font-size: 12px;
  background: rgba(255,255,255,0.06);
  padding: 1px 4px;
  border-radius: 4px;
}

.ow-typing {
  display: inline-flex;
  gap: 4px;
  padding: 10px 14px;
}
.ow-typing-dot {
  width: 6px; height: 6px;
  border-radius: 999px;
  background: var(--text-dim);
  animation: ow-blink 1s infinite;
}
.ow-typing-dot:nth-child(2) { animation-delay: 0.2s; }
.ow-typing-dot:nth-child(3) { animation-delay: 0.4s; }
@keyframes ow-blink {
  0%, 60%, 100% { opacity: 0.3; }
  30% { opacity: 1; }
}
@media (prefers-reduced-motion: reduce) {
  .ow-typing-dot { animation: none; opacity: 0.6; }
}

.ow-tool-calls {
  display: flex;
  flex-direction: column;
  gap: 4px;
  margin-top: 2px;
}
.ow-tool-chip {
  display: flex;
  flex-wrap: wrap;          /* description + Open pill wrap to a 2nd line if needed */
  gap: 4px 6px;             /* row-gap 4, col-gap 6 — tighter when stacked */
  align-items: center;
  padding: 4px 10px;
  border-radius: 12px;       /* less pill-y when content stacks */
  background: var(--accent-faint);
  color: var(--text);
  border: 1px solid var(--border);
  font-size: 12px;
  max-width: 100%;          /* fill the message bubble width before wrapping */
}
.ow-tool-chip.ow-error { background: var(--err-faint); }
.ow-tool-name { color: var(--accent); font-weight: 600; }
.ow-tool-sep { color: var(--text-faint); }
.ow-tool-result {
  color: var(--text-dim);
  /* Wrap onto multiple lines instead of ellipsing — the chip
     itself wraps so the Open pill drops onto the next row when
     the description is long. */
  white-space: normal;
  overflow-wrap: anywhere;
  flex: 1 1 auto;
  min-width: 0;
}
.ow-tool-open {
  flex-shrink: 0;
  margin-left: 6px;
  padding: 2px 8px;
  border-radius: 999px;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.02em;
  color: #001a22;
  background: var(--accent);
  text-decoration: none;
  white-space: nowrap;
  transition: background 120ms ease;
}
.ow-tool-open:hover { background: var(--accent-hover); }

.ow-composer {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 10px 12px;
  border-top: 1px solid var(--border);
  background: var(--bg-elevated-2);
}
.ow-input {
  flex: 1;
  border: 1px solid var(--border-strong);
  background: var(--bg);
  color: var(--text);
  border-radius: 8px;
  padding: 8px 12px;
  font: inherit;
  font-size: 14px;
  outline: none;
  transition: border-color 120ms ease;
}
.ow-input:focus { border-color: var(--accent); }
.ow-input:disabled { opacity: 0.6; cursor: not-allowed; }

.ow-mic, .ow-attach {
  background: transparent;
  border: 1px solid var(--border);
  color: var(--text);
  border-radius: 8px;
  width: 34px;
  height: 34px;
  display: inline-grid;
  place-items: center;
  cursor: pointer;
  font-size: 14px;
  transition: background 120ms ease, border-color 120ms ease;
}
.ow-mic:hover, .ow-attach:hover {
  background: var(--surface-hover);
  border-color: var(--border-strong);
}
.ow-mic.ow-active {
  background: var(--accent);
  color: #001a22;
  border-color: var(--accent);
}
/* Three conversational states: listening (recording), thinking
   (waiting for the assistant), speaking (TTS playing). Each has a
   distinct pulse so the operator knows what stage they're at
   without watching the chat. */
.ow-mic.ow-listening { animation: ow-pulse-red 1s infinite; background: #d11; border-color: #d11; color: #fff; }
.ow-mic.ow-thinking  { animation: ow-pulse-amber 1.4s infinite; background: #c87a00; border-color: #c87a00; color: #fff; }
.ow-mic.ow-speaking  { animation: ow-pulse-green 1.6s infinite; background: #1e7a4a; border-color: #1e7a4a; color: #fff; }
@keyframes ow-pulse-red {
  0%, 100% { box-shadow: 0 0 0 0 rgba(220, 30, 30, 0.7); }
  50%      { box-shadow: 0 0 0 8px transparent; }
}
@keyframes ow-pulse-amber {
  0%, 100% { box-shadow: 0 0 0 0 rgba(200, 122, 0, 0.6); }
  50%      { box-shadow: 0 0 0 6px transparent; }
}
@keyframes ow-pulse-green {
  0%, 100% { box-shadow: 0 0 0 0 rgba(30, 122, 74, 0.6); }
  50%      { box-shadow: 0 0 0 6px transparent; }
}
.ow-file { display: none; }

.ow-send {
  background: var(--accent);
  color: #001a22;
  border: none;
  border-radius: 8px;
  padding: 8px 14px;
  font: inherit;
  font-weight: 700;
  font-size: 14px;
  cursor: pointer;
  transition: background 120ms ease;
}
.ow-send:hover:not(:disabled) { background: var(--accent-hover); }
.ow-send:disabled { background: var(--text-faint); cursor: not-allowed; opacity: 0.7; }

@media (max-width: 600px) {
  .ow-panel {
    bottom: 0;
    right: 0;
    left: 0;
    width: auto;
    max-width: 100vw;
    height: 100vh;
    border-radius: 0;
    border-left: 0;
    border-right: 0;
    border-bottom: 0;
  }
  /* On phones the launcher collapses to a circular FAB — text would
     crowd the corner. Star icon alone is enough recognition once the
     user knows what it is. */
  .ow-launcher {
    padding: 14px;
    border-radius: 999px;
    bottom: 18px;
    right: 18px;
  }
  .ow-launcher-text { display: none; }
}

@media (prefers-reduced-motion: reduce) {
  /* Keep the look — strong layered glow — but stop it breathing. */
  .ow-launcher-star,
  .ow-launcher { animation: none; }
}

/* Print: hide the whole assistant. Operator reports are paper-bound,
   the chat surface has no place there. */
@media print {
  .ow-launcher, .ow-panel { display: none !important; }
}

/* ----- Maintenance deep-dive (search, calendar, photos, etc.) -------- */

/* Global maintenance search */
.maint-search-wrap { position: relative; }
.maint-search-input {
  width: 320px;
  max-width: 60vw;
  font-size: 13px;
  padding: 6px 10px;
  border: 1px solid var(--border, rgba(0, 0, 0, 0.18));
  border-radius: 6px;
  background: var(--surface, #fff);
}
.maint-search-results {
  position: absolute;
  top: 100%;
  right: 0;
  margin-top: 4px;
  width: 420px;
  max-width: 90vw;
  max-height: 60vh;
  overflow-y: auto;
  background: var(--surface, #fff);
  border: 1px solid var(--border, rgba(0, 0, 0, 0.18));
  border-radius: 6px;
  box-shadow: 0 6px 24px rgba(0, 0, 0, 0.08);
  z-index: 50;
}
.search-hit {
  display: grid;
  grid-template-columns: 70px 1fr;
  grid-template-rows: auto auto;
  gap: 2px 8px;
  padding: 8px 12px;
  border-bottom: 1px solid var(--border, rgba(0, 0, 0, 0.06));
  text-decoration: none;
  color: inherit;
}
.search-hit:hover { background: var(--surface-hover, rgba(0, 0, 0, 0.03)); }
.search-hit:last-child { border-bottom: 0; }
.search-hit-kind {
  grid-row: 1 / 3;
  align-self: center;
  font-size: 10px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.5px;
  padding: 2px 8px;
  border-radius: 10px;
  text-align: center;
}
.search-hit-kind.kind-asset    { background: #e0e7ff; color: #3730a3; }
.search-hit-kind.kind-machine  { background: #fef3c7; color: #92400e; }
.search-hit-kind.kind-workorder{ background: #ddd6fe; color: #5b21b6; }
.search-hit-title { font-size: 14px; font-weight: 500; }
.search-hit-sub { font-size: 12px; }

/* ---- Calendar (month-at-a-time) ----------------------------------
   Replaced the original 12-month horizontal strip — it overflowed
   the page (~2400px wide) and the dot-only events made the operator
   click every day to find out what was due. New shape: one month
   filling the panel, with two visible event titles per day plus a
   "+N more" pill, and prev/next month nav in the panel header. */
.cal-panel { overflow: visible; }
.cal-header { flex-wrap: wrap; gap: 12px; }
.cal-legend { gap: 12px; }
.cal-month-big {
  display: grid;
  grid-template-columns: repeat(7, minmax(0, 1fr));
  gap: 6px;
  width: 100%;
}
.cal-dow-big {
  font-size: 11px;
  font-weight: 600;
  color: var(--text-dim, #888);
  text-align: left;
  padding: 4px 6px;
  text-transform: uppercase;
  letter-spacing: 0.04em;
}
.cal-day-big {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 4px;
  min-height: 88px;
  padding: 6px 8px;
  /* Default cell = bright surface so future-date numbers are
     legible against it without any contrast tricks. Past cells
     dim themselves below. */
  background: var(--bg-elevated-2, #1c1d22);
  border: 1px solid rgba(255, 255, 255, 0.14);
  border-radius: 6px;
  text-align: left;
  font: inherit;
  /* Force --text rather than `inherit` — buttons in some browsers
     fall back to ButtonText (system gray) which is what was making
     future-day numbers look dim against the dark surface. */
  color: var(--text);
  cursor: default;
  transition: border-color var(--t-fast, 120ms),
              background var(--t-fast, 120ms);
}
.cal-day-big.cal-day-empty { visibility: hidden; }
/* Past = dim (sunk into the panel), future = full brightness. The
   user explicitly asked to invert from the prior treatment — past
   should fade into the background, the days ahead should be the
   ones jumping out. */
.cal-day-big.cal-day-past {
  background: transparent;
  border-color: rgba(255, 255, 255, 0.04);
}
.cal-day-big.cal-day-past .cal-day-big-num { color: var(--text-faint); }
.cal-day-big.cal-day-past .cal-day-big-event { opacity: 0.6; }
/* Today = accent ring + slight glow so it pops without redrawing
   the cell's surface. */
.cal-day-big.cal-day-today {
  border-color: var(--accent, #07bbf1);
  box-shadow: 0 0 0 2px var(--accent, #07bbf1),
              0 0 12px rgba(7, 187, 241, 0.35);
}
.cal-day-big.cal-day-today .cal-day-big-num {
  color: var(--accent, #07bbf1);
  font-weight: 700;
}
.cal-day-big.cal-day-has-events { cursor: pointer; }
.cal-day-big.cal-day-has-events:hover {
  border-color: var(--accent, #07bbf1);
}

/* ---- Maintenance achievements strip — HERO TREATMENT --------------
 * Designed to feel like the page's flagship feature, not a footer.
 *
 * Layout: a single wide hero card with a radial-gradient backdrop in
 * brand amber, hero number on the left, secondary stats inline, and
 * the most-recent badge floating right with a soft glow. A separate
 * "My maintenance" pill sits adjacent, accent-bordered, that expands
 * the personal panel below.
 *
 * Visual language:
 *   - Hero stat: 56px tabular-numerals, accent colour, count-up on load
 *   - Background: layered radial-gradients that subtly shimmer
 *   - Badge: 40px emoji wrapped in an animated radial halo
 *   - Streak: flame emoji + value, with a pulse if > 0
 *   - Hover on badges: tilt + lift + emoji wobble
 *
 * Emoji font stack pinned so colour emoji render properly on Windows /
 * Linux (otherwise PostgreSQL / Chrome may fall back to the monochrome
 * "tofu" rectangle the operator showed in the screenshot).
 */

:root {
  --emoji-font:
    "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol",
    "Noto Color Emoji", "EmojiOne Color", "Twemoji Mozilla", sans-serif;
}

.ach-strip {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: 14px;
  margin-bottom: 16px;
}

/* ---- Hero team card ---- */
.ach-team-card {
  position: relative;
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: center;
  gap: 24px;
  padding: 18px 22px;
  border-radius: 14px;
  /* Three layered backgrounds:
     1. Top-left accent halo (warm amber bloom)
     2. Right-side accent-faint wash for depth
     3. Base panel surface */
  background:
    radial-gradient(420px 220px at 0% 0%, rgba(7, 187, 241, 0.22) 0%, transparent 60%),
    radial-gradient(560px 280px at 100% 100%, rgba(7, 187, 241, 0.10) 0%, transparent 70%),
    linear-gradient(180deg, var(--bg-elevated), var(--bg-elevated-2));
  border: 1px solid var(--border);
  box-shadow:
    0 12px 32px rgba(0, 0, 0, 0.35),
    inset 0 0 0 1px rgba(7, 187, 241, 0.04);
  overflow: hidden;
}
/* Animated shimmer band — sweeps slowly across the card so it
   has a "live" feel without being distracting. Reduced-motion users
   get a static panel. */
.ach-team-card::before {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(
    115deg,
    transparent 30%,
    rgba(7, 187, 241, 0.07) 45%,
    rgba(7, 187, 241, 0.12) 50%,
    rgba(7, 187, 241, 0.07) 55%,
    transparent 70%
  );
  transform: translateX(-100%);
  animation: ach-shimmer 9s ease-in-out infinite;
  pointer-events: none;
}
@keyframes ach-shimmer {
  0%, 30% { transform: translateX(-100%); }
  70%, 100% { transform: translateX(100%); }
}
@media (prefers-reduced-motion: reduce) {
  .ach-team-card::before { animation: none; opacity: 0.3; }
}

.ach-team-left {
  display: grid;
  grid-template-columns: auto 1fr;
  align-items: center;
  gap: 22px;
  min-width: 0;
}

/* Hero number — the centrepiece. Tabular nums so count-up looks crisp.
   Inter, deliberately — a display face's narrow digit metrics render as
   near-invisible thin slabs at heavy weights on Windows Chrome (an
   operator screenshot showed digits as orange tofu rectangles). That is
   also why --font-display is scoped to titles and never to numerals.
   Inter at 800 weight is
   bold enough to read as a feature number and renders consistently. */
.ach-team-hero {
  display: flex;
  flex-direction: column;
  gap: 2px;
  position: relative;
  z-index: 1;
}
.ach-team-hero-label {
  font-size: 10.5px;
  font-weight: 800;
  letter-spacing: 0.10em;
  text-transform: uppercase;
  color: var(--text-dim);
}
.ach-team-hero-value {
  font-family: "Inter", system-ui, -apple-system, sans-serif;
  font-size: 56px;
  font-weight: 800;
  line-height: 1;
  color: var(--accent);
  font-variant-numeric: tabular-nums;
  text-shadow: 0 0 24px rgba(7, 187, 241, 0.35);
  letter-spacing: -0.02em;
}
.ach-team-hero-sub {
  font-size: 11px;
  font-weight: 600;
  color: var(--text-dim);
  letter-spacing: 0.04em;
  margin-top: 2px;
}
.ach-team-hero-sub.ach-delta-up    { color: #6ee7b7; }
.ach-team-hero-sub.ach-delta-down  { color: #fcd34d; }

.ach-team-secondary {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 14px 22px;
  position: relative;
  z-index: 1;
}
.ach-team-mini {
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.ach-team-mini-value {
  font-family: var(--font-mono);
  font-size: 22px;
  font-weight: 700;
  line-height: 1;
  color: var(--text);
  font-variant-numeric: tabular-nums;
}
.ach-team-mini-label {
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-dim);
}
.ach-team-mini.ach-mini-overdue .ach-team-mini-value {
  color: #fcd34d;
}
.ach-team-mini.ach-mini-pms .ach-team-mini-value {
  color: #93c5fd;
}

/* Recent-award chip — floats on the right with a glowing halo around
   the badge emoji. Hovering it slightly lifts it. */
.ach-recent-chip {
  position: relative;
  z-index: 1;
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  align-items: center;
  gap: 14px;
  padding: 14px 18px 14px 14px;
  background: rgba(7, 187, 241, 0.12);
  border: 1px solid rgba(7, 187, 241, 0.40);
  border-radius: 12px;
  box-shadow: 0 6px 18px rgba(7, 187, 241, 0.10);
  transition: transform 200ms ease, box-shadow 200ms ease;
  min-width: 260px;
  max-width: 340px;
}
.ach-recent-chip:hover {
  transform: translateY(-2px);
  box-shadow: 0 10px 24px rgba(7, 187, 241, 0.20);
}
.ach-recent-emoji {
  font-family: var(--emoji-font);
  font-size: 40px;
  line-height: 1;
  width: 56px;
  height: 56px;
  display: grid;
  place-items: center;
  border-radius: 50%;
  background:
    radial-gradient(circle, rgba(7, 187, 241, 0.30) 0%, rgba(7, 187, 241, 0.10) 60%, transparent 80%);
  animation: ach-emoji-glow 3s ease-in-out infinite;
}
@keyframes ach-emoji-glow {
  0%, 100% {
    box-shadow: 0 0 0 0 rgba(7, 187, 241, 0.0),
                inset 0 0 12px rgba(7, 187, 241, 0.10);
  }
  50% {
    box-shadow: 0 0 24px rgba(7, 187, 241, 0.35),
                inset 0 0 16px rgba(7, 187, 241, 0.20);
  }
}
@media (prefers-reduced-motion: reduce) {
  .ach-recent-emoji { animation: none; }
}
.ach-recent-text {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}
.ach-recent-eyebrow {
  font-size: 9.5px;
  font-weight: 800;
  letter-spacing: 0.10em;
  text-transform: uppercase;
  color: var(--accent);
}
.ach-recent-line {
  font-size: 13.5px;
  font-weight: 600;
  color: var(--text);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.ach-recent-tone {
  font-size: 11.5px;
  color: var(--text-dim);
  font-style: italic;
}
.ach-recent-empty {
  font-size: 12px;
  color: var(--text-dim);
}

/* My-maintenance toggle pill — accent-bordered, lifts on hover. */
.ach-me-toggle {
  position: relative;
  display: inline-flex;
  flex-direction: row;
  align-items: center;
  justify-content: center;
  gap: 10px;
  padding: 14px 22px;
  min-width: 180px;
  background:
    linear-gradient(180deg, rgba(255,145,0,0.06) 0%, rgba(255,145,0,0.02) 60%, rgba(255,145,0,0.18) 100%),
    linear-gradient(180deg, var(--bg-elevated), var(--bg-elevated-2));
  border: 1px solid rgba(255,145,0,0.55);
  color: var(--text);
  font: inherit;
  font-size: 14px;
  font-weight: 700;
  letter-spacing: 0.04em;
  border-radius: 14px;
  cursor: pointer;
  transition: transform 160ms ease, box-shadow 200ms ease, border-color 160ms ease, background 160ms ease;
  box-shadow: 0 6px 16px rgba(0,0,0,0.30), 0 -2px 0 rgba(255,145,0,0.10) inset;
}
.ach-me-toggle::before {
  content: "🛠";
  font-size: 18px;
  line-height: 1;
}
.ach-me-toggle:hover {
  transform: translateY(-2px);
  border-color: rgba(255,145,0,0.85);
  box-shadow: 0 12px 24px rgba(0,0,0,0.40),
              0 0 0 1px rgba(255,145,0,0.6),
              0 0 22px rgba(255,145,0,0.18);
}
.ach-me-toggle:active { transform: translateY(0); }
.ach-me-toggle-label { color: var(--text); text-transform: uppercase; }
.ach-me-toggle-caret {
  font-size: 14px;
  color: var(--accent);
  transition: transform 200ms ease;
  animation: ach-me-nudge 2.4s ease-in-out infinite;
}
@keyframes ach-me-nudge {
  0%, 88%, 100% { transform: translateX(0); }
  92% { transform: translateX(3px); }
  96% { transform: translateX(0); }
}
.ach-me-toggle:hover .ach-me-toggle-caret {
  animation: none;
  transform: translateX(2px);
}
.ach-me-toggle[aria-expanded="true"] .ach-me-toggle-caret {
  animation: none;
  transform: rotate(90deg);
}

/* ---- Engineers leaderboard ---------------------------------------
 * Four cards side-by-side, each ranking the top-3 engineers in a
 * metric over the last 30 days. Medal emoji on the left, name in
 * the middle, count on the right.  Empty rows show a muted "—" so
 * the card height is consistent.
 */
.ach-board {
  margin-bottom: 16px;
  padding: 14px 18px 16px;
  background:
    radial-gradient(380px 200px at 100% 0%, rgba(7, 187, 241, 0.05) 0%, transparent 60%),
    linear-gradient(180deg, var(--bg-elevated), var(--bg-elevated-2));
  border: 1px solid var(--border);
  border-radius: 14px;
  box-shadow: 0 8px 22px rgba(0, 0, 0, 0.22);
}
.ach-board-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  margin-bottom: 12px;
}
.ach-board-title {
  font-size: 12px;
  font-weight: 800;
  letter-spacing: 0.10em;
  text-transform: uppercase;
  color: var(--text-dim);
}
.ach-board-grid {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 12px;
}
.ach-board-card {
  display: flex;
  flex-direction: column;
  padding: 12px 14px 14px;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 10px;
  transition: border-color 160ms ease, transform 160ms ease;
}
.ach-board-card:hover {
  border-color: var(--border-strong);
  transform: translateY(-1px);
}
.ach-board-card-head {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 8px;
}
.ach-board-icon {
  font-family: var(--emoji-font);
  font-size: 18px;
  line-height: 1;
}
.ach-board-name {
  font-size: 11.5px;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--text);
}
.ach-board-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.ach-board-row {
  display: grid;
  grid-template-columns: 22px 1fr auto;
  align-items: center;
  gap: 8px;
  padding: 5px 6px;
  border-radius: 6px;
  transition: background 100ms ease;
}
.ach-board-row:hover { background: var(--surface-hover); }
.ach-board-medal {
  font-family: var(--emoji-font);
  font-size: 16px;
  line-height: 1;
  text-align: center;
}
.ach-board-rank-1 { background: rgba(7, 187, 241, 0.06); }
.ach-board-who {
  font-size: 13px;
  font-weight: 500;
  color: var(--text);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  min-width: 0;
}
.ach-board-rank-1 .ach-board-who { font-weight: 700; }
.ach-board-count {
  font-size: 13px;
  color: var(--text);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.ach-board-row-empty .ach-board-medal,
.ach-board-row-empty .ach-board-who,
.ach-board-row-empty .ach-board-count {
  opacity: 0.5;
}

@media (max-width: 980px) {
  .ach-board-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 540px) {
  .ach-board-grid { grid-template-columns: 1fr; }
}

/* My-maintenance expanded panel — slides in, gradient surface. */
.ach-me-panel {
  position: relative;
  margin-bottom: 16px;
  padding: 18px 22px 20px;
  background:
    radial-gradient(420px 200px at 0% 0%, rgba(7, 187, 241, 0.06) 0%, transparent 60%),
    linear-gradient(180deg, var(--bg-elevated), var(--bg-elevated-2));
  border: 1px solid var(--border);
  border-radius: 14px;
  box-shadow: 0 8px 22px rgba(0, 0, 0, 0.25);
  animation: ach-panel-in 220ms cubic-bezier(0.16, 1, 0.3, 1);
}
@keyframes ach-panel-in {
  from { opacity: 0; transform: translateY(-4px); }
  to   { opacity: 1; transform: translateY(0); }
}
@media (prefers-reduced-motion: reduce) {
  .ach-me-panel { animation: none; }
}

.ach-me-stats {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 14px;
}
.ach-me-stat {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding: 14px 16px;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 10px;
  overflow: hidden;
  transition: border-color 160ms ease, transform 160ms ease;
}
.ach-me-stat:hover {
  border-color: var(--border-strong);
  transform: translateY(-1px);
}
.ach-me-stat-value {
  font-family: "Inter", system-ui, -apple-system, sans-serif;
  font-size: 32px;
  font-weight: 800;
  color: var(--text);
  line-height: 1;
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.02em;
  /* Force a positive height even when content is "0" so the label
     below doesn't creep up if a previous render glitch ate the
     baseline. */
  min-height: 32px;
  display: block;
}
.ach-me-stat-label {
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-dim);
}

/* Streak tile — flame icon + accent value. Pulses if > 0. */
.ach-me-streak {
  background:
    radial-gradient(140px 100px at 100% 0%, rgba(7, 187, 241, 0.18) 0%, transparent 70%),
    var(--bg);
  border-color: rgba(7, 187, 241, 0.30);
}
.ach-me-streak .ach-me-stat-value {
  color: var(--accent);
}
/* Flame sits in its own inline span next to the number — using
   a real DOM node not a ::before pseudo so the digit's baseline
   isn't dictated by the emoji's metrics (which differ by OS).
   The JS injects the span on render. */
.ach-me-streak-flame {
  font-family: var(--emoji-font);
  font-size: 22px;
  line-height: 1;
  margin-right: 6px;
  vertical-align: -2px;
  filter: drop-shadow(0 0 6px rgba(7, 187, 241, 0.45));
  animation: ach-flame-flicker 2.4s ease-in-out infinite;
  display: inline-block;
}
.ach-me-streak[data-zero="1"] .ach-me-streak-flame {
  filter: grayscale(1);
  opacity: 0.4;
  animation: none;
}
@keyframes ach-flame-flicker {
  0%, 100% { transform: translateY(0) scale(1); }
  50%      { transform: translateY(-1px) scale(1.08); }
}

.ach-me-badges-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  margin: 18px 0 10px;
}
.ach-me-badges-title {
  font-size: 12px;
  font-weight: 800;
  letter-spacing: 0.10em;
  text-transform: uppercase;
  color: var(--text-dim);
}
.ach-me-badges-count {
  font-size: 11px;
  color: var(--text-faint);
  font-variant-numeric: tabular-nums;
}
.ach-me-badges {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
  gap: 12px;
}

/* Individual badge tile — gradient backdrop + tilting hover */
.ach-badge {
  position: relative;
  display: grid;
  grid-template-columns: 56px minmax(0, 1fr);
  align-items: center;
  gap: 14px;
  padding: 12px 16px 12px 12px;
  background:
    radial-gradient(120px 80px at 0% 0%, rgba(7, 187, 241, 0.10) 0%, transparent 70%),
    linear-gradient(180deg, var(--bg), var(--bg-elevated-2));
  border: 1px solid var(--border);
  border-radius: 12px;
  cursor: help;
  transition: transform 180ms cubic-bezier(0.16, 1, 0.3, 1),
              border-color 200ms ease,
              box-shadow 200ms ease;
  overflow: hidden;
}
.ach-badge:hover {
  transform: translateY(-3px) rotate(-0.5deg);
  border-color: rgba(7, 187, 241, 0.45);
  box-shadow: 0 12px 28px rgba(0, 0, 0, 0.35),
              0 0 24px rgba(7, 187, 241, 0.18);
}
.ach-badge:hover .ach-badge-emoji {
  transform: scale(1.12) rotate(-6deg);
}
.ach-badge-emoji {
  font-family: var(--emoji-font);
  font-size: 32px;
  line-height: 1;
  width: 48px;
  height: 48px;
  display: grid;
  place-items: center;
  border-radius: 50%;
  background:
    radial-gradient(circle at 50% 50%,
      rgba(7, 187, 241, 0.28) 0%,
      rgba(7, 187, 241, 0.10) 60%,
      transparent 80%);
  transition: transform 220ms cubic-bezier(0.16, 1, 0.3, 1);
  filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.45));
}
.ach-badge-text {
  display: flex;
  flex-direction: column;
  min-width: 0;
  gap: 2px;
}
.ach-badge-name {
  color: var(--text);
  font-weight: 700;
  font-size: 13.5px;
  letter-spacing: 0.01em;
}
.ach-badge-tone {
  font-size: 11.5px;
  color: var(--text-dim);
  font-style: italic;
}

/* Empty-cabinet card — friendly, not corporate. Shown when the user
   hasn't earned any badges yet. */
.ach-badges-empty {
  display: grid;
  grid-template-columns: 56px 1fr;
  align-items: center;
  gap: 14px;
  padding: 14px 18px;
  background:
    repeating-linear-gradient(
      135deg,
      transparent 0 12px,
      rgba(255, 255, 255, 0.015) 12px 24px
    ),
    linear-gradient(180deg, var(--bg), var(--bg-elevated-2));
  border: 1px dashed var(--border-strong);
  border-radius: 12px;
}
.ach-badges-empty-icon {
  font-family: var(--emoji-font);
  font-size: 32px;
  line-height: 1;
  text-align: center;
  opacity: 0.65;
}
.ach-badges-empty-title {
  font-weight: 700;
  font-size: 13.5px;
  color: var(--text);
  letter-spacing: 0.01em;
}
.ach-badges-empty-sub {
  font-size: 12px;
  color: var(--text-dim);
  margin-top: 2px;
}

/* ---- Responsive ---- */
@media (max-width: 980px) {
  .ach-team-card {
    grid-template-columns: 1fr;
    grid-template-rows: auto auto;
    gap: 16px;
  }
  .ach-recent-chip { max-width: none; }
  .ach-team-left {
    grid-template-columns: auto auto;
    column-gap: 28px;
    row-gap: 12px;
  }
}
@media (max-width: 720px) {
  .ach-strip { grid-template-columns: 1fr; }
  .ach-me-stats { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .ach-team-hero-value { font-size: 44px; }
  .ach-team-left { grid-template-columns: 1fr; }
  .ach-team-secondary { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

/* Drag-and-drop reschedule visuals.
   `.cal-evt-dragging` dims the source tag while a drag is in flight.
   `.cal-day-drop-hover` highlights the destination cell.
   Tag itself: grab cursor + tiny hint on hover that it's draggable. */
.cal-day-big-event {
  cursor: grab;
  transition: filter 100ms ease, transform 100ms ease;
}
.cal-day-big-event:active { cursor: grabbing; }
.cal-day-big-event:hover {
  filter: brightness(1.15);
}
.cal-evt-dragging {
  opacity: 0.4;
  transform: scale(0.97);
}
.cal-day-big.cal-day-drop-hover {
  border-color: var(--accent, #07bbf1) !important;
  background: rgba(7, 187, 241, 0.10) !important;
  box-shadow: 0 0 0 2px rgba(7, 187, 241, 0.45),
              0 0 16px rgba(7, 187, 241, 0.30);
}

/* Reschedule confirmation popover. Floats over the calendar near
   the drop target. Fixed-positioned + JS clamps to viewport. */
.cal-reschedule-popover {
  position: fixed;
  z-index: 9050;
  width: 320px;
  max-width: calc(100vw - 24px);
  background: var(--bg-elevated);
  border: 1px solid var(--border-strong);
  border-radius: 10px;
  box-shadow: 0 18px 48px rgba(0, 0, 0, 0.55);
  color: var(--text);
  font-family: var(--font-ui);
  overflow: hidden;
  animation: cal-rs-pop-in 140ms cubic-bezier(0.16, 1, 0.3, 1);
}
@keyframes cal-rs-pop-in {
  from { opacity: 0; transform: translateY(-4px) scale(0.98); }
  to   { opacity: 1; transform: translateY(0) scale(1); }
}
.cal-rs-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 10px 12px;
  border-bottom: 1px solid var(--border);
  background: var(--bg-elevated-2);
}
.cal-rs-close {
  background: transparent;
  border: 0;
  color: var(--text-dim);
  width: 24px;
  height: 24px;
  font-size: 14px;
  cursor: pointer;
  border-radius: 4px;
}
.cal-rs-close:hover { color: var(--text); background: var(--surface-hover); }
.cal-rs-body {
  padding: 12px;
  display: flex;
  flex-direction: column;
  gap: 8px;
  font-size: 13px;
}
.cal-rs-line {
  display: flex;
  align-items: center;
  gap: 8px;
}
.cal-rs-title {
  color: var(--text);
  font-weight: 500;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.cal-rs-date {
  color: var(--accent);
  font-family: var(--font-mono);
  font-weight: 600;
}
.cal-rs-keep {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-top: 4px;
  cursor: pointer;
  user-select: none;
}
.cal-rs-time-row {
  display: flex;
  align-items: center;
  gap: 8px;
}
.cal-rs-time {
  padding: 4px 8px;
  background: var(--bg);
  border: 1px solid var(--border-strong);
  border-radius: 6px;
  color: var(--text);
  font: inherit;
  font-size: 12.5px;
}
.cal-rs-time:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}
.cal-rs-actions {
  display: flex;
  justify-content: flex-end;
  gap: 8px;
  padding: 10px 12px;
  border-top: 1px solid var(--border);
  background: var(--bg-elevated-2);
}
.cal-rs-status {
  padding: 0 12px 10px;
  min-height: 14px;
  color: var(--err, #f87171);
}
.cal-day-big-num {
  font-size: 13px;
  font-weight: 600;
  color: var(--text);
}
.cal-day-big-events {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}
.cal-day-big-event {
  display: block;
  font-size: 11px;
  font-weight: 500;
  padding: 1px 6px;
  border-radius: 4px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  color: #1a1a1a;
  border-left: 3px solid transparent;
}
.cal-evt-wo       { background: #c7d2fe; border-left-color: #4338ca; }
.cal-evt-pm       { background: #a7f3d0; border-left-color: #047857; }
.cal-evt-urgent   { background: #fde68a; border-left-color: #b45309; }
.cal-evt-overdue  { background: #fecaca; border-left-color: #b91c1c; }
.cal-day-big-more { font-size: 10px; padding: 0 4px; }

/* Day-detail panel — renders inline below the calendar grid when an
   operator clicks a day. Replaces the old position:fixed drawer
   that floated outside the page footprint. */
.cal-day-inline {
  margin-top: 14px;
  border-top: 1px solid var(--border, rgba(255, 255, 255, 0.08));
  padding-top: 12px;
}
.cal-day-inline-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 8px;
}
.cal-day-inline-body {
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.cal-day-event {
  display: grid;
  grid-template-columns: 40px 1fr auto auto;
  grid-template-rows: auto auto;
  gap: 2px 10px;
  align-items: center;
  padding: 10px 12px;
  border-radius: 6px;
  text-decoration: none;
  color: inherit;
  background: var(--bg-elevated, transparent);
  border: 1px solid var(--border, rgba(255, 255, 255, 0.06));
}
.cal-day-event:hover {
  border-color: var(--accent, #4a90e2);
  background: var(--bg-elevated-2, var(--bg-elevated, #1c1d22));
}
.cal-day-event-kind {
  grid-row: 1 / 3;
  align-self: center;
  font-size: 11px;
  font-weight: 700;
  text-align: center;
  padding: 4px;
  border-radius: 6px;
}
.cal-day-event-kind.kind-wo { background: #c7d2fe; color: #3730a3; }
.cal-day-event-kind.kind-pm { background: #a7f3d0; color: #065f46; }
.cal-day-event-title {
  grid-column: 2;
  font-size: 14px;
  font-weight: 600;
}
.cal-day-event-meta {
  grid-column: 2;
  display: flex;
  align-items: center;
  gap: 6px;
  flex-wrap: wrap;
  font-size: 12px;
}
.cal-day-event-arrow {
  grid-row: 1 / 3;
  font-size: 22px;
  font-weight: 300;
  padding-right: 4px;
}

/* Legend dots — kept as small swatches for the panel-header legend. */
.cal-dot {
  display: inline-block;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  margin-right: 4px;
  vertical-align: middle;
}
.cal-dot-overdue { background: #ef4444; }
.cal-dot-urgent  { background: #f59e0b; }
.cal-dot-wo      { background: #4338ca; }
.cal-dot-pm      { background: #047857; }

/* Tighter day cells on phones — keep all 7 columns visible by
   trading vertical room for horizontal compactness. */
@media (max-width: 720px) {
  .cal-day-big { min-height: 60px; padding: 4px 5px; }
  .cal-day-big-event { font-size: 10px; padding: 0 4px; }
  .cal-dow-big { font-size: 10px; padding: 2px 4px; }
}

/* Asset hero — QR right next to photo */
.asset-hero-photos {
  /* Single source of truth for both tile dimensions. Bump this and
     both photo + QR resize in lockstep. Grid guarantees pixel-perfect
     alignment — flex was leaving the QR ~4px above the photo top
     because the <label> + <a> had different default vertical-align. */
  --asset-tile-size: 240px;
  display: grid;
  grid-template-columns: var(--asset-tile-size) var(--asset-tile-size);
  grid-auto-rows: var(--asset-tile-size);
  gap: 10px;
  flex: 0 0 auto;
}
.asset-hero-photo, .asset-hero-qr {
  margin: 0;
  /* Each cell already constrained by the grid track size, so the
     width/height on the child are belt-and-braces. */
}
.asset-hero-qr {
  flex: 0 0 var(--asset-tile-size);
  width: var(--asset-tile-size);
  height: var(--asset-tile-size);
  box-sizing: border-box;
  border-radius: 8px;
  border: 1px solid var(--border, rgba(0, 0, 0, 0.18));
  background: #fff;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-decoration: none;
  color: inherit;
  transition: border-color 80ms ease;
  padding: 6px;
}
.asset-hero-qr:hover { border-color: var(--accent, #4a90e2); }
.asset-hero-qr img {
  max-width: 100%;
  max-height: 180px;
  display: block;
}
.asset-qr-placeholder {
  font-weight: 700;
  font-size: 28px;
  color: var(--ink-muted, #888);
}
.asset-qr-caption {
  margin-top: 4px;
  font-size: 10px;
  text-align: center;
}
@media (max-width: 480px) {
  .asset-hero-photos {
    flex: 0 0 100%;
    flex-direction: column;
  }
  .asset-hero-photo, .asset-hero-qr {
    width: 100%;
    max-width: 320px;
    height: auto;
    aspect-ratio: 1 / 1;
    align-self: center;
  }
}

/* Photo gallery on WO detail */
.photo-gallery {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
  gap: 10px;
}
.photo-tile {
  border: 1px solid var(--border, rgba(0, 0, 0, 0.12));
  border-radius: 6px;
  overflow: hidden;
  background: var(--surface, #fff);
}
.photo-tile img {
  width: 100%;
  height: 110px;
  object-fit: cover;
  display: block;
}
.photo-meta {
  padding: 4px 8px;
  display: flex;
  align-items: center;
  gap: 6px;
}
.photo-cat {
  font-size: 9px;
  font-weight: 700;
  text-transform: uppercase;
  padding: 1px 6px;
  border-radius: 6px;
}
.photo-cat-before   { background: #fef3c7; color: #92400e; }
.photo-cat-after    { background: #d1fae5; color: #065f46; }
.photo-cat-progress { background: #e0e7ff; color: #3730a3; }

/* Activity feed */
.activity-feed {
  display: flex;
  flex-direction: column;
  gap: 4px;
  border-left: 2px solid var(--border, rgba(0, 0, 0, 0.08));
  padding-left: 12px;
  margin-bottom: 8px;
}
.activity-row {
  display: grid;
  grid-template-columns: 22px 1fr auto;
  gap: 6px;
  padding: 4px 0;
  font-size: 12px;
  align-items: baseline;
}
.activity-icon { font-size: 14px; }
.activity-text code {
  font-family: var(--font-mono, monospace);
  font-size: 11px;
  background: var(--surface-hover, rgba(0, 0, 0, 0.05));
  padding: 1px 4px;
  border-radius: 3px;
}

/* Checklist */
.checklist-item {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 6px 0;
  border-bottom: 1px solid var(--border, rgba(0, 0, 0, 0.06));
  font-size: 13px;
  cursor: pointer;
}
.checklist-item:last-child { border-bottom: 0; }
.checklist-item.checked .checklist-text {
  text-decoration: line-through;
  color: var(--ink-muted, #888);
}

/* Asset reliability stats grid */
.stats-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
  gap: 12px;
}
.stat-cell {
  background: var(--surface-hover, rgba(0, 0, 0, 0.03));
  border-radius: 6px;
  padding: 10px 12px;
}
.stat-label {
  font-size: 11px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.5px;
  color: var(--ink-muted, #888);
}
.stat-value {
  font-size: 20px;
  font-weight: 700;
  font-family: var(--font-mono, monospace);
  margin-top: 2px;
}
.stat-sub {
  font-size: 11px;
  margin-top: 2px;
}

/* Failure-mode horizontal bars */
.failure-row {
  display: grid;
  grid-template-columns: 140px 1fr 40px;
  gap: 8px;
  align-items: center;
  margin: 4px 0;
  font-size: 12px;
}
.failure-code {
  font-family: var(--font-mono, monospace);
  font-size: 11px;
}
.failure-bar-wrap {
  background: var(--surface-hover, rgba(0, 0, 0, 0.05));
  border-radius: 8px;
  height: 10px;
  overflow: hidden;
}
.failure-bar {
  height: 100%;
  background: #4a90e2;
  border-radius: 8px;
  transition: width 200ms ease;
}
.failure-count { text-align: right; }

/* ---- Orion Admin god view (admin.orionwcs.com) -------------------- */

.orion-cust-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
  gap: 14px;
}
.orion-cust-card {
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 14px 16px;
  border: 1px solid var(--border, rgba(0, 0, 0, 0.12));
  border-radius: 8px;
  background: var(--surface, #fff);
  color: inherit;
  text-decoration: none;
  transition: border-color 80ms ease, transform 80ms ease;
}
.orion-cust-card:hover {
  border-color: var(--accent, #4a90e2);
  transform: translateY(-1px);
}
.orion-cust-card-head {
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.orion-cust-name { font-weight: 600; font-size: 16px; }
.orion-cust-stats {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(80px, 1fr));
  gap: 8px;
}
.orion-cust-stat {
  display: flex;
  flex-direction: column;
  padding: 6px 8px;
  background: var(--surface-hover, rgba(0, 0, 0, 0.03));
  border-radius: 4px;
}
.orion-cust-stat-value { font-size: 18px; font-weight: 700; }
.orion-cust-stat-label { font-size: 10px; }
.orion-cust-features {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
}
.orion-feat-chip {
  font-size: 10px;
  font-weight: 600;
  padding: 2px 8px;
  /* was hard-coded light-mode #e0e7ff/#3730a3 — glowing lilac pills on
     the black theme */
  background: var(--accent-faint, rgba(7, 187, 241, 0.08));
  color: var(--accent, #07bbf1);
  border: 1px solid var(--border-accent, rgba(7, 187, 241, 0.4));
  border-radius: 8px;
}

.orion-feature-list { display: flex; flex-direction: column; gap: 8px; }
.orion-agent-status { display: flex; flex-direction: column; gap: 6px; }
.orion-agent-row {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 4px 0;
  font-size: 13px;
}
.orion-feature-row {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  gap: 16px;
  padding: 12px 14px;
  border: 1px solid var(--border, rgba(0, 0, 0, 0.08));
  border-radius: 6px;
  background: var(--surface, #fff);
}
.orion-feature-meta { flex: 1 1 auto; min-width: 0; }
.orion-feature-label {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
  margin-bottom: 4px;
}
.orion-feature-desc { font-size: 12px; line-height: 1.4; }
.orion-feature-state { margin-top: 4px; font-size: 11px; }
.orion-feature-toggle {
  flex: 0 0 auto;
  position: relative;
  display: inline-block;
  width: 44px;
  height: 24px;
}
.orion-feature-toggle input { opacity: 0; width: 0; height: 0; }
.orion-feature-toggle span {
  position: absolute;
  inset: 0;
  cursor: pointer;
  background: #cbd5e1;
  border-radius: 12px;
  transition: background 120ms ease;
}
.orion-feature-toggle span::before {
  content: "";
  position: absolute;
  top: 3px; left: 3px;
  width: 18px; height: 18px;
  background: #fff;
  border-radius: 50%;
  transition: transform 120ms ease;
}
.orion-feature-toggle input:checked + span { background: var(--accent, #4a90e2); }
.orion-feature-toggle input:checked + span::before { transform: translateX(20px); }
.orion-feature-toggle input:disabled + span { opacity: 0.5; cursor: not-allowed; }

/* Customer logo preview tile on the orion-staff customer detail
   page. Fixed-frame box so different aspect-ratio logos all read
   tidily against the same background as the page header. */
.orion-logo-preview {
  width: 200px;
  height: 100px;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--bg-elevated-2, #1c1d22);
  border: 1px solid var(--border-strong, rgba(255, 255, 255, 0.14));
  border-radius: 8px;
  padding: 8px;
  flex-shrink: 0;
}
.orion-logo-preview img {
  max-width: 100%;
  max-height: 100%;
  object-fit: contain;
}

/* SCADA Run/Stop/Fault button group — segmented control. The
   active button gets a colored fill matching the state it triggers
   so the operator can read the current state at a glance. */
.scada-state-buttons {
  display: inline-flex;
  gap: 0;
  border: 1px solid var(--border-strong, rgba(255, 255, 255, 0.14));
  border-radius: var(--r-md, 6px);
  overflow: hidden;
}
.scada-state-btn {
  border: 0;
  border-radius: 0;
  padding: 8px 14px;
  background: var(--bg-elevated, #15161a);
  color: var(--text-dim, #9092a0);
  font-weight: 600;
  font-size: 13px;
  cursor: pointer;
  transition: background var(--t-fast, 120ms),
              color var(--t-fast, 120ms);
}
.scada-state-btn:not(:last-child) {
  border-right: 1px solid var(--border-strong, rgba(255, 255, 255, 0.12));
}
.scada-state-btn:hover { background: var(--bg-elevated-2, #1c1d22); color: var(--text); }
.scada-state-btn.active { color: #fff; }
.scada-state-btn[data-state="run"].active   { background: #1e7a4a; }
.scada-state-btn[data-state="stop"].active  { background: #b35400; }
.scada-state-btn[data-state="fault"].active { background: #b91c1c; }

/* ---- Camera view quick-buttons (Overview / Showroom / Plan) -------
   2026-08-27: replaced the Stop/Run/Fault pill in the same slot, so it
   reuses that pill's segmented geometry. These pick a CAMERA VIEW, not a
   machine state, so the active fill is the neutral accent — colouring it
   red/green/amber here would read as plant status and compete with the
   alarm colours, which are the only things on this page allowed to mean
   run/stop/fault. A view whose scene does not define it (Plan on an asset
   without one) is disabled rather than silently inert. */
.scada-view-buttons {
  display: inline-flex;
  gap: 0;
  border: 1px solid var(--border-strong, rgba(255, 255, 255, 0.14));
  border-radius: var(--r-md, 6px);
  overflow: hidden;
}
.scada-view-btn {
  border: 0;
  border-radius: 0;
  padding: 8px 14px;
  background: var(--bg-elevated, #15161a);
  color: var(--text-dim, #9092a0);
  font-weight: 600;
  font-size: 13px;
  cursor: pointer;
  transition: background var(--t-fast, 120ms),
              color var(--t-fast, 120ms);
}
.scada-view-btn:not(:last-child) {
  border-right: 1px solid var(--border-strong, rgba(255, 255, 255, 0.12));
}
.scada-view-btn:hover:not(:disabled) {
  background: var(--bg-elevated-2, #1c1d22);
  color: var(--text);
}
.scada-view-btn.active {
  background: var(--accent, #2f6feb);
  color: #fff;
}
.scada-view-btn:disabled {
  opacity: 0.4;
  cursor: default;
}

/* ---- Orion-staff impersonation banner ----------------------------
   Sits above the page header on every customer-subdomain page when an
   active "act as" session is in flight. Yellow background = "this is
   not your real session". Sticky so scrolling doesn't lose it. */
.orion-imp-banner {
  position: sticky;
  top: 0;
  z-index: 1000;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 10px 18px;
  background: #fbbf24;
  color: #422006;
  border-bottom: 2px solid #d97706;
  font-size: 13px;
  font-weight: 500;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.18);
}
/* The bar now carries two controls (Knowledge base, Stop impersonating).
   With justify-content: space-between a third child would strand itself in
   the middle, so push the actions together on the right. */
.orion-imp-banner > a { margin-left: auto; }
.orion-imp-banner .mono { color: #1a1a1a; }
.orion-imp-banner strong { font-weight: 700; }
.orion-imp-banner .btn {
  background: #422006;
  color: #fbbf24;
  border-color: #422006;
}
.orion-imp-banner .btn:hover {
  background: #2a1404;
  color: #fff;
}

/* ---- Sites grid (maintenance home) -------------------------------
   Richer tile than the generic .maint-tile — has a hero photo at
   the top, the site name + address below, and counts at the foot.
   Tiles stay roughly square so a row of varying-length names lines
   up. Inherits the card surface so text styles match the rest of
   the app (no white-on-white footguns). */
.maint-sites-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
  gap: 14px;
}
.maint-site-tile {
  display: flex;
  flex-direction: column;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  text-decoration: none;
  color: inherit;
  overflow: hidden;
  transition: transform var(--t-fast), border-color var(--t-fast),
              box-shadow var(--t-fast);
}
.maint-site-tile:hover {
  transform: translateY(-2px);
  border-color: var(--border-strong);
  box-shadow: var(--shadow-card);
}
.maint-site-tile-photo {
  width: 100%;
  height: 140px;
  object-fit: cover;
  display: block;
  background: var(--bg-elevated);
}
.maint-site-tile-photo.placeholder {
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 32px;
  color: var(--text-faint);
}
.maint-site-tile-body {
  padding: 12px 14px 14px;
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.maint-site-tile-name {
  font-weight: 600;
  font-size: 1rem;
  color: var(--text);
}
.maint-site-tile-addr { line-height: 1.35; }
.maint-site-tile-counts { margin-top: 2px; }

/* ---- Machine tile (mirror of site tile, distinct hover accent) ----
 * Used in the site → machines grid. The grid container is
 * `.maint-machines-grid` so the JS can swap the className from the
 * default text-only grid to this photo-tile layout when the photo
 * pipeline is in use. */
.maint-machines-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
  gap: 14px;
}
.maint-machine-tile {
  display: flex;
  flex-direction: column;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  text-decoration: none;
  color: inherit;
  overflow: hidden;
  transition: transform var(--t-fast), border-color var(--t-fast),
              box-shadow var(--t-fast);
  position: relative;
}
.maint-machine-tile:hover {
  transform: translateY(-2px);
  border-color: var(--accent);
  box-shadow: var(--shadow-card), 0 0 18px rgba(7, 187, 241, 0.10);
}
.maint-machine-tile-photo {
  width: 100%;
  height: 140px;
  object-fit: cover;
  display: block;
  background: var(--bg-elevated);
}
.maint-machine-tile-photo.placeholder {
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 36px;
  color: var(--text-faint);
  background:
    radial-gradient(220px 120px at 50% 50%, rgba(7, 187, 241, 0.08) 0%, transparent 70%),
    var(--bg-elevated);
}
.maint-machine-tile-body {
  padding: 12px 14px 14px;
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.maint-machine-tile-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
}
.maint-machine-tile-name {
  font-weight: 600;
  font-size: 1rem;
  color: var(--text);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  min-width: 0;
}
.maint-machine-tile-sub { line-height: 1.35; }
.maint-machine-tile-badge {
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  padding: 2px 7px;
  border-radius: 999px;
  border: 1px solid var(--border);
  flex-shrink: 0;
}
.maint-machine-tile-badge.crit-critical {
  color: #fca5a5;
  background: rgba(239, 68, 68, 0.10);
  border-color: rgba(239, 68, 68, 0.35);
}
.maint-machine-tile-badge.crit-high {
  color: #fcd34d;
  background: rgba(245, 158, 11, 0.10);
  border-color: rgba(245, 158, 11, 0.35);
}
.maint-machine-tile-badge.crit-low {
  color: #93c5fd;
  background: rgba(59, 130, 246, 0.08);
  border-color: rgba(59, 130, 246, 0.30);
}

/* ---- Asset-page breadcrumb with parent-photo avatars -------------
 * Each crumb is an <a> with a 24px round thumbnail next to the name.
 * Hidden by default — the JS unhides only when the image load fires
 * onload (i.e. the parent has a real photo). Failed loads stay
 * hidden via onerror so we never show a broken-image glyph. */
.maint-crumbs {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 4px 8px;
}
.maint-crumb-link {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  text-decoration: none;
  color: var(--text-dim);
  font-size: 12px;
  transition: color 100ms ease;
}
.maint-crumb-link:hover { color: var(--text); }
.maint-crumb-sep { user-select: none; }
.maint-crumb-avatar {
  width: 22px;
  height: 22px;
  border-radius: 50%;
  object-fit: cover;
  border: 1px solid var(--border);
  background: var(--bg-elevated);
  flex-shrink: 0;
}

/* ---- Site hero on the site detail page --------------------------
 * Same layout shape as the machine hero so navigating from a site
 * tile → site detail → machine tile → machine detail feels like one
 * consistent pattern. Slightly larger photo (240×200) since sites
 * are a higher-level entity and operators expect the bigger image
 * to mean "container".
 */
.maint-site-hero {
  display: grid;
  grid-template-columns: 240px 1fr;
  gap: 22px;
  align-items: start;
}
.maint-site-hero-photo {
  position: relative;
  width: 240px;
  height: 200px;
  border-radius: var(--r-lg);
  overflow: hidden;
  background: var(--bg-elevated);
  border: 1px solid var(--border);
}
.maint-site-hero-photo img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
.maint-site-hero-photo .placeholder {
  width: 100%;
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 64px;
  color: var(--text-faint);
  background:
    radial-gradient(220px 120px at 50% 50%, rgba(59, 130, 246, 0.07) 0%, transparent 70%);
}
.maint-site-hero-body { min-width: 0; }

@media (max-width: 720px) {
  .maint-site-hero { grid-template-columns: 1fr; }
  .maint-site-hero-photo { width: 100%; }
}

/* ---- Machine hero on the machine detail page --------------------- */
.maint-machine-hero {
  display: grid;
  grid-template-columns: 220px 1fr;
  gap: 22px;
  align-items: start;
}
.maint-machine-hero-photo {
  position: relative;
  width: 220px;
  height: 180px;
  border-radius: var(--r-lg);
  overflow: hidden;
  background: var(--bg-elevated);
  border: 1px solid var(--border);
}
.maint-machine-hero-photo img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
.maint-machine-hero-photo .placeholder {
  width: 100%;
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 56px;
  color: var(--text-faint);
  background:
    radial-gradient(180px 100px at 50% 50%, rgba(7, 187, 241, 0.08) 0%, transparent 70%);
}
.maint-machine-hero-body { min-width: 0; }

@media (max-width: 720px) {
  .maint-machine-hero { grid-template-columns: 1fr; }
  .maint-machine-hero-photo { width: 100%; }
}

/* Platform-wide KPI strip on the orion dashboard. */
.orion-kpi-strip {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
  gap: 12px;
}
.orion-kpi {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: 10px 12px;
  background: var(--surface-hover, rgba(0, 0, 0, 0.03));
  border-radius: 6px;
}
.orion-kpi-value {
  font-size: 22px;
  font-weight: 700;
  line-height: 1.1;
}
.orion-kpi-label { line-height: 1.2; }

/* Fleet insights table (admin.orionwcs.com/admin/fleet). */
.orion-fleet-table-wrap { overflow-x: auto; }
.orion-fleet-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 13px;
}
.orion-fleet-table th,
.orion-fleet-table td {
  padding: 8px 10px;
  border-bottom: 1px solid var(--border, rgba(0, 0, 0, 0.08));
  vertical-align: top;
  text-align: left;
}
.orion-fleet-table th {
  font-weight: 600;
  color: var(--muted, #64748b);
  font-size: 12px;
  text-transform: uppercase;
  letter-spacing: 0.04em;
}
.orion-fleet-table th.num,
.orion-fleet-table td.num { text-align: right; white-space: nowrap; }
.orion-fleet-table tbody tr:hover {
  background: var(--surface-hover, rgba(0, 0, 0, 0.03));
}
.orion-fleet-sites-strong {
  font-weight: 700;
  color: var(--accent, #4a90e2);
}
.orion-fc-cell { max-width: 320px; }
.orion-fc-chip {
  display: inline-block;
  padding: 1px 8px;
  margin: 1px 2px;
  background: #fef3c7;
  color: #92400e;
  border-radius: 8px;
  font-size: 11px;
  font-weight: 600;
  white-space: nowrap;
}

/* Raise-issue form polish — matches the rest of the platform's
   panel styling rather than relying on browser defaults. */
.raise-issue-panel { border-left: 4px solid #f59e0b; }
.raise-issue-context {
  padding: 8px 12px;
  background: var(--surface-hover, rgba(0, 0, 0, 0.03));
  border-radius: 6px;
  margin-bottom: 14px;
  font-size: 13px;
}
.raise-issue-field {
  display: flex;
  flex-direction: column;
  gap: 6px;
  margin-bottom: 14px;
}
.raise-issue-label {
  font-size: 12px;
  font-weight: 600;
  color: var(--ink-muted, #888);
  text-transform: uppercase;
  letter-spacing: 0.5px;
}
.raise-issue-input,
.raise-issue-textarea {
  width: 100%;
  padding: 10px 12px;
  font-size: 14px;
  font-family: inherit;
  border: 1px solid var(--border, rgba(0, 0, 0, 0.18));
  border-radius: 6px;
  background: var(--surface, #fff);
  color: var(--ink, #222);
  box-sizing: border-box;
  transition: border-color 80ms ease, box-shadow 80ms ease;
}
.raise-issue-input:focus,
.raise-issue-textarea:focus {
  outline: none;
  border-color: var(--accent, #4a90e2);
  box-shadow: 0 0 0 3px rgba(74, 144, 226, 0.15);
}
.raise-issue-textarea {
  resize: vertical;
  min-height: 80px;
}

/* Priority pills — colored, click-to-select. Replaces the native
   <select> which felt out of place against the rest of the UI. */
.priority-pills {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}
.priority-pill {
  appearance: none;
  border: 1px solid var(--border, rgba(0, 0, 0, 0.18));
  background: var(--surface, #fff);
  color: var(--ink, #222);
  font-size: 12px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.5px;
  padding: 6px 14px;
  border-radius: 16px;
  cursor: pointer;
  transition: all 80ms ease;
}
.priority-pill:hover { background: var(--surface-hover, rgba(0, 0, 0, 0.04)); }
.priority-pill.active { box-shadow: 0 0 0 2px rgba(74, 144, 226, 0.18); }
/* Active colour scheme matches the wo-priority-pill palette so the
   form, kanban, and lists tell the same colour story. */
.priority-pill[data-priority="low"].active {
  background: #f3f4f6; color: #4b5563; border-color: #4b5563;
}
.priority-pill[data-priority="medium"].active {
  background: #e0e7ff; color: #3730a3; border-color: #3730a3;
}
.priority-pill[data-priority="high"].active {
  background: #fef3c7; color: #92400e; border-color: #92400e;
}
.priority-pill[data-priority="urgent"].active {
  background: #fee2e2; color: #991b1b; border-color: #991b1b;
}

/* Due-by row: native datetime input + small quick-set chips. */
.raise-issue-due-row {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  align-items: center;
}
.raise-issue-due-input {
  width: auto;
  min-width: 220px;
  flex: 0 0 auto;
}
.due-quicks {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
}
.due-quick {
  appearance: none;
  border: 1px solid var(--border, rgba(0, 0, 0, 0.18));
  background: var(--surface, #fff);
  color: var(--ink-muted, #555);
  font-size: 11px;
  padding: 4px 10px;
  border-radius: 12px;
  cursor: pointer;
  transition: all 80ms ease;
}
.due-quick:hover {
  background: var(--accent, #4a90e2);
  color: #fff;
  border-color: var(--accent, #4a90e2);
}
.due-quick-clear { color: var(--ink-muted, #888); font-style: italic; }
.due-quick-clear:hover {
  background: var(--surface-hover, rgba(0, 0, 0, 0.06));
  color: var(--ink, #222);
  border-color: var(--border, rgba(0, 0, 0, 0.18));
}

.raise-issue-actions {
  display: flex;
  gap: 12px;
  align-items: center;
  margin-top: 16px;
  padding-top: 12px;
  border-top: 1px solid var(--border, rgba(0, 0, 0, 0.06));
}

/* Asset photo dropzone — clickable when admin/engineer */
.asset-hero-photo {
  cursor: pointer;
  position: relative;
  overflow: hidden;
}
.asset-hero-photo img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
.asset-photo-overlay {
  position: absolute;
  bottom: 0;
  left: 0;
  right: 0;
  background: rgba(0, 0, 0, 0.55);
  color: #fff;
  font-size: 11px;
  text-align: center;
  padding: 3px 0;
  opacity: 0;
  transition: opacity 80ms ease;
}
.asset-hero-photo:hover .asset-photo-overlay { opacity: 1; }

/* Compatible-spares row on asset page */
.asset-part-row {
  display: grid;
  grid-template-columns: 100px 1fr auto auto auto;
  gap: 8px;
  align-items: center;
  padding: 6px 0;
  border-bottom: 1px solid var(--border, rgba(0, 0, 0, 0.06));
  font-size: 13px;
}
.asset-part-row:last-child { border-bottom: 0; }
.asset-part-row.low { background: rgba(254, 226, 226, 0.3); }
.asset-part-name { font-weight: 500; }

/* Parts inventory */
.row-low-stock {
  background: rgba(254, 226, 226, 0.4);
}
.part-thumb {
  width: 44px;
  height: 44px;
  object-fit: cover;
  border-radius: 4px;
  border: 1px solid var(--border, rgba(0, 0, 0, 0.12));
  display: block;
}
.part-thumb-placeholder {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  border-radius: 4px;
  background: var(--surface-hover, rgba(0, 0, 0, 0.04));
  border: 1px dashed var(--border, rgba(0, 0, 0, 0.18));
  color: var(--ink-muted, #888);
  font-size: 18px;
}

/* Tight click-to-expand row */
.part-row {
  cursor: pointer;
  transition: background 80ms ease;
}
.part-row:hover { background: var(--surface-hover, rgba(0, 0, 0, 0.04)); }
.part-row.expanded { background: var(--surface-hover, rgba(0, 0, 0, 0.06)); }
.part-row-name { font-weight: 500; }
.part-row-icon {
  margin-left: 6px;
  font-size: 12px;
  opacity: 0.7;
}
.part-row-chevron {
  text-align: right;
  font-size: 18px;
  transition: transform 120ms ease;
}
.part-row.expanded .part-row-chevron {
  transform: rotate(90deg);
  display: inline-block;
}

/* Inline editor expansion */
.part-row-expand > td {
  background: var(--surface-hover, rgba(0, 0, 0, 0.03));
  padding: 14px 16px;
}
.part-editor {
  display: grid;
  grid-template-columns: 200px 1fr;
  gap: 18px;
  align-items: start;
}
@media (max-width: 720px) {
  .part-editor { grid-template-columns: 1fr; }
}
.part-editor-photo-col {
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.part-editor-photo-wrap {
  position: relative;
  display: block;
  cursor: pointer;
  border-radius: 6px;
  overflow: hidden;
  background: var(--surface, #fff);
  border: 1px solid var(--border, rgba(0, 0, 0, 0.12));
}
.part-edit-photo {
  width: 100%;
  aspect-ratio: 1;
  object-fit: cover;
  display: block;
}
.part-edit-photo.placeholder {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  color: var(--ink-muted, #aaa);
  font-size: 32px;
  gap: 6px;
}
.part-edit-overlay {
  position: absolute;
  inset: auto 0 0 0;
  padding: 4px;
  text-align: center;
  font-size: 11px;
  color: #fff;
  background: rgba(0, 0, 0, 0.55);
  opacity: 0;
  transition: opacity 80ms ease;
}
.part-editor-photo-wrap:hover .part-edit-overlay { opacity: 1; }
.part-edit-photo.placeholder + .part-edit-overlay {
  opacity: 1;
  background: rgba(0, 0, 0, 0.35);
}
.part-editor-datasheet {
  display: flex;
  flex-direction: column;
  gap: 4px;
  font-size: 12px;
}

.part-editor-fields { min-width: 0; }
.part-editor-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
  gap: 10px;
}
.part-editor-grid label,
.part-editor-fields label {
  display: flex;
  flex-direction: column;
  font-size: 12px;
}
.part-editor-grid input,
.part-editor-fields textarea,
.part-editor-fields input,
.part-editor-fields select {
  font-size: 13px;
  padding: 5px 8px;
  border: 1px solid var(--border, rgba(0, 0, 0, 0.18));
  border-radius: 4px;
  background: var(--surface, #fff);
  font-family: inherit;
}
.part-editor-grid input:disabled,
.part-editor-fields textarea:disabled,
.part-editor-fields input:disabled {
  background: var(--surface-hover, rgba(0, 0, 0, 0.04));
  color: var(--ink-muted, #888);
  cursor: not-allowed;
}
.part-editor-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  align-items: center;
  margin-top: 12px;
  padding-top: 10px;
  border-top: 1px solid var(--border, rgba(0, 0, 0, 0.06));
  font-size: 12px;
}
.part-editor-divider {
  color: var(--ink-muted, #aaa);
  margin: 0 4px;
}
.part-status.err { color: #f87171; }

/* Stock-adjust panel — visually distinct from the field editor so
   it's obvious that "Save details" doesn't touch stock and "Apply
   stock change" is a separate action. */
.part-stock-block {
  margin-top: 12px;
  padding: 12px 14px;
  background: var(--bg-elevated-2, #1c1d22);
  border: 1px solid var(--border-strong, rgba(255, 255, 255, 0.12));
  border-radius: 6px;
}
.part-stock-header {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  margin-bottom: 10px;
}
.part-stock-quick {
  display: flex;
  gap: 6px;
  align-items: center;
  margin-bottom: 8px;
  flex-wrap: wrap;
}
.part-stock-row {
  display: flex;
  gap: 8px;
  align-items: center;
  flex-wrap: wrap;
}
.part-stock-row .part-restock-delta { flex: 1 1 220px; min-width: 180px; }
.part-stock-row .part-restock-reason { flex: 0 1 240px; min-width: 200px; }
.part-stock-current { font-size: 14px; font-weight: 600; color: var(--text); }

/* Delete part — pushed to its own row, ghost-styled, tinted red on
   hover so accidental clicks are unlikely. */
.part-danger-row {
  margin-top: 14px;
  padding-top: 10px;
  border-top: 1px dashed var(--border, rgba(255, 255, 255, 0.08));
  text-align: right;
}
.part-danger-row .part-delete:hover {
  border-color: #b91c1c;
  color: #fca5a5;
}
.badge-warn {
  display: inline-block;
  font-size: 10px;
  font-weight: 700;
  text-transform: uppercase;
  background: #fee2e2;
  color: #991b1b;
  padding: 1px 6px;
  border-radius: 8px;
  margin-left: 6px;
}

/* Asset criticality colour borders on tiles */
.maint-tile[data-criticality="critical"] { border-left: 4px solid #ef4444; }
.maint-tile[data-criticality="high"]     { border-left: 4px solid #f59e0b; }
.maint-tile[data-criticality="medium"]   { border-left: 4px solid #4a90e2; }
.maint-tile[data-criticality="low"]      { border-left: 4px solid #94a3b8; }

/* Print stylesheet — clean WO printout for paper sites */
@media print {
  .page-header, .ow-launcher, .ow-panel,
  .wo-actions, #btn-print-wo,
  #comment-form, .activity-feed,
  #photo-input, .panel-header .btn,
  .maint-search-wrap {
    display: none !important;
  }
  .panel {
    border: 1px solid #ccc !important;
    page-break-inside: avoid;
  }
  body { background: #fff !important; color: #000 !important; }
}

/* ----- Maintenance / CMMS module (Phases A + B + C) ---------------- */
/* KPI variants for the planner dashboard */
.kpi-card.kpi-warn-active { border-left: 4px solid #f59e0b; }
.kpi-card.kpi-critical-active { border-left: 4px solid #ef4444; }
.kpi-card.kpi-warn { border-left: 4px solid #f59e0b; }

/* Planner three-column responsive grid for Today / Upcoming / Recent */
.planner-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  gap: 12px;
  margin-top: 12px;
}

/* Work-order row — used in lists, kanban cards, asset history */
.wo-row {
  display: block;
  padding: 10px 12px;
  margin-bottom: 6px;
  border: 1px solid var(--border, rgba(0, 0, 0, 0.12));
  border-radius: 6px;
  background: var(--surface, #fff);
  color: inherit;
  text-decoration: none;
  transition: border-color 80ms ease, background 80ms ease;
}
.wo-row:hover {
  border-color: var(--accent, #4a90e2);
  background: var(--surface-hover, rgba(0, 0, 0, 0.03));
}
/* Priority left-border tint — urgent jumps out, low fades into the
   list. Matches the colour palette of the priority pills. */
.wo-row[data-priority="urgent"] { border-left: 4px solid #ef4444; }
.wo-row[data-priority="high"]   { border-left: 4px solid #f59e0b; }
.wo-row[data-priority="medium"] { border-left: 4px solid transparent; }
.wo-row[data-priority="low"]    { border-left: 4px solid transparent; opacity: 0.85; }
.wo-row-head {
  display: flex;
  align-items: center;
  gap: 6px;
  flex-wrap: wrap;
  margin-bottom: 4px;
}
.wo-row-title {
  font-size: 14px;
  font-weight: 500;
}
.wo-row-sub {
  margin-top: 2px;
}
.wo-number {
  font-size: 11px;
  font-weight: 700;
  color: var(--ink-muted, #666);
}
.wo-overdue {
  font-size: 10px;
  font-weight: 700;
  text-transform: uppercase;
  color: #b91c1c;
  background: #fee2e2;
  padding: 1px 6px;
  border-radius: 8px;
}

/* Status / priority pills — Fiix-style colour coding */
.wo-status-pill, .wo-priority-pill {
  display: inline-block;
  font-size: 10px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.5px;
  padding: 1px 8px;
  border-radius: 10px;
}
.wo-status-open       { background: #e0e7ff; color: #3730a3; }
.wo-status-assigned   { background: #fef3c7; color: #92400e; }
.wo-status-in_progress{ background: #ddd6fe; color: #5b21b6; }
.wo-status-completed  { background: #dcfce7; color: #166534; }
.wo-status-cancelled  { background: #f3f4f6; color: #4b5563; }
.wo-status-scheduled  { background: #ecfeff; color: #155e75; }

.wo-priority-low      { background: #f3f4f6; color: #4b5563; }
.wo-priority-medium   { background: #e0e7ff; color: #3730a3; }
.wo-priority-high     { background: #fef3c7; color: #92400e; }
.wo-priority-urgent   { background: #fee2e2; color: #991b1b; }

/* Kanban */
.kanban {
  display: grid;
  grid-template-columns: repeat(4, minmax(220px, 1fr));
  gap: 12px;
  overflow-x: auto;
}
.kanban-col {
  background: var(--surface-hover, rgba(0, 0, 0, 0.03));
  border-radius: 8px;
  padding: 10px;
  min-height: 240px;
}
.kanban-col-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  font-size: 13px;
  font-weight: 600;
  margin-bottom: 8px;
  padding: 0 4px;
}
.kanban-col-count {
  font-size: 11px;
  color: var(--ink-muted, #888);
  background: var(--surface, #fff);
  border-radius: 10px;
  padding: 1px 8px;
}
.kanban-col-body .wo-row {
  margin-bottom: 8px;
}
/* Drag-and-drop reclassify (kanban only). The card lifts and fades
   while held; the destination column highlights a dashed border so
   the operator knows it's accepting the drop. Illegal targets get
   no feedback (the JS doesn't preventDefault), so the browser shows
   the "no drop" cursor naturally. */
.kanban-col-body .wo-row[draggable="true"] { cursor: grab; }
.kanban-col-body .wo-row.dragging {
  opacity: 0.45;
  cursor: grabbing;
  transform: scale(0.98);
}
.kanban-col.drop-target {
  outline: 2px dashed var(--accent, #4a90e2);
  outline-offset: -4px;
  background: var(--accent-faint, rgba(74, 144, 226, 0.08));
}
@media (max-width: 768px) {
  .kanban {
    grid-template-columns: 1fr;
  }
}

/* Asset hero — actions column added in Phase B */
.asset-hero-actions {
  display: flex;
  flex-direction: column;
  gap: 6px;
  align-self: flex-start;
}

/* Asset doc rows */
.asset-doc-row {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 6px 0;
  border-bottom: 1px solid var(--border, rgba(0, 0, 0, 0.06));
  font-size: 13px;
}
.asset-doc-row:last-child { border-bottom: 0; }
.asset-doc-title { flex: 1 1 auto; }

/* PM rows */
.pm-row {
  padding: 8px 0;
  border-bottom: 1px solid var(--border, rgba(255, 255, 255, 0.06));
  font-size: 13px;
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 12px;
}
.pm-row:last-child { border-bottom: 0; }
.pm-row-summary { flex: 1 1 auto; min-width: 0; }
.pm-row-name { font-weight: 500; }
.pm-edit-btn { flex: 0 0 auto; }
.pm-edit-form {
  background: var(--bg-elevated-2, #1c1d22);
  border: 1px solid var(--border-strong, rgba(255, 255, 255, 0.12));
  border-radius: 6px;
  padding: 10px 12px;
}

/* Work-order detail page */
.wo-detail .wo-header {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  gap: 12px;
  margin-bottom: 12px;
  flex-wrap: wrap;
}
.wo-detail .wo-number-row {
  display: flex;
  align-items: center;
  gap: 6px;
  margin-bottom: 6px;
  flex-wrap: wrap;
}
.wo-detail h1 { margin: 0 0 4px; }
.wo-actions {
  display: flex;
  gap: 6px;
  flex-wrap: wrap;
}

/* Comment thread */
.comments-list { display: flex; flex-direction: column; gap: 12px; }
.comment {
  padding: 10px 12px;
  border-radius: 6px;
  background: var(--surface-hover, rgba(0, 0, 0, 0.03));
}
.comment-meta { margin-bottom: 4px; }
.comment-body { font-size: 13px; line-height: 1.5; }

/* ----- Maintenance / CMMS module (Phase A) -------------------------- */
.maint-tile {
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding: 14px 16px;
  border: 1px solid var(--border, rgba(0, 0, 0, 0.12));
  border-radius: 8px;
  background: var(--surface, #fff);
  color: inherit;
  text-decoration: none;
  position: relative;
  transition: border-color 80ms ease, background 80ms ease;
}
.maint-tile:hover {
  border-color: var(--accent, #4a90e2);
  background: var(--surface-hover, rgba(0, 0, 0, 0.03));
}
.maint-tile-title {
  font-weight: 600;
  font-size: 15px;
}
.maint-tile-sub {
  font-size: 12px;
}
.maint-tile-badge {
  position: absolute;
  top: 10px;
  right: 10px;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.5px;
  text-transform: uppercase;
  padding: 2px 8px;
  border-radius: 10px;
  background: #fef3c7;
  color: #92400e;
}

/* Asset detail hero — mobile-first; the QR-scan landing page must be
   readable on a 4-inch phone in poor warehouse lighting. */
.asset-hero {
  display: flex;
  gap: 16px;
  align-items: flex-start;
  margin-bottom: 18px;
  flex-wrap: wrap;
}
.asset-hero-photo {
  /* Use the same tile var as .asset-hero-qr so they always match
     even if we bump --asset-tile-size on the parent. */
  flex: 0 0 var(--asset-tile-size, 240px);
  width: var(--asset-tile-size, 240px);
  height: var(--asset-tile-size, 240px);
  box-sizing: border-box;
  border-radius: 8px;
  background: var(--surface-hover, rgba(0, 0, 0, 0.05));
  border: 1px solid var(--border, rgba(0, 0, 0, 0.12));
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
}
.asset-photo-placeholder {
  font-size: 48px;
  opacity: 0.35;
}
.asset-hero-meta {
  flex: 1 1 240px;
  min-width: 0;
}
.asset-hero-meta h1 {
  margin: 0;
  word-break: break-word;
}
.asset-meta-row {
  margin-top: 4px;
  font-size: 13px;
}
.asset-dl {
  display: grid;
  grid-template-columns: 140px 1fr;
  gap: 6px 16px;
  margin: 0;
}
.asset-dl dt {
  color: var(--ink-muted, #888);
  font-size: 12px;
  align-self: center;
}
.asset-dl dd {
  margin: 0;
  font-size: 13px;
  word-break: break-word;
}
@media (max-width: 480px) {
  .asset-hero-photo {
    flex: 0 0 100%;
    width: 100%;
    height: 200px;
  }
  .asset-dl {
    grid-template-columns: 110px 1fr;
  }
}

/* ----- /admin/kb (Stage 4 admin page) ------------------------------- */
.kb-tier {
  display: inline-block;
  padding: 2px 8px;
  border-radius: 10px;
  font-size: 11px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.5px;
}
.kb-tier-generic        { background: #e6f0ff; color: #1e40af; }
.kb-tier-shared_curated { background: #ecfdf5; color: #047857; }
.kb-tier-customer_private { background: #fef3c7; color: #92400e; }
.kb-admin-page textarea {
  width: 100%;
  font-family: inherit;
  font-size: 13px;
  padding: 6px 8px;
  border: 1px solid var(--border, rgba(0, 0, 0, 0.18));
  border-radius: 4px;
  resize: vertical;
}
.kb-admin-page input[type="text"],
.kb-admin-page select {
  width: 100%;
}

/* ----- Source pills (Stage 3 RAG attribution) -----------------------
   Sit between the assistant bubble and the feedback row. Distinct
   from the tool-call chips: those describe what the assistant DID;
   sources describe what it KNEW going in. */
.ow-sources {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px;
  margin-top: 6px;
  padding-left: 4px;
  font-size: 12px;
}
.ow-sources-label {
  color: var(--ink-muted, #888);
  font-size: 12px;
}
.ow-source-pill {
  /* `--ink` isn't defined in the dark theme — fell back to #222
     and rendered invisible against the dark chat panel. `--text`
     is the actual theme-aware text colour. */
  background: var(--surface-hover);
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: 12px;
  padding: 2px 6px 2px 10px;
  font-size: 11px;
  white-space: nowrap;
  max-width: 280px;
  overflow: hidden;
  text-overflow: ellipsis;
  display: inline-flex;
  align-items: center;
  gap: 4px;
}

/* Per-chunk thumbs (PR #2 — RAG feedback). Tiny inline pair next to
   each source label. Default state is faint; .active marks the
   user's current vote so they can see what they cast. */
.ow-chunk-thumbs {
  display: inline-flex;
  gap: 2px;
  margin-left: 2px;
}
.ow-chunk-thumb {
  background: transparent;
  border: none;
  padding: 0 3px;
  font-size: 11px;
  line-height: 1;
  cursor: pointer;
  opacity: 0.45;
  border-radius: 8px;
}
.ow-chunk-thumb:hover { opacity: 0.85; }
.ow-chunk-thumb.active { opacity: 1; background: var(--surface); }
.ow-chunk-thumb.up.active { color: #2bb673; }
.ow-chunk-thumb.down.active { color: #d44; }

/* ----- Feedback row (Stage 2c) ----------------------------------------
   Sits under each assistant bubble. Subtle by default — operators
   shouldn't feel pressured to rate every reply — but readable on
   hover so the path to "👎 + comment" is one-click obvious when the
   answer was wrong. */
.ow-feedback {
  display: flex;
  align-items: center;
  gap: 6px;
  margin-top: 6px;
  padding-left: 4px;
  font-size: 12px;
  color: var(--ink-muted, #888);
  min-height: 22px;
}
.ow-feedback-btn {
  background: transparent;
  border: 1px solid transparent;
  border-radius: 4px;
  padding: 2px 6px;
  cursor: pointer;
  font-size: 14px;
  line-height: 1;
  opacity: 0.55;
  transition: opacity 80ms ease, border-color 80ms ease, background 80ms ease;
}
.ow-feedback-btn:hover {
  opacity: 1;
  background: var(--surface-hover);
  border-color: var(--border);
}
.ow-feedback-btn:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 1px;
  opacity: 1;
}
/* Theme note: `--ink` was never defined in the dark theme so every
   one of these elements used to fall back to #222 — dark text on a
   dark chat panel, invisible until highlighted. All swapped to
   `--text` / `--text-dim` which are the theme-aware tokens. */
.ow-feedback-comment {
  flex: 1 1 auto;
  min-width: 120px;
  padding: 4px 8px;
  border: 1px solid var(--border);
  border-radius: 4px;
  font-size: 12px;
  font-family: inherit;
  background: var(--surface);
  color: var(--text);
}
.ow-feedback-comment-send,
.ow-feedback-comment-skip {
  border: 1px solid var(--border);
  background: var(--surface);
  color: var(--text);
  border-radius: 4px;
  padding: 3px 8px;
  font-size: 12px;
  cursor: pointer;
}
.ow-feedback-comment-send {
  background: var(--accent);
  color: #001a22;       /* dark text on the warm-amber accent reads cleanest */
  border-color: var(--accent);
}
.ow-feedback-status {
  font-size: 12px;
  color: var(--text-dim);
  font-style: italic;
}
/* "this fixed it" reads more like a positive action than a binary
   thumb — give it a distinct, slightly emphasised look so operators
   notice it as the data-flywheel button. */
.ow-feedback-fixed {
  font-size: 11px;
  padding: 3px 8px;
  border: 1px solid var(--border);
  background: var(--surface);
  color: var(--text);
  border-radius: 12px;
  margin-left: 8px;
  opacity: 0.85;
}
.ow-feedback-fixed:hover {
  /* Light-green confirmation hover — keep the named colours
     literally since the success tone is the same on either
     theme. */
  background: #ecfdf5;
  color: #047857;
  border-color: #047857;
  opacity: 1;
}

/* ----- Atlas routing pill + Go-deeper button ---------------------------
   The pill labels each assistant turn with the user-facing tier
   (Comet ☄ or Nova ✦). Two visual states:
     - pending (route preview hasn't returned yet) → invisible spacer
     - resolved (`-comet` / `-nova` modifier) → coloured chip with text
   On Comet replies a "✦ Go deeper with Nova ↓" button sits under
   the bubble so operators can escalate without retyping. */
.ow-routing-pill {
  display: inline-block;
  font-size: 11px;
  font-weight: 500;
  line-height: 1.2;
  letter-spacing: 0.01em;
  padding: 2px 8px;
  border-radius: 999px;
  margin-bottom: 4px;
  align-self: flex-start;
  opacity: 0;
  transition: opacity 120ms ease;
}
.ow-routing-pill-pending {
  /* Empty placeholder while we wait for the preview — reserve the
     vertical space so the bubble doesn't jump when the pill arrives. */
  min-height: 14px;
  min-width: 60px;
  opacity: 0;
}
.ow-routing-pill-comet {
  background: #fef3c7;          /* warm amber */
  color: #92400e;
  border: 1px solid #fcd34d;
  opacity: 1;
}
.ow-routing-pill-nova {
  background: #ede9fe;          /* cool violet */
  color: #5b21b6;
  border: 1px solid #c4b5fd;
  opacity: 1;
}
.ow-routing-pill-done {
  opacity: 0.85;
}
/* Fallback styling — softer (washed-out amber) so the operator
   notices their query went to a different tier than expected.
   Overrides the colour set by .ow-routing-pill-comet above. */
.ow-routing-pill-fallback {
  background: #f3f0e9;
  color: #7c5e16;
  border-color: #e6dbc1;
  font-style: italic;
}

.ow-go-deeper-row {
  display: flex;
  margin-top: 6px;
  padding-left: 4px;
}
.ow-go-deeper-btn {
  background: transparent;
  color: #5b21b6;
  border: 1px solid #c4b5fd;
  border-radius: 999px;
  padding: 4px 12px;
  font-size: 12px;
  font-weight: 500;
  cursor: pointer;
  transition: background 80ms ease, color 80ms ease;
}
.ow-go-deeper-btn:hover:not(:disabled) {
  background: #ede9fe;
}
.ow-go-deeper-btn:disabled {
  opacity: 0.6;
  cursor: progress;
}
.ow-go-deeper-btn:focus-visible {
  outline: 2px solid #7c3aed;
  outline-offset: 1px;
}

/* ===== Volumetric report ============================================
   Reuses the page's existing panel + chart shells; adds its own KPI
   strip, leaderboard layout, and inline filter row. */
.vol-controls {
  display: flex;
  flex-wrap: wrap;
  gap: 12px 18px;
  align-items: flex-end;
  margin-bottom: 14px;
}
.vol-control { display: inline-flex; flex-direction: column; gap: 4px; min-width: 150px; }
.vol-control-label { color: var(--text-dim); font-size: 12px; letter-spacing: 0.02em; }

.vol-kpis {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
  gap: 12px;
  margin-bottom: 18px;
}
.vol-kpi {
  background: var(--surface-hover);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  padding: 12px 14px;
}
.vol-kpi-value {
  font-size: 1.55rem;
  font-weight: 700;
  color: var(--text);
  letter-spacing: -0.01em;
}
.vol-kpi-label { font-size: 11px; letter-spacing: 0.04em; text-transform: uppercase; }

/* Two-column leaderboard. Collapses to a single column under 720 px so
   each row keeps its bar-chart visible on mobile. */
.vol-leaderboards {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 18px;
  margin-top: 14px;
}
@media (max-width: 720px) {
  .vol-leaderboards { grid-template-columns: 1fr; }
}
.vol-lb-col { min-width: 0; }
.vol-lb-list {
  list-style: none;
  padding: 0;
  margin: 6px 0 0 0;
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.vol-lb-row {
  display: grid;
  grid-template-columns: 28px 1fr auto auto;
  grid-template-areas: "rank label primary secondary"
                       "bar  bar   bar     bar";
  align-items: center;
  gap: 4px 10px;
  padding: 6px 10px;
  background: var(--surface-hover);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  position: relative;
  overflow: hidden;
}
.vol-lb-rank { grid-area: rank; font-weight: 700; color: var(--text-dim); font-size: 0.85rem; }
.vol-lb-label {
  grid-area: label;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  color: var(--text);
  font-weight: 500;
}
.vol-lb-primary   { grid-area: primary;   color: var(--accent); font-weight: 600; }
.vol-lb-secondary { grid-area: secondary; }
/* Bar chart inside each row: a thin gradient strip whose width
   mirrors the ranked value relative to the top of the leaderboard.
   Pure CSS via the --bar-pct custom property set inline. */
.vol-lb-bar {
  grid-area: bar;
  height: 3px;
  width: var(--bar-pct, 0%);
  background: linear-gradient(90deg, var(--accent), rgba(7, 187, 241, 0.25));
  border-radius: 999px;
  margin-top: 2px;
}

/* Filter + show-all controls — sit as the first row inside each
   leaderboard <ul>. Visually distinct from the data rows so the
   operator doesn't confuse the input for a result. */
.vol-lb-controls {
  display: grid;
  grid-template-columns: 1fr auto auto;
  align-items: center;
  gap: 8px;
  padding: 4px 0 6px;
  list-style: none;       /* override the <ul> bullets */
  background: transparent;
  border: 0;
}
.vol-lb-filter {
  padding: 6px 10px;
  background: var(--bg);
  border: 1px solid var(--border-strong);
  border-radius: 6px;
  color: var(--text);
  font: inherit;
  font-size: 12.5px;
  outline: none;
  width: 100%;
  min-width: 0;
}
.vol-lb-filter:focus { border-color: var(--accent); }
.vol-lb-meta {
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}
.vol-lb-toggle {
  background: transparent;
  border: 1px solid var(--border);
  color: var(--text-dim);
  font: inherit;
  font-size: 11.5px;
  font-weight: 600;
  letter-spacing: 0.02em;
  padding: 5px 10px;
  border-radius: 6px;
  cursor: pointer;
  white-space: nowrap;
  transition: color 100ms ease, border-color 100ms ease, background 100ms ease;
}
.vol-lb-toggle:hover {
  color: var(--text);
  border-color: var(--border-strong);
  background: var(--surface-hover);
}
.vol-lb-toggle:empty { display: none; }   /* hide when nothing to expand */

/* Expanded-mode scroll container. The inner <ul.vol-lb-body> renders
   ALL filtered rows; the <li.vol-lb-body-wrap> wraps it with a max
   height so the leaderboard never pushes the rest of the page down. */
.vol-lb-body-wrap {
  list-style: none;
  padding: 0;
  margin: 0;
  max-height: 480px;
  overflow-y: auto;
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  background: var(--bg);
  /* Tighter scrollbar so the rail doesn't dominate. */
  scrollbar-width: thin;
}
.vol-lb-body {
  list-style: none;
  padding: 4px;
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.vol-lb-expanded .vol-lb-row {
  /* Drop the per-row background tint inside the scroll body — the
     container provides the visual frame, and the rows-on-rows look
     gets noisy at 100+ rows. */
  background: transparent;
  border-color: transparent;
}
.vol-lb-expanded .vol-lb-row:hover {
  background: var(--surface-hover);
}

/* Sub-headings inside the volumetric panel — smaller than .panel-title
   because they're already nested inside a panel. */
.panel-subhead {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  margin: 16px 0 6px;
}
.panel-subtitle {
  font-size: 0.9rem;
  font-weight: 600;
  color: var(--text);
}

/* =====================================================================
   Home dashboard v2 — Gridstack-powered, "high-tech" aesthetic.
   ===================================================================== */

.home-page { padding-bottom: 40px; }
.home-toolbar { align-items: flex-end; margin-bottom: 22px; gap: 14px; }
.home-title { margin: 0; }  /* treatment now comes from the shared h1 rule */
.home-title-accent { color: var(--accent); }

/* Empty state — clean panel with a soft glow. */
.home-empty {
  padding: 48px 24px;
  text-align: center;
  border: 1px dashed var(--border);
  border-radius: var(--r-lg);
  background:
    radial-gradient(800px 200px at 50% 0%, rgba(7, 187, 241,0.06), transparent 60%),
    var(--surface);
  margin: 12px 0;
}
.home-empty-icon  { font-size: 2.4rem; margin-bottom: 8px; }
.home-empty-title { font-size: 1.05rem; font-weight: 600; color: var(--text); margin-bottom: 4px; }
.home-empty-sub   { color: var(--text-dim); }

/* ----- Gridstack tweaks --------------------------------------------
   We don't override Gridstack's positioning math, just its skin.
   Cards are rendered inside .grid-stack-item-content > .w-card. */

.grid-stack > .grid-stack-item > .grid-stack-item-content {
  inset: 0;
  overflow: hidden;
  padding: 0;
}

/* Edit-mode visual cue — every widget gets a dashed outline + slight
   tilt-on-grab. View mode is clean: no outline, normal cursor. */
.home-page[data-edit-mode="edit"] .w-card {
  outline: 1px dashed rgba(7, 187, 241, 0.5);
  outline-offset: -1px;
}
.home-page[data-edit-mode="edit"] .w-head {
  cursor: grab;
}
.home-page[data-edit-mode="edit"] .w-head:active { cursor: grabbing; }
.home-page[data-edit-mode="view"] .w-remove { display: none; }

/* Gridstack's own placeholder/resize handles, themed. */
.grid-stack-placeholder > .placeholder-content {
  background: rgba(7, 187, 241, 0.10) !important;
  border: 1px dashed var(--accent) !important;
  border-radius: var(--r-lg) !important;
}
.grid-stack > .grid-stack-item > .ui-resizable-handle {
  background-image: none;
  background: rgba(7, 187, 241, 0.4);
  width: 8px !important;
  height: 8px !important;
  border-radius: 2px;
  opacity: 0;
  transition: opacity 100ms ease;
}
.home-page[data-edit-mode="edit"] .grid-stack-item:hover > .ui-resizable-handle { opacity: 1; }
.home-page[data-edit-mode="edit"] .grid-stack-item:focus-within > .ui-resizable-handle { opacity: 1; }

/* ----- Widget card skin -------------------------------------------- */

.w-card {
  height: 100%;
  background:
    linear-gradient(180deg, rgba(7, 187, 241, 0.04) 0%, transparent 30%),
    var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  overflow: hidden;
  display: flex;
  flex-direction: column;
  min-width: 0;
  position: relative;
  transition: border-color 120ms ease, box-shadow 120ms ease;
}
.w-card::before {
  /* Thin gradient hairline across the top — the "high-tech" cue. */
  content: "";
  position: absolute;
  inset: 0 0 auto 0;
  height: 1px;
  background: linear-gradient(90deg, transparent 0%, var(--accent) 50%, transparent 100%);
  opacity: 0.5;
  pointer-events: none;
}
.w-card:hover {
  border-color: rgba(7, 187, 241, 0.4);
  box-shadow: 0 4px 14px rgba(0, 0, 0, 0.25), 0 0 0 1px rgba(7, 187, 241, 0.1);
}
.w-card-unknown { opacity: 0.7; }

.w-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 8px 14px;
  border-bottom: 1px solid var(--border);
  background:
    linear-gradient(180deg, var(--bg-elevated), var(--surface-hover));
  font-size: 0.78rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--text-dim);
  flex-shrink: 0;
}
.w-title { color: var(--text); }
.w-head-actions { display: flex; gap: 4px; }

.w-body {
  padding: 14px;
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
  flex-direction: column;
  justify-content: center;
  overflow: hidden;
}

.w-ctl {
  background: transparent;
  border: 1px solid transparent;
  color: var(--text-dim);
  border-radius: 4px;
  padding: 0 6px;
  font-size: 1.1rem;
  line-height: 1.4;
  cursor: pointer;
  transition: color 80ms ease, border-color 80ms ease, background 80ms ease;
}
.w-ctl:hover { background: var(--surface-hover); color: var(--text); border-color: var(--border); }
.w-remove:hover { color: var(--err, #ff4d6d); border-color: var(--err, #ff4d6d); }

/* ----- Stat tile -------------------------------------------------- */

.w-stat {
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: flex-start;
  gap: 6px;
  height: 100%;
}
.w-stat-value {
  font-size: clamp(1.8rem, 3.2vw, 3rem);
  font-weight: 700;
  letter-spacing: -0.02em;
  color: var(--text);
  line-height: 1.0;
  text-shadow: 0 0 24px rgba(7, 187, 241, 0.15);
}
.w-stat-frag { font-size: 0.55em; color: var(--text-dim); font-weight: 500; }
.w-stat-label {
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-dim);
}
.w-stat-sub { font-size: 0.78rem; }

.w-fail { color: var(--text-dim); }

/* ----- Clock widget ---------------------------------------------- */
.w-clock { display: flex; flex-direction: column; gap: 4px; }
.w-clock-local {
  font-size: clamp(1.6rem, 3vw, 2.6rem);
  font-weight: 700;
  letter-spacing: 0.04em;
  color: var(--text);
  font-variant-numeric: tabular-nums;
}
.w-clock-utc, .w-clock-date { letter-spacing: 0.03em; }

/* ----- Recent-parcels mini feed --------------------------------- */
.w-feed {
  display: flex;
  flex-direction: column;
  gap: 3px;
  overflow: auto;
  max-height: 100%;
  font-size: 0.85rem;
}
.w-feed-row {
  display: grid;
  grid-template-columns: 60px 1fr auto auto;
  gap: 8px;
  align-items: center;
  padding: 3px 6px;
  background: var(--surface-hover);
  border-radius: 4px;
  border-left: 2px solid transparent;
}
.w-feed-row:hover { background: var(--bg-elevated); }
.w-feed-time { color: var(--text-dim); font-size: 0.78rem; }
.w-feed-bc { color: var(--text); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.w-feed-chute { color: var(--accent); font-weight: 600; font-size: 0.85rem; }
.w-feed-status {
  font-size: 0.65rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  padding: 1px 6px;
  border-radius: 999px;
}
.w-status-ok    { background: rgba(61,220,151,0.15); color: var(--ok, #3ddc97); }
.w-status-warn  { background: rgba(7, 187, 241,0.18); color: var(--accent); }
.w-status-err   { background: rgba(255,77,109,0.18); color: var(--err, #ff4d6d); }
.w-status-muted { background: var(--surface-hover); color: var(--text-dim); }

/* ----- Mini bar list ---------------------------------------------- */
.w-bar-list { list-style: none; padding: 0; margin: 0; display: flex; flex-direction: column; gap: 5px; }
.w-bar-list li {
  display: grid;
  grid-template-columns: 24px 1fr auto;
  grid-template-areas: "rank label val" "bar bar bar";
  gap: 2px 8px;
  padding: 5px 8px;
  background: var(--surface-hover);
  border-radius: 5px;
  border-left: 2px solid transparent;
  transition: border-color 80ms;
}
.w-bar-list li:hover { border-left-color: var(--accent); }
.w-bar-rank  { grid-area: rank;  font-weight: 700; color: var(--text-dim); font-size: 0.8rem; }
.w-bar-label { grid-area: label; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--text); }
.w-bar-val   { grid-area: val;   color: var(--accent); font-weight: 600; font-variant-numeric: tabular-nums; }
.w-bar-bar {
  grid-area: bar;
  height: 3px;
  width: var(--bar-pct, 0%);
  background: linear-gradient(90deg, var(--accent), rgba(7, 187, 241, 0.3));
  border-radius: 999px;
  margin-top: 2px;
  box-shadow: 0 0 4px rgba(7, 187, 241, 0.4);
}

/* ----- 365-day heatmap (portrait) -------------------------------
   Two rows of CSS Grid: a slim header (Mon-Sun) on top, then the
   body that's one row per week. Each row is `[month-label] auto`
   + 7 equal cells. Cells shrink with the widget — the widget is
   sized in cell units (Gridstack), so the grid stays usable from
   3x4 minimum up to 6x10+ if the operator stretches it.
   Most-recent week sits at the top so the eye lands on "now". */
.w-hp { display: flex; flex-direction: column; gap: 4px; height: 100%; }
.w-hp-body { flex: 1 1 auto; display: flex; flex-direction: column; gap: 2px; min-height: 0; }
.w-hp-foot { margin-top: 4px; }

.w-hp-dowrow,
.w-hp-row {
  display: grid;
  grid-template-columns: 22px repeat(7, 1fr);
  gap: 2px;
  align-items: center;
}
.w-hp-dow {
  font-size: 0.62rem;
  letter-spacing: 0.04em;
  color: var(--text-faint);
  text-align: center;
  font-family: var(--font-mono);
  text-transform: uppercase;
}
.w-hp-mlabel {
  font-size: 0.62rem;
  letter-spacing: 0.02em;
  color: var(--text-dim);
  font-family: var(--font-mono);
  font-weight: 500;
  text-transform: uppercase;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.w-hp-cell {
  aspect-ratio: 1 / 1;
  min-height: 9px;
  border-radius: 2px;
  background: var(--bg-elevated);
  transition: transform 80ms, box-shadow 80ms;
}
.w-hp-cell:hover {
  transform: scale(1.6);
  z-index: 2;
  position: relative;
  box-shadow: 0 0 6px var(--accent-glow);
}
.w-hp-empty { visibility: hidden; }

/* Keep the old landscape strip styles around — the /reports page
   still uses them. Renamed to avoid bleeding through to the new
   portrait widget cells above. */
.w-heatmap {
  display: flex;
  flex-wrap: wrap;
  gap: 2px;
  align-items: flex-start;
  align-content: flex-start;
  height: 100%;
  overflow: hidden;
}
.w-heatmap-cell {
  width: 9px;
  height: 9px;
  border-radius: 1px;
  display: inline-block;
  transition: transform 80ms;
}
.w-heatmap-cell:hover { transform: scale(1.6); z-index: 2; position: relative; }

/* ----- Routing grid ---------------------------------------------- */
.w-routing-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(110px, 1fr));
  gap: 4px 8px;
}
.w-routing-cell {
  display: flex;
  align-items: center;
  gap: 4px;
  padding: 4px 8px;
  background: var(--surface-hover);
  border-radius: 4px;
  font-size: 0.85rem;
  font-variant-numeric: tabular-nums;
}
.w-routing-route { color: var(--text); font-weight: 600; }
.w-routing-arrow { color: var(--text-faint); }
.w-routing-chute { color: var(--accent); font-weight: 600; }
.w-routing-foot { margin-top: 10px; }

/* ----- SCADA 3D widget (iframe of /scada?embed=1) --------------- */
/* The widget body is positioned relative so the pop-out link can
   absolute-position over the top-right corner. The iframe fills
   the entire body. */
.w-card-body:has(.w-scada3d-frame) {
  position: relative;
  padding: 0;
  overflow: hidden;
}
.w-scada3d-frame {
  display: block;
  width: 100%;
  /* Stretched to fill below the scene-picker bar (~32px tall). The
     `:has(.w-scada3d-bar)` rule below trims this when the bar is
     present so it lines up with the body fill. */
  height: 100%;
  border: 0;
  background: #000000;     /* matches the 3D viewer's dark stage */
}
/* Scene-picker bar sits above the iframe — small, unobtrusive,
   only consumes ~32px of the widget body so the iframe still has
   most of the real estate. Multiple SCADA widgets each get their
   own picker, picking different scenes for side-by-side ops view. */
.w-scada3d-bar {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 4px 8px;
  background: var(--bg-elevated, #14141a);
  border-bottom: 1px solid var(--border);
  font-size: 0.78rem;
}
.w-scada3d-scene {
  flex: 1;
  background: var(--surface);
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  padding: 3px 6px;
  font-size: 0.78rem;
}
.w-card-body:has(.w-scada3d-bar) .w-scada3d-frame {
  height: calc(100% - 32px);
}
/* When the bar is present, the popout link sits inside it (not
   absolute-over-the-iframe). Reset the absolute positioning. */
.w-scada3d-bar .w-scada3d-popout {
  position: static;
  top: auto;
  right: auto;
  background: transparent;
  border: 1px solid var(--border);
  font-size: 0.7rem;
  padding: 2px 8px;
}
.w-scada3d-popout {
  position: absolute;
  top: 8px;
  right: 8px;
  font-size: 11px;
  font-weight: 600;
  color: var(--text-dim);
  background: rgba(0, 0, 0, 0.55);
  border: 1px solid var(--border);
  padding: 3px 8px;
  border-radius: 6px;
  text-decoration: none;
  letter-spacing: 0.03em;
  transition: color 100ms ease, border-color 100ms ease;
  z-index: 2;
}
.w-scada3d-popout:hover {
  color: var(--accent);
  border-color: var(--border-accent);
}

/* ----- SCADA widget (alarms + insights, no iframe) -------------- */
.w-scada {
  display: flex;
  flex-direction: column;
  gap: 12px;
  height: 100%;
  min-height: 0;
}

/* Header — at-a-glance status. Green pill + "All clear" when no
   alarms; red/amber pill + count when alarms are active. */
.w-scada-head {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 6px 10px;
  border-radius: var(--r-md);
  border: 1px solid var(--border);
  background: var(--surface-hover);
}
.w-scada-head-ok    { border-color: rgba(61, 220, 151, 0.35); }
.w-scada-head-alarm { border-color: rgba(7, 187, 241, 0.35); }
.w-scada-pill {
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  padding: 2px 8px;
  border-radius: 999px;
}
.w-pill-warn { background: rgba(7, 187, 241, 0.16); color: var(--accent); border: 1px solid rgba(7, 187, 241, 0.4); }
.w-scada-head-text { color: var(--text-dim); }

.w-scada-section { display: flex; flex-direction: column; gap: 6px; }
.w-scada-section-title {
  font-size: 0.68rem;
  font-weight: 600;
  letter-spacing: 0.08em;
  color: var(--text-dim);
  text-transform: uppercase;
}

.w-scada-list {
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: 4px;
}

/* Alarm rows — severity bar on the left + symbol + device + msg. */
.w-scada-alarm {
  display: grid;
  grid-template-columns: 22px auto 1fr auto;
  gap: 8px;
  align-items: center;
  padding: 5px 8px;
  border-radius: 4px;
  background: var(--surface-hover);
  border-left: 3px solid var(--border);
}
.w-scada-alarm.w-scada-sev-critical { border-left-color: var(--err, #ff4d6d); }
.w-scada-alarm.w-scada-sev-warning  { border-left-color: var(--accent); }
.w-scada-alarm.w-scada-sev-info     { border-left-color: var(--text-dim); }
.w-scada-sev    { font-size: 0.9rem; line-height: 1; }
.w-scada-device { color: var(--text); font-weight: 600; font-size: 0.85rem; }
.w-scada-msg {
  color: var(--text-dim);
  font-size: 0.85rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.w-scada-time { font-size: 0.72rem; }
.w-scada-more { padding: 4px 8px; }

/* Insight rows — same left bar tint as the alarm, but with a
   title + subtle body. Click target is the row itself; no link
   here today because insights are generated text, not URLs. */
.w-scada-insight {
  display: grid;
  grid-template-columns: 22px 1fr;
  gap: 8px;
  padding: 5px 8px;
  border-radius: 4px;
  background: var(--surface-hover);
  border-left: 3px solid rgba(7, 187, 241, 0.4);
}
.w-scada-insight.w-scada-sev-critical { border-left-color: var(--err); }
.w-scada-insight.w-scada-sev-info     { border-left-color: var(--text-dim); }
.w-scada-insight-body { min-width: 0; }
.w-scada-insight-title { color: var(--text); font-weight: 600; font-size: 0.85rem; }
.w-scada-insight-text  { line-height: 1.3; }

.w-scada-foot { margin-top: auto; text-align: right; }

/* ----- System-health pills ---------------------------------------- */
.w-health { display: flex; flex-direction: column; gap: 8px; }
.w-health-row { display: flex; align-items: center; gap: 10px; }
.w-pill {
  display: inline-block;
  padding: 2px 10px;
  border-radius: 999px;
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  font-variant-numeric: tabular-nums;
  min-width: 50px;
  text-align: center;
}
.w-pill-ok  { background: rgba(61,220,151,0.15); color: var(--ok, #3ddc97); border: 1px solid rgba(61,220,151,0.4); }
.w-pill-err { background: rgba(255,77,109,0.15); color: var(--err, #ff4d6d); border: 1px solid rgba(255,77,109,0.4); }
.w-health-label { color: var(--text); font-size: 0.9rem; }

/* ----- Chart wrappers inside widget bodies ----------------------- */
.chart-wrap-fill { height: 100%; min-height: 120px; position: relative; }

/* ----- Add-widget picker ------------------------------------------ */
.home-picker { margin-bottom: 14px; }
.home-picker-tabs {
  display: flex;
  gap: 4px;
  margin-bottom: 12px;
  border-bottom: 1px solid var(--border);
  padding-bottom: 8px;
}
.home-picker-tab {
  background: transparent;
  border: 1px solid transparent;
  color: var(--text-dim);
  padding: 4px 12px;
  border-radius: var(--r-md);
  cursor: pointer;
  font-size: 0.85rem;
  text-transform: capitalize;
}
.home-picker-tab:hover { background: var(--surface-hover); color: var(--text); }
.home-picker-tab.active {
  background: rgba(7, 187, 241, 0.12);
  border-color: rgba(7, 187, 241, 0.4);
  color: var(--accent);
}
.home-picker-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
  gap: 10px;
}
.home-picker-tile {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 2px;
  padding: 10px 12px;
  background: var(--surface-hover);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  cursor: pointer;
  text-align: left;
  color: var(--text);
  transition: border-color 80ms ease, background 80ms ease, transform 80ms ease;
}
.home-picker-tile:hover {
  border-color: var(--accent);
  background: var(--surface);
  transform: translateY(-1px);
}
.home-picker-cat   { letter-spacing: 0.06em; text-transform: uppercase; color: var(--accent); font-weight: 600; }
.home-picker-title { font-weight: 600; color: var(--text); }
.home-picker-desc  { line-height: 1.3; }
.home-picker-size  { letter-spacing: 0.04em; text-transform: uppercase; opacity: 0.6; font-variant-numeric: tabular-nums; }

/* ----- Per-widget scope chip + dropdown --------------------------
   Sits inside the card header. Chip is clickable in any mode (the
   user shouldn't have to enter Customise just to retarget one
   widget to a different sorter). Site-wide selection shows a
   hollow dot; pinned-to-sorter selection shows a filled accent
   dot — quick visual scan across the dashboard. */
.w-scope-chip {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 1px 8px 1px 6px;
  background: var(--surface-hover);
  border: 1px solid var(--border);
  border-radius: 999px;
  color: var(--text-dim);
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  cursor: pointer;
  transition: background 80ms ease, color 80ms ease, border-color 80ms ease;
  max-width: 160px;
}
.w-scope-chip:hover {
  background: var(--surface);
  color: var(--text);
  border-color: rgba(7, 187, 241, 0.5);
}
.w-scope-label {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.w-scope-caret { color: var(--text-faint); }
.w-scope-dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--accent);
  box-shadow: 0 0 6px rgba(7, 187, 241, 0.6);
  flex-shrink: 0;
}
.w-scope-dot-all {
  background: transparent;
  border: 1.5px solid var(--text-dim);
  box-shadow: none;
}

.w-scope-menu {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  padding: 4px;
  box-shadow: 0 8px 28px rgba(0, 0, 0, 0.45), 0 0 0 1px rgba(7, 187, 241, 0.08);
  display: flex;
  flex-direction: column;
  gap: 1px;
  max-height: 320px;
  overflow-y: auto;
  min-width: 180px;
}
.w-scope-opt {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 6px 10px;
  background: transparent;
  border: none;
  color: var(--text);
  font-size: 0.85rem;
  text-align: left;
  border-radius: 4px;
  cursor: pointer;
}
.w-scope-opt:hover { background: var(--surface-hover); }
.w-scope-opt.is-active {
  background: rgba(7, 187, 241, 0.10);
  color: var(--accent);
}

/* =================================================================
   COMMAND CENTER — orion-staff dashboard at admin.orionwcs.com
   Used at HQ demos to walk customers through "continuous site
   monitoring across every tenant we operate." Visual language: NOC
   meets Casio LCD — big monospace digits, orange glow, scan-line
   banner, breathing status dots. Auto-refreshes every 15s so a demo
   stays alive without intervention.
   ================================================================= */

/* ----- The banner ----- */

.orion-noc-banner {
  position: relative;
  margin: -8px -4px 22px -4px;
  padding: 0;
  border-radius: 12px;
  background:
    radial-gradient(ellipse at top left, rgba(58, 160, 255, 0.16), transparent 55%),
    radial-gradient(ellipse at bottom right, rgba(7, 187, 241, 0.18), transparent 60%),
    linear-gradient(180deg, #0a0c12 0%, #06080d 100%);
  border: 1px solid rgba(7, 187, 241, 0.28);
  box-shadow:
    0 0 0 1px rgba(58, 160, 255, 0.10) inset,
    0 14px 40px -18px rgba(7, 187, 241, 0.35),
    0 4px 18px -8px rgba(0, 0, 0, 0.6);
  overflow: hidden;
}
/* Scan-line texture overlay — subtle, gives the CRT/monitor feel
   without distracting from the content underneath. */
.orion-noc-banner-scanline {
  pointer-events: none;
  position: absolute;
  inset: 0;
  background-image: repeating-linear-gradient(
    0deg,
    transparent 0px,
    transparent 2px,
    rgba(255, 255, 255, 0.025) 3px,
    rgba(255, 255, 255, 0.025) 4px
  );
  mix-blend-mode: screen;
  opacity: 0.7;
  border-radius: inherit;
}
.orion-noc-banner-grid {
  position: relative;
  display: grid;
  grid-template-columns: 1fr 2fr 1fr;
  align-items: center;
  gap: 18px;
  padding: 20px 28px;
}
@media (max-width: 900px) {
  .orion-noc-banner-grid {
    grid-template-columns: 1fr;
    text-align: center;
    gap: 12px;
    padding: 18px 18px;
  }
}

/* Left cell — PLATFORM ONLINE pulsing pill */
.orion-noc-status {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  padding: 8px 14px;
  border: 1px solid rgba(61, 220, 151, 0.40);
  border-radius: 999px;
  background: rgba(61, 220, 151, 0.08);
  color: #6ee0a8;
  font-family: "Share Tech Mono", "JetBrains Mono", monospace;
  font-size: 13px;
  letter-spacing: 0.16em;
  white-space: nowrap;
}
@media (max-width: 900px) {
  .orion-noc-status { justify-self: center; }
}
.orion-noc-status-dot {
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background: #3ddc97;
  box-shadow:
    0 0 0 2px rgba(61, 220, 151, 0.25),
    0 0 12px 2px rgba(61, 220, 151, 0.6);
  animation: orion-noc-pulse 1.8s ease-in-out infinite;
}
@keyframes orion-noc-pulse {
  0%, 100% {
    transform: scale(1);
    box-shadow:
      0 0 0 2px rgba(61, 220, 151, 0.25),
      0 0 12px 3px rgba(61, 220, 151, 0.65);
  }
  50% {
    transform: scale(1.18);
    box-shadow:
      0 0 0 5px rgba(61, 220, 151, 0.10),
      0 0 20px 6px rgba(61, 220, 151, 0.30);
  }
}

/* Centre cell — the Casio-LCD wordmark */
.orion-noc-title {
  text-align: center;
  line-height: 1;
}
.orion-noc-title-main {
  font-family: "Share Tech Mono", "JetBrains Mono", monospace;
  font-size: 30px;
  letter-spacing: 0.20em;
  color: #07bbf1;
  text-shadow:
    0 0 8px rgba(7, 187, 241, 0.65),
    0 0 22px rgba(7, 187, 241, 0.35),
    0 0 1px rgba(255, 255, 255, 0.4);
  /* Subtle flicker so the LCD feels alive without being annoying. */
  animation: orion-noc-flicker 6s ease-in-out infinite;
}
@keyframes orion-noc-flicker {
  0%, 92%, 100% { opacity: 1; }
  93%, 95%      { opacity: 0.86; }
  94%           { opacity: 1; }
}
.orion-noc-title-sub {
  margin-top: 8px;
  font-family: "Share Tech Mono", "JetBrains Mono", monospace;
  font-size: 11.5px;
  letter-spacing: 0.32em;
  color: rgba(207, 208, 216, 0.7);
}
@media (max-width: 900px) {
  .orion-noc-title-main { font-size: 22px; letter-spacing: 0.14em; }
  .orion-noc-title-sub  { font-size: 10px;  letter-spacing: 0.22em; }
}

/* Right cell — live UTC clock */
.orion-noc-clock {
  text-align: right;
  font-family: "Share Tech Mono", "JetBrains Mono", monospace;
  color: #cfd0d8;
  line-height: 1;
}
@media (max-width: 900px) {
  .orion-noc-clock { text-align: center; }
}
.orion-noc-clock-time {
  font-size: 26px;
  letter-spacing: 0.10em;
  color: #6cb6ff;
  text-shadow:
    0 0 8px rgba(108, 182, 255, 0.55),
    0 0 22px rgba(108, 182, 255, 0.25);
}
.orion-noc-clock-date {
  margin-top: 6px;
  font-size: 11px;
  letter-spacing: 0.22em;
  color: rgba(207, 208, 216, 0.55);
}

/* ----- Big LCD KPI strip (the platform totals row) ----- */

.orion-noc-kpi-strip .orion-kpi-value {
  font-family: "Share Tech Mono", "JetBrains Mono", monospace !important;
  font-size: 38px;
  letter-spacing: 0.04em;
  color: #07bbf1;
  text-shadow:
    0 0 6px rgba(7, 187, 241, 0.50),
    0 0 16px rgba(7, 187, 241, 0.22);
}
.orion-noc-kpi-strip .orion-kpi-label {
  font-family: "Share Tech Mono", "JetBrains Mono", monospace;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  font-size: 10.5px;
  color: rgba(207, 208, 216, 0.65) !important;
}

/* ----- Customer command tiles ----- */

.orion-noc-cust-grid {
  /* Slightly wider min-width than the old grid — these cards carry
     site photos + a metric stack, they need elbow room. */
  grid-template-columns: repeat(auto-fill, minmax(380px, 1fr)) !important;
  gap: 16px;
}

.orion-noc-card {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 14px;
  padding: 18px;
  background:
    linear-gradient(180deg, #11131a 0%, #0c0e14 100%) !important;
  border: 1px solid rgba(7, 187, 241, 0.22) !important;
  border-radius: 12px;
  overflow: hidden;
  transition: border-color 160ms ease, transform 160ms ease, box-shadow 160ms ease;
}
.orion-noc-card::before {
  /* Faint corner glow accent — sells the "live panel" feel without
     adding visual weight to the body content. */
  content: "";
  position: absolute;
  inset: 0;
  background: radial-gradient(
    220px 100px at 8% -10%,
    rgba(58, 160, 255, 0.10),
    transparent 60%
  );
  pointer-events: none;
}
.orion-noc-card:hover {
  border-color: rgba(7, 187, 241, 0.55) !important;
  transform: translateY(-2px);
  box-shadow:
    0 18px 36px -22px rgba(7, 187, 241, 0.50),
    0 4px 12px -6px rgba(0, 0, 0, 0.5);
}

.orion-noc-card-head {
  display: grid;
  grid-template-columns: 96px 1fr auto;
  align-items: center;
  gap: 12px;
}
/* Logo container is wide-landscape (96×52) so horizontal wordmarks
   like ORION|HELIOS fit without cropping. object-fit: contain keeps
   the whole logo visible; the dark background fills the slack. */
.orion-noc-logo {
  width: 96px;
  height: 52px;
  border-radius: 8px;
  object-fit: contain;
  padding: 6px 8px;
  background: #0e1119;
  border: 1px solid rgba(255, 255, 255, 0.06);
}
.orion-noc-logo-placeholder {
  display: grid;
  place-items: center;
  padding: 0;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 28px;
  color: #07bbf1;
  background:
    radial-gradient(circle at 30% 30%, rgba(7, 187, 241, 0.20), transparent 60%),
    #0e1119;
  text-shadow: 0 0 12px rgba(7, 187, 241, 0.45);
}
.orion-noc-card-name {
  font-size: 17px;
  font-weight: 700;
  color: #fff;
  letter-spacing: 0.005em;
}
.orion-noc-card-host {
  margin-top: 2px;
  color: rgba(207, 208, 216, 0.55);
  font-size: 11.5px;
}

/* Status pill at the right of the card head — nominal / warning /
   critical. Visual weight scales with severity so a red ALARM pill
   draws the eye across the page. */
.orion-noc-card-status {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 5px 10px;
  border-radius: 999px;
  font-family: "Share Tech Mono", "JetBrains Mono", monospace;
  font-size: 10.5px;
  letter-spacing: 0.16em;
  white-space: nowrap;
  text-transform: uppercase;
}
.orion-noc-card-status.nominal {
  background: rgba(61, 220, 151, 0.10);
  border: 1px solid rgba(61, 220, 151, 0.35);
  color: #6ee0a8;
}
.orion-noc-card-status.nominal .orion-noc-status-dot { background: #3ddc97; }
.orion-noc-card-status.warning {
  background: rgba(255, 210, 63, 0.10);
  border: 1px solid rgba(255, 210, 63, 0.35);
  color: #ffd23f;
}
.orion-noc-card-status.warning .orion-noc-status-dot {
  background: #ffd23f;
  box-shadow:
    0 0 0 2px rgba(255, 210, 63, 0.25),
    0 0 10px 2px rgba(255, 210, 63, 0.6);
}
.orion-noc-card-status.critical {
  background: rgba(255, 77, 109, 0.14);
  border: 1px solid rgba(255, 77, 109, 0.45);
  color: #ff8aa0;
}
.orion-noc-card-status.critical .orion-noc-status-dot {
  background: #ff4d6d;
  box-shadow:
    0 0 0 2px rgba(255, 77, 109, 0.30),
    0 0 14px 3px rgba(255, 77, 109, 0.7);
  animation-duration: 1s;
}

/* Site strip — up to 4 thumbnails with the site name overlaid. The
   "+N" tile shows the remaining count when a customer has more than
   four sites. */
.orion-noc-sites {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(80px, 1fr));
  gap: 8px;
}
.orion-noc-sites-empty {
  padding: 18px 10px;
  text-align: center;
  border: 1px dashed rgba(255, 255, 255, 0.10);
  border-radius: 8px;
  font-style: italic;
  letter-spacing: 0.04em;
}
.orion-noc-site {
  position: relative;
  aspect-ratio: 16 / 10;
  border-radius: 8px;
  overflow: hidden;
  background: #161922;
  border: 1px solid rgba(255, 255, 255, 0.06);
}
.orion-noc-site img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  filter: saturate(1.05) brightness(0.92);
}
.orion-noc-site-noimg {
  width: 100%;
  height: 100%;
  display: grid;
  place-items: center;
  font-family: "Share Tech Mono", monospace;
  font-size: 18px;
  letter-spacing: 0.14em;
  color: rgba(207, 208, 216, 0.65);
  background:
    repeating-linear-gradient(45deg,
      rgba(255, 255, 255, 0.025) 0 6px,
      transparent 6px 12px),
    #161922;
}
.orion-noc-site-name {
  position: absolute;
  bottom: 0; left: 0; right: 0;
  padding: 4px 6px;
  background: linear-gradient(180deg, transparent, rgba(0, 0, 0, 0.78));
  color: #fff;
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.04em;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.orion-noc-site-more {
  display: grid;
  place-items: center;
  font-family: "Share Tech Mono", monospace;
  font-size: 18px;
  color: rgba(7, 187, 241, 0.75);
  background:
    radial-gradient(circle at center, rgba(7, 187, 241, 0.10), transparent 60%),
    #161922;
  letter-spacing: 0.04em;
}

/* LCD metric stack — the load-bearing data row */
.orion-noc-card-metrics {
  display: grid;
  /* SIX metrics — five columns left the sixth wrapping alone on a full
     row (Chris, 2026-09-01: "the customer tiles are shit"). 3x2 is
     clean at every card width. */
  grid-template-columns: repeat(3, 1fr);
  gap: 8px;
  padding: 10px;
  background:
    linear-gradient(180deg, rgba(255, 255, 255, 0.015), rgba(0, 0, 0, 0.25)),
    #0a0c12;
  border: 1px solid rgba(255, 255, 255, 0.04);
  border-radius: 8px;
}
@media (max-width: 520px) {
  .orion-noc-card-metrics {
    grid-template-columns: repeat(3, 1fr);
  }
}
.orion-noc-metric {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  text-align: center;
}
.orion-noc-metric-value {
  font-family: "Share Tech Mono", "JetBrains Mono", monospace !important;
  font-size: 22px;
  letter-spacing: 0.02em;
  color: #07bbf1;
  text-shadow:
    0 0 6px rgba(7, 187, 241, 0.40),
    0 0 14px rgba(7, 187, 241, 0.18);
  line-height: 1;
}
.orion-noc-metric.warn .orion-noc-metric-value {
  color: #ffd23f;
  text-shadow:
    0 0 6px rgba(255, 210, 63, 0.50),
    0 0 14px rgba(255, 210, 63, 0.22);
}
.orion-noc-metric.crit .orion-noc-metric-value {
  color: #ff4d6d;
  text-shadow:
    0 0 6px rgba(255, 77, 109, 0.55),
    0 0 14px rgba(255, 77, 109, 0.25);
  /* Critical metrics pulse so they pull the eye across the screen. */
  animation: orion-noc-flicker 2.4s ease-in-out infinite;
}
.orion-noc-metric-sub {
  font-size: 14px;
  color: rgba(207, 208, 216, 0.55);
  margin-left: 2px;
}
.orion-noc-metric-label {
  font-family: "Share Tech Mono", "JetBrains Mono", monospace;
  font-size: 9.5px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: rgba(207, 208, 216, 0.55);
}

/* Subtle scope tweak — the existing .orion-cust-features chip styles
   stay; we just want them tighter in the NOC card. */
.orion-noc-card .orion-cust-features {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
  padding-top: 2px;
}

/* ----- Live insights strip (replaces feature chips on cards) -----
   Three slots: throughput / quality / latest-activity. Each is an
   icon + a primary monospace value + a small muted qualifier. Sits
   directly below the metric stack so the operator reads metrics →
   insights → continuation top-to-bottom. */
.orion-noc-insights {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 6px;
  padding: 10px 4px 2px;
}
@media (max-width: 520px) {
  .orion-noc-insights { grid-template-columns: 1fr; }
}
.orion-noc-insight {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px 10px;
  border-radius: 8px;
  background: rgba(255, 255, 255, 0.025);
  border: 1px solid rgba(255, 255, 255, 0.05);
}
.orion-noc-insight-icon {
  flex: 0 0 auto;
  font-size: 16px;
  opacity: 0.85;
}
.orion-noc-insight-text {
  display: flex;
  flex-direction: column;
  gap: 1px;
  line-height: 1.2;
  min-width: 0;
}
.orion-noc-insight-text strong {
  font-size: 14px;
  color: #e8e8ee;
  font-weight: 600;
  letter-spacing: 0.01em;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.orion-noc-insight-text strong.mono {
  font-family: "Share Tech Mono", "JetBrains Mono", monospace;
  letter-spacing: 0.02em;
}
.orion-noc-insight-text .small {
  font-size: 10.5px;
  letter-spacing: 0.04em;
}

/* Quality-warning + critical states — colour-shifts when good_read
   drops below 97% / 90%. Sells the "we're watching" story. */
.orion-noc-insight.warn {
  background: rgba(255, 210, 63, 0.06);
  border-color: rgba(255, 210, 63, 0.30);
}
.orion-noc-insight.warn .orion-noc-insight-text strong { color: #ffd23f; }
.orion-noc-insight.crit {
  background: rgba(255, 77, 109, 0.08);
  border-color: rgba(255, 77, 109, 0.38);
}
.orion-noc-insight.crit .orion-noc-insight-text strong {
  color: #ff8aa0;
  animation: orion-noc-flicker 2.2s ease-in-out infinite;
}

/* Dim variant — for idle / dormant / no-data states. Keeps the
   layout intact (operator still sees all three slots) but signals
   "nothing happening" without raising alarm. */
.orion-noc-insight-dim {
  opacity: 0.55;
}
.orion-noc-insight-dim .orion-noc-insight-text strong {
  color: rgba(207, 208, 216, 0.75);
}

/* ===== RAG console (admin) ============================================= */

.rag-console-page .panel-body { padding: 12px 16px; }

.rag-retrieval-card {
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 10px 12px;
  margin-bottom: 10px;
  background: var(--surface);
}
.rag-retrieval-head {
  display: flex;
  gap: 10px;
  align-items: center;
  flex-wrap: wrap;
  margin-bottom: 6px;
  font-size: 12px;
}
.rag-prompt {
  font-size: 13px;
  color: var(--text);
  padding: 6px 8px;
  background: var(--surface-hover);
  border-radius: 6px;
  margin-bottom: 6px;
  white-space: pre-wrap;
}
.rag-completion {
  font-size: 12px;
  margin-top: 6px;
  padding-left: 8px;
  border-left: 2px solid var(--border);
}
.rag-tags { display: flex; gap: 4px; flex-wrap: wrap; margin: 4px 0; }
.rag-tags.inline { display: inline-flex; margin: 0 0 0 6px; }
.rag-tag {
  background: var(--surface-hover);
  border: 1px solid var(--border);
  border-radius: 4px;
  padding: 1px 6px;
  font-size: 11px;
  font-family: "Share Tech Mono", "JetBrains Mono", monospace;
  color: var(--ink-muted, #aaa);
}

.rag-chunks { margin-top: 6px; }
.rag-chunk-row {
  display: grid;
  grid-template-columns: 32px auto auto 1fr;
  gap: 8px;
  align-items: baseline;
  padding: 4px 0;
  border-bottom: 1px dashed var(--border);
  font-size: 12px;
}
.rag-chunk-row:last-child { border-bottom: 0; }
.rag-chunk-idx { color: var(--ink-muted, #888); }
.rag-chunk-title { color: var(--text); font-weight: 500; }
.rag-chunk-snippet {
  font-size: 11px;
  grid-column: 4;
  margin-top: 2px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 100%;
}
.rag-no-chunks { font-size: 12px; padding: 4px 0; }

.rag-tier {
  font-size: 10px;
  padding: 1px 6px;
  border-radius: 4px;
  background: var(--surface-hover);
  font-family: "Share Tech Mono", "JetBrains Mono", monospace;
  letter-spacing: 0.04em;
}
.rag-tier-generic { color: #88b4ff; }
.rag-tier-shared_curated { color: #b48cff; }
.rag-tier-customer_private { color: #ff9b88; }

.rag-score {
  font-family: "Share Tech Mono", "JetBrains Mono", monospace;
  font-size: 12px;
  padding: 1px 6px;
  border-radius: 4px;
  background: var(--surface-hover);
}
.rag-score.strong { color: #2bb673; }
.rag-score.ok { color: #ffd23f; }
.rag-score.weak { color: #d44; }

.rag-status {
  font-size: 10px;
  padding: 1px 6px;
  border-radius: 4px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  background: var(--surface-hover);
}
.rag-status-ok, .rag-status-success { color: #2bb673; }
.rag-status-error, .rag-status-fail { color: #d44; }

.rag-kind {
  font-size: 10px;
  padding: 1px 6px;
  border-radius: 4px;
  background: var(--surface-hover);
  color: var(--ink-muted, #aaa);
  letter-spacing: 0.04em;
}

/* Leaderboard panels — top & bottom side by side */
.rag-chunk-list { display: flex; flex-direction: column; gap: 8px; }
.rag-leaderboard-row {
  display: grid;
  grid-template-columns: auto auto auto 1fr;
  gap: 8px;
  align-items: start;
  padding: 6px 0;
  border-bottom: 1px dashed var(--border);
}
.rag-leaderboard-row:last-child { border-bottom: 0; }
.rag-leaderboard-meat { min-width: 0; }
.rag-leaderboard-meat .rag-chunk-snippet {
  display: block;
  font-size: 11px;
  white-space: normal;
  margin-top: 2px;
  max-height: 2.4em;
  overflow: hidden;
}
.rag-score-pill {
  font-family: "Share Tech Mono", "JetBrains Mono", monospace;
  font-size: 13px;
  font-weight: 600;
  padding: 2px 8px;
  border-radius: 12px;
  background: var(--surface-hover);
  min-width: 36px;
  text-align: center;
}
.rag-score-pill.pos { color: #2bb673; }
.rag-score-pill.neg { color: #d44; }
.rag-vote-counts { font-size: 11px; white-space: nowrap; }

/* Settings sliders */
.rag-sliders {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 10px 24px;
}
.rag-slider-row {
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding: 6px 0;
}
.rag-slider-label {
  display: flex;
  flex-direction: column;
  gap: 2px;
  font-size: 12px;
}
.rag-slider-name { color: var(--text); font-weight: 500; }
.rag-slider-hint { font-size: 11px; }
.rag-slider-controls {
  display: grid;
  grid-template-columns: 1fr 90px;
  gap: 8px;
  align-items: center;
}
.rag-slider-range { width: 100%; }
.rag-slider-number {
  background: var(--surface-hover);
  border: 1px solid var(--border);
  border-radius: 4px;
  padding: 3px 6px;
  font-size: 12px;
  color: var(--text);
  font-family: "Share Tech Mono", "JetBrains Mono", monospace;
  width: 100%;
}

@media (max-width: 760px) {
  .rag-sliders { grid-template-columns: 1fr; }
}

/* Re-embed pipeline panel */
.rag-reembed-row {
  display: grid;
  grid-template-columns: 1fr auto auto;
  gap: 12px;
  align-items: center;
  padding: 8px 0;
  border-bottom: 1px dashed var(--border);
}
.rag-reembed-row:last-child { border-bottom: 0; }
.rag-reembed-meta { min-width: 0; }
.rag-reembed-title {
  color: var(--text);
  font-size: 13px;
  font-weight: 500;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.rag-reembed-sub { font-size: 11px; margin-top: 2px; }
.rag-reembed-reasons { display: flex; gap: 4px; flex-wrap: wrap; }
.rag-reembed-reason {
  font-size: 10px;
  padding: 2px 8px;
  border-radius: 10px;
  background: var(--surface-hover);
  font-family: "Share Tech Mono", "JetBrains Mono", monospace;
  letter-spacing: 0.04em;
}
.rag-reembed-reason.reason-embedder { color: #88b4ff; }
.rag-reembed-reason.reason-chunker { color: #b48cff; }
.rag-reembed-reason.reason-downvote { color: #d44; }

.btn.small {
  padding: 4px 10px;
  font-size: 12px;
}

/* Gaps panel */
.rag-gap-row {
  display: grid;
  grid-template-columns: auto auto auto 1fr auto;
  gap: 10px;
  align-items: center;
  padding: 6px 0;
  border-bottom: 1px dashed var(--border);
  font-size: 12px;
}
.rag-gap-row:last-child { border-bottom: 0; }
.rag-gap-prompt {
  color: var(--text);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  min-width: 0;
}


/* ---------------------------------------------------------------------------
   Sort outcome — why a parcel did not reach its destination chute.
   Added 2026-07-30 for on-site debugging (Chris: "can we get the failure
   readout for each parcel ... highlighted in red or something so it stands out
   from parcel data"). Sits ABOVE the detail grid and is deliberately loud:
   different background, red rule down the left, so the eye never confuses a
   sorter failure with the parcel's own data. Rendered only when
   chute_dest !== chute_actual, so a clean parcel's panel is unchanged.
   --------------------------------------------------------------------------- */
.sort-outcome {
  border: 1px solid rgba(255, 77, 109, 0.35);
  border-left: 4px solid var(--err);
  border-radius: 8px;
  background: var(--err-faint);
  padding: 12px 16px;
  margin-bottom: 18px;
}
.sort-outcome-head {
  display: flex;
  align-items: baseline;
  gap: 10px;
  flex-wrap: wrap;
}
.sort-outcome-tag {
  flex: none;
  font-size: 0.68rem;
  font-weight: 700;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--err);
  border: 1px solid rgba(255, 77, 109, 0.45);
  border-radius: 999px;
  padding: 2px 9px;
}
.sort-outcome-headline {
  font-size: 0.95rem;
  font-weight: 600;
  color: var(--text);
}
/* The "we don't actually know yet" line. Muted on purpose — it is a caveat,
   not a finding, and must not read as louder than the evidence below it. */
.sort-outcome-pending {
  margin: 9px 0 0;
  font-size: 0.82rem;
  line-height: 1.45;
  color: var(--text-faint);
}

/* #663: the PLC told us the cause outright - the strongest statement the panel
   can make, so it reads as confirmed rather than as the faint "pending" note. */
.sort-outcome-definitive {
  margin: 9px 0 0;
  font-size: 0.82rem;
  line-height: 1.45;
  font-weight: 600;
  color: var(--ok, #3fb950);
}
.sort-outcome-facts {
  margin: 9px 0 0;
  padding-left: 18px;
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.sort-outcome-fact {
  font-size: 0.86rem;
  line-height: 1.45;
  color: var(--text-dim, var(--text));
}
/* An established cause reads red; a fact that RULES OUT a cause, or is merely
   contextual, stays neutral — so a scan of the list shows what is proven. */
.sort-outcome-fact--cause { color: var(--err); }
.sort-outcome-fact--cause::marker { color: var(--err); }
.sort-outcome-fact--rules-out::marker,
.sort-outcome-fact--note::marker { color: var(--text-faint); }
/* 2026-09-04: a fact that is REASSURING — the tracking landed on the chute's
   fire centre. Distinct from a plain note because "2 zones off" and "dead on"
   are the difference between a window you can tighten and one you cannot. */
/* A CORRECT sort still gets a panel (2026-09-04) — it carries the firing
   accuracy, which only means anything on a parcel that actually diverted.
   Green edge so a glance never mistakes confirmation for a fault. */
.sort-outcome--ok { border-color: var(--ok, #3ddc97); }
.sort-outcome--ok .sort-outcome-tag { background: var(--ok, #3ddc97); color: #04140d; }
.sort-outcome-fact--good { color: var(--ok, #34d399); }
.sort-outcome-fact--good::marker { color: var(--ok, #34d399); }

/* ---- Sorter doctor ----------------------------------------------------
   An instrument readout, not a dashboard. One question gets answered on
   sight — "do I need to do something?" — so the verdict carries the weight
   and everything else recedes.

   Colour discipline: accent orange stays structural (brand + section
   furniture); green/amber/red mean severity and nothing else. Mixing the two
   is what makes status panels unreadable at a glance.

   This replaced three shared .kpi-cards, which gave equal weight to three
   numbers when only one of them decides anything — and whose 3px rail sat
   inside 20px padding against a 1.9rem value on line-height 1, which read as
   cramped and looked worse with an em-dash placeholder in it. */

/* .eng-section-body carries no padding of its own — each section brings its
   own inner shell (that is what .eng-eplan-body is). Without this the verdict
   box sat flush against the section header's border-top and read as touching
   the banner. Same modifier pattern, so ePlan/CAD/Documents are unaffected. */
.eng-doctor-body { padding: 18px 20px 20px; }

.doctor-verdict {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 18px 28px;
  padding: 18px 22px;
  margin-bottom: 18px;
  border: 1px solid var(--border);
  border-left: 3px solid var(--text-faint);
  border-radius: var(--r-lg);
  background: linear-gradient(135deg, var(--surface) 0%, var(--bg-elevated) 100%);
  transition: border-color var(--t-base);
}
.doctor-verdict-main { display: flex; align-items: center; gap: 14px; min-width: 0; }
.doctor-verdict-headline {
  font-size: 1.25rem; font-weight: 600; letter-spacing: -0.01em; color: var(--text);
}

/* Freshness. A monitoring panel that cannot show it has gone stale is worse
   than none — it reads as "all clear" when it means "no idea". */
.doctor-verdict-dot {
  width: 10px; height: 10px; border-radius: 50%; flex: none;
  background: var(--text-faint);
  box-shadow: 0 0 0 4px rgba(255, 255, 255, 0.03);
}
.doctor-verdict[data-state="ok"]     { border-left-color: var(--ok); }
.doctor-verdict[data-state="adjust"] { border-left-color: var(--warn); }
.doctor-verdict[data-state="alert"]  { border-left-color: var(--err); }
.doctor-verdict[data-state="ok"]     .doctor-verdict-dot { background: var(--ok);   box-shadow: 0 0 0 4px var(--ok-faint); }
.doctor-verdict[data-state="adjust"] .doctor-verdict-dot { background: var(--warn); box-shadow: 0 0 0 4px var(--warn-faint); }
.doctor-verdict[data-state="alert"]  .doctor-verdict-dot { background: var(--err);  box-shadow: 0 0 0 4px var(--err-faint); }
.doctor-verdict[data-state="ok"]     .doctor-verdict-headline { color: var(--ok); }
.doctor-verdict[data-state="adjust"] .doctor-verdict-headline { color: var(--warn); }
.doctor-verdict[data-state="alert"]  .doctor-verdict-headline { color: var(--err); }
.doctor-verdict[data-state="stale"]  .doctor-verdict-headline { color: var(--text-dim); }
@media (prefers-reduced-motion: no-preference) {
  .doctor-verdict[data-state="ok"] .doctor-verdict-dot,
  .doctor-verdict[data-state="adjust"] .doctor-verdict-dot,
  .doctor-verdict[data-state="alert"] .doctor-verdict-dot {
    animation: doctor-pulse 2.4s ease-in-out infinite;
  }
}
@keyframes doctor-pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.45; } }

/* Supporting numbers: present, subordinate, aligned. */
.doctor-meta { display: flex; flex-wrap: wrap; gap: 8px 26px; margin: 0; }
.doctor-meta > div { display: flex; flex-direction: column; gap: 3px; }
.doctor-meta dt {
  font-size: 0.66rem; letter-spacing: 0.1em; text-transform: uppercase;
  color: var(--text-faint); font-weight: 500;
}
.doctor-meta dd {
  margin: 0; font-family: var(--font-mono); font-size: 0.85rem;
  color: var(--text-dim); font-variant-numeric: tabular-nums;
}

/* ---- findings ---------------------------------------------------------
   Severity-ranked. The ADJUST ones are the payload: the doctor names a
   parameter AND a value. Rendering that as a prescription rather than prose
   is the difference between reading the panel and acting on it. */
.doctor-findings { display: flex; flex-direction: column; gap: 10px; }
.doctor-finding {
  border: 1px solid var(--border); border-left-width: 3px;
  border-radius: var(--r-md); padding: 13px 16px;
  background: var(--surface); transition: border-color var(--t-fast);
}
.doctor-finding:hover { border-color: var(--border-strong); }
.doctor-finding.sev-alert  { border-left-color: var(--err); }
.doctor-finding.sev-adjust { border-left-color: var(--warn); }
.doctor-finding.sev-check  { border-left-color: var(--info); }
.doctor-finding.sev-ok     { border-left-color: var(--ok); }

.doctor-finding-head { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; }
.doctor-sev {
  flex: none; font-family: var(--font-mono); font-size: 0.64rem; font-weight: 700;
  letter-spacing: 0.09em; padding: 3px 7px; border-radius: var(--r-sm);
  border: 1px solid transparent;
}
.doctor-finding.sev-alert  .doctor-sev { color: var(--err);  background: var(--err-faint);  border-color: rgba(255, 77, 109, 0.3); }
.doctor-finding.sev-adjust .doctor-sev { color: var(--warn); background: var(--warn-faint); border-color: rgba(255, 210, 63, 0.3); }
.doctor-finding.sev-check  .doctor-sev { color: var(--info); background: rgba(108, 182, 255, 0.12); border-color: rgba(108, 182, 255, 0.3); }
.doctor-finding.sev-ok     .doctor-sev { color: var(--ok);   background: var(--ok-faint);   border-color: rgba(61, 220, 151, 0.3); }
.doctor-what { font-weight: 600; color: var(--text); line-height: 1.45; }

/* Parameter -> value, with the CURRENT value kept visible so the size of the
   change is legible, not just its destination. */
.doctor-rx {
  display: flex; align-items: baseline; flex-wrap: wrap; gap: 10px;
  margin-top: 10px; padding: 9px 12px;
  border: 1px dashed var(--border-strong); border-radius: var(--r-sm);
  background: var(--bg-elevated);
  font-family: var(--font-mono); font-variant-numeric: tabular-nums;
}
.doctor-rx-param { color: var(--text-dim); font-size: 0.86rem; }
.doctor-rx-arrow { color: var(--text-faint); }
.doctor-rx-new   { color: var(--warn); font-weight: 700; font-size: 0.95rem; }
.doctor-rx-now   { color: var(--text-faint); font-size: 0.78rem; }

.doctor-note { display: flex; gap: 10px; margin-top: 8px; }
.doctor-note-label {
  flex: none; width: 46px; padding-top: 2px;
  font-size: 0.62rem; letter-spacing: 0.09em; text-transform: uppercase;
  color: var(--text-faint); font-weight: 600;
}
.doctor-note-body { margin: 0; font-size: 0.855rem; color: var(--text-dim); line-height: 1.5; }

.doctor-empty { padding: 26px 20px; text-align: center; }
.doctor-empty-title { color: var(--text-dim); font-size: 0.95rem; }
.doctor-empty-hint { color: var(--text-faint); font-size: 0.82rem; margin-top: 6px; }

@media (max-width: 720px) {
  .doctor-verdict { align-items: flex-start; }
  .doctor-meta { gap: 8px 18px; }
}

/* ---- Shifts table ------------------------------------------------------
   A shift day is local noon to noon, so the interesting column is not the
   total — it is WHEN the work happened. The split bar carries that: work
   before 18:00, inside the nominal night, and overrunning past 06:00.
   Overrun is the one a shift lead is looking for, so it is the only segment
   that gets a warning colour. */
.shifts-table td, .shifts-table th { vertical-align: middle; }
.shifts-table .num { text-align: right; font-variant-numeric: tabular-nums; }
.shift-quiet td { opacity: 0.5; }        /* a night that never ran, still listed */

.shift-split {
  display: flex; height: 7px; border-radius: 999px; overflow: hidden;
  background: rgba(255, 255, 255, 0.05); min-width: 120px;
}
.shift-seg { display: block; height: 100%; }
/* Blue, not grey. Grey reads as disabled or missing, and work that happened
   before 18:00 is neither — it is real work that simply landed outside the
   nominal night. Blue says "informational", which is what it means. Faded
   grey stays reserved for a shift that genuinely did not run. */
.shift-seg.early { background: var(--info); }
.shift-seg.in    { background: var(--ok); }
.shift-seg.over  { background: var(--warn); }
.shift-split-legend { margin-top: 4px; }

.ok-text   { color: var(--ok); }
.warn-text { color: var(--warn); }
.err-text  { color: var(--err); }

/* ---- Shift league ------------------------------------------------------
   Medals earn colour; everything else stays quiet. If every row glows, the
   podium stops meaning anything — which is the same failure as ranking a
   three-parcel night first, expressed visually. */
.league { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 6px; }
.league-row {
  display: grid;
  grid-template-columns: 40px minmax(90px, auto) 1fr auto;
  align-items: center; gap: 12px;
  padding: 10px 12px;
  border: 1px solid var(--border); border-radius: var(--r-md);
  background: var(--surface);
}
.league-row.quiet { opacity: 0.45; }        /* a night that never ran, still shown */
.league-row.medalled { border-color: var(--border-strong); }
.league-row.gold   { background: linear-gradient(90deg, rgba(255,210,63,0.10), var(--surface) 55%); }
.league-row.silver { background: linear-gradient(90deg, rgba(255,255,255,0.07), var(--surface) 55%); }
.league-row.bronze { background: linear-gradient(90deg, rgba(7, 187, 241,0.09), var(--surface) 55%); }

.league-pos {
  font-size: 1.15rem; text-align: center;
  font-family: var(--font-mono); color: var(--text-dim);
  font-variant-numeric: tabular-nums;
}
.league-day { font-weight: 600; }
.league-stats { display: flex; flex-wrap: wrap; gap: 4px 14px; align-items: baseline; }
.league-stat { font-size: 0.83rem; color: var(--text-dim); }
.league-why { flex-basis: 100%; }
.league-score {
  font-size: 1.05rem; font-weight: 700; color: var(--text);
  font-variant-numeric: tabular-nums;
}
.league-summary { margin-bottom: 12px; }

@media (max-width: 720px) {
  .league-row { grid-template-columns: 34px 1fr auto; }
  .league-stats { grid-column: 1 / -1; }
}

/* ---- Run summary -------------------------------------------------------
   Sits above the feed while a run window is pinned. A readout, not a
   dashboard: six numbers an operator checks mid-run and at the end of it.
   Rates carry colour, raw counts stay neutral — two mis-sorts in five
   thousand should not make a good run look like a bad one. */
.run-summary {
  margin: 0 0 14px;
  padding: 14px 18px;
  border: 1px solid var(--border);
  border-left: 3px solid var(--accent);
  border-radius: var(--r-lg);
  background: linear-gradient(135deg, var(--surface) 0%, var(--bg-elevated) 100%);
}
.run-summary-head {
  display: flex; align-items: baseline; gap: 12px; flex-wrap: wrap;
  margin-bottom: 12px;
}
.run-summary-title { font-weight: 600; font-size: 0.95rem; }
.run-summary-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(130px, 1fr));
  gap: 14px 22px;
}
.run-metric { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.run-metric-label {
  font-size: 0.66rem; letter-spacing: 0.1em; text-transform: uppercase;
  color: var(--text-faint); font-weight: 500;
}
.run-metric-value {
  font-size: 1.35rem; font-weight: 600; line-height: 1.1;
  font-variant-numeric: tabular-nums; color: var(--text);
}
.run-metric-unit { font-size: 0.8rem; font-weight: 500; color: var(--text-dim); margin-left: 2px; }
.run-metric-sub { font-size: 0.72rem; }
/* ---- Boxes-per-hour card (rebuilt 2026-08-30) --------------------------
   Chris: "its still unclear what the rest means i need clarity ... its
   right at the top and i want it looking sexy".

   Three zones, in the order an operator asks the questions:
     1. FASTEST EVER - the record. Always populated (it reads the parcel
        record, not the live agent feed) so the card can never present as
        broken while the line is idle.
     2. THE TWO SPEEDS - with boxes coming vs counting the waiting, drawn
        against a shared scale so the shortfall between the bars IS the
        waiting time. This comparison is the reason the card exists.
     3. THE RUN'S FACTS - boxes, length, pauses. Quietest of the three.
   Weight descends left to right; only zone 1 carries the accent. */
.feed-card { padding: 0; overflow: hidden; }

.feed-card-head {
  display: flex; align-items: center; flex-wrap: wrap; gap: 10px 18px;
  padding: 13px 18px;
  border-bottom: 1px solid var(--border);
  background: var(--bg-elevated);
}
.feed-card-titles { display: flex; flex-direction: column; gap: 1px; }
.feed-card-sub { font-size: 0.72rem; color: var(--text-dim); }

/* State pill. Colour is the signal; the words carry the detail. */
.feed-state {
  font-size: 0.7rem; font-weight: 600; letter-spacing: 0.02em;
  padding: 3px 10px; border-radius: 999px;
  color: var(--text-dim);
  background: rgba(255, 255, 255, 0.05);
  border: 1px solid var(--border-strong);
}
.feed-state.is-live {
  color: var(--ok); background: var(--ok-faint);
  border-color: rgba(61, 220, 151, 0.35);
}
.feed-state.is-off { color: var(--text-faint); }

.feed-restart { margin-left: auto; }

/* The two run settings. They were in the header reading "a pause counts
   after 5s" - true, but silent on why anyone would change it, and the
   global `label` rule was shouting them in caps. Down here they get room
   to be whole sentences, and each names the figure above that it moves. */
.feed-card-foot {
  display: flex; align-items: center; flex-wrap: wrap; gap: 8px 22px;
  padding: 10px 20px;
  border-top: 1px solid var(--border);
  background: rgba(255, 255, 255, 0.015);
}
.feed-foot-lead {
  font-size: 0.63rem; font-weight: 700; letter-spacing: 0.14em;
  text-transform: uppercase; color: var(--text-faint);
}
.feed-card-foot label.feed-foot-rule {
  /* beats the global `label { text-transform: uppercase }` */
  display: inline-flex; align-items: center; gap: 6px; flex-wrap: wrap;
  margin: 0; text-transform: none; letter-spacing: 0;
  font-size: 0.75rem; font-weight: 400; color: var(--text-dim);
}
.feed-card-foot input {
  width: 3.6em; text-align: center; padding: 2px 4px; margin: 0;
  font-family: var(--font-mono); font-size: 0.75rem;
  color: var(--text); background: var(--bg-elevated-2);
  border: 1px solid var(--border-strong); border-radius: var(--r-sm);
}
.feed-card-foot input:focus {
  outline: none; border-color: var(--accent);
  box-shadow: 0 0 0 2px var(--accent-faint);
}


.feed-card-body {
  display: grid;
  grid-template-columns: minmax(230px, 270px) minmax(280px, 1fr) auto;
  gap: 0;
  align-items: stretch;
}
.feed-card-body > * { padding: 16px 20px; }
.feed-card-body > * + * { border-left: 1px solid var(--border); }
@media (max-width: 1080px) {
  .feed-card-body { grid-template-columns: 1fr; }
  .feed-card-body > * + * {
    border-left: 0; border-top: 1px solid var(--border);
  }
}

/* ---- 1. The record ---------------------------------------------------- */
.feed-record {
  background:
    radial-gradient(120% 130% at 0% 0%, rgba(7, 187, 241, 0.13), transparent 68%),
    var(--bg-elevated-2);
}
.feed-record-label {
  font-size: 0.65rem; letter-spacing: 0.14em; text-transform: uppercase;
  font-weight: 700; color: var(--accent);
}
.feed-record-value {
  display: flex; align-items: baseline; gap: 6px; flex-wrap: wrap;
  margin: 4px 0 6px;
  font-size: 2.6rem; font-weight: 700; line-height: 1;
  color: var(--accent); font-variant-numeric: tabular-nums;
  text-shadow: 0 0 28px rgba(7, 187, 241, 0.28);
}
.feed-record-unit {
  font-size: 0.72rem; font-weight: 600; letter-spacing: 0.06em;
  text-transform: uppercase; color: var(--text-dim); text-shadow: none;
}
.feed-record-note { font-size: 0.73rem; line-height: 1.45; color: var(--text-dim); }
.feed-record.is-empty .feed-record-value {
  color: var(--text-faint); text-shadow: none;
}

/* ---- 2. The two speeds ------------------------------------------------ */
.feed-speeds { display: flex; flex-direction: column; gap: 11px; justify-content: center; }
.feed-speed-top {
  display: flex; align-items: baseline; justify-content: space-between; gap: 12px;
}
.feed-speed-label { font-size: 0.76rem; font-weight: 600; color: var(--text); }
.feed-speed-value {
  font-size: 1.15rem; font-weight: 700; color: var(--text);
  font-variant-numeric: tabular-nums;
}
.feed-speed-unit {
  font-size: 0.68rem; font-weight: 600; color: var(--text-dim); margin-left: 2px;
}
.feed-speed-hint { font-size: 0.7rem; color: var(--text-faint); margin-top: 3px; }

.feed-track {
  height: 7px; margin-top: 5px; border-radius: 4px;
  background: rgba(255, 255, 255, 0.06); overflow: hidden;
}
.feed-track-fill {
  height: 100%; border-radius: 4px;
  transition: width var(--t-slow);
}
/* Full-speed bar takes the accent; the with-waiting bar is deliberately
   quieter, so the eye reads the shortfall rather than two rival colours. */
.is-cap .feed-track-fill {
  background: linear-gradient(90deg, var(--accent), #6fe3ff);
  box-shadow: 0 0 12px var(--accent-glow);
}
.is-cap .feed-speed-value { color: var(--accent); }
.is-ovr .feed-track-fill { background: var(--text-faint); }

.feed-verdict {
  margin-top: 3px; padding-top: 10px;
  border-top: 1px solid var(--border);
  font-size: 0.76rem; line-height: 1.5; color: var(--text-dim);
}
.feed-verdict b { color: var(--text); font-weight: 700; }

/* ---- 3. The run's facts ----------------------------------------------- */
.feed-facts {
  display: flex; gap: 30px; align-items: center;
  background: rgba(255, 255, 255, 0.012);
}
.feed-fact { display: flex; flex-direction: column; gap: 1px; }
.feed-fact-value {
  font-size: 1.5rem; font-weight: 700; line-height: 1.1; color: var(--text);
  font-variant-numeric: tabular-nums;
}
.feed-fact-value.is-warn { color: var(--warn); }
.feed-fact-label {
  font-size: 0.7rem; font-weight: 600; color: var(--text-dim);
  letter-spacing: 0.02em;
}
.feed-fact-sub { font-size: 0.68rem; color: var(--text-faint); }
@media (max-width: 620px) { .feed-facts { gap: 20px; flex-wrap: wrap; } }



/* ---- Commissioning-mask banner -----------------------------------
   Shown above the alarm table whenever central is suppressing alarms for
   unwired devices. Deliberately high-contrast amber: every masked code is
   safety-class, so this is the only thing standing between an operator and
   the belief that a quiet alarm list means a safe machine. It is meant to
   be slightly annoying — it should nag until the mask is removed. */
.alarm-mask-banner {
  display: block;
  margin: 0 0 8px;
  padding: 8px 12px;
  border: 1px solid #b45309;
  border-left: 4px solid #f59e0b;
  border-radius: var(--r-md, 6px);
  background: rgba(245, 158, 11, 0.12);
  color: #fbbf24;
  font-size: 13px;
  font-weight: 600;
  line-height: 1.35;
  cursor: help;
}


/* ---- SCADA manual-controls embed (scene only) --------------------------
   The manual page hosts the viewer purely as a plan view: its own panel
   column carries the data, so the viewer's HUD boxes, stats card and game
   button would be noise inside that frame. Hover tooltip stays — it is the
   click affordance. */
.page-scada-embed-manual #scada-game-toggle,
.page-scada-embed-manual #scada-stats,
.page-scada-embed-manual #scada-hud,
.page-scada-embed-manual #scada-coord-hud,
/* The sidebar ships hidden-but-present in embed mode so scada.js can keep
   writing its ids — but its own display rule beats the `hidden` attribute,
   which is exactly what leaked Insights/Throughput into the manual frame
   (Chris, 2026-09-01). Scene only in here. */
.page-scada-embed-manual .scada-side,
.page-scada-embed-manual .scada-lower {
  display: none !important;
}


/* Stats card must scroll when taller than the stage (Chris, 2026-09-01 —
   the 1201-5 servo card ran past the bottom of the viewport). */
.scada-stats {
  max-height: calc(100% - 32px);
  overflow-y: auto;
}


/* ---- Phone portrait guards (Chris, 2026-09-01) -------------------------
   Boxes were cut off with no way to scroll. Principles: the PAGE scrolls,
   fixed-width overlays clamp to the viewport, control rows wrap. */
@media (max-width: 700px) {
  .main { padding: 12px; }
  /* The SCADA stats card is a fixed 320px overlay — clamp it. */
  .scada-stats {
    left: 8px;
    right: 8px;
    width: auto;
    max-height: 62%;
  }
  .scada-toolbar, .window-selector, .scope-selector { flex-wrap: wrap; }
  .kpi-strip { grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); }
  .page-header { padding-left: 10px; padding-right: 10px; }
}


/* ---- Command centre on a phone (Chris, 2026-09-01) --------------------- */
@media (max-width: 700px) {
  .orion-cust-grid,
  .orion-noc-cust-grid {
    grid-template-columns: 1fr !important;
  }
  .orion-noc-card-head {
    grid-template-columns: 64px 1fr auto;
    gap: 10px;
  }
  .orion-noc-logo { width: 64px; height: 40px; }
  .orion-noc-banner-grid { padding: 14px 14px; gap: 10px; }
  .orion-noc-kpi-strip .orion-kpi-value { font-size: 26px; }
  .orion-kpi-strip,
  .orion-noc-kpi-strip {
    grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
  }
  .orion-admin-page .row.between { flex-wrap: wrap; gap: 8px; }
}


/* Clickable rows on the SCADA asset card (function group / part-of) */
.scada-stats-link { cursor: pointer; border-radius: var(--r-sm); }
.scada-stats-link:hover { background: var(--accent-faint); }
.scada-stats-link .scada-stats-val { color: var(--accent); }
