/* ============================================================
   JAGS AeroStores — calm neutral workspace
   A cool grey canvas, white rounded cards that float on it, near-black
   pill actions, circular icon controls, large light headings. Colour is
   reserved for meaning: stores-tag status, the amber signature, and one
   hue per department. Identity kept: tag colour bands, stamped references.
   ============================================================ */

@font-face {
  font-family: "Jakarta"; src: url("/assets/fonts/plus-jakarta-sans-var.woff2") format("woff2-variations"), url("/assets/fonts/plus-jakarta-sans-var.woff2") format("woff2");
  font-weight: 200 800; font-style: normal; font-display: swap;
}

:root {
  /* ink family: near-black with a cool cast, used for primaries and active states */
  --navy-900: #0A1316;
  --navy-800: #0F1D22;
  --navy-700: #1A2A30;
  --navy-600: #2B3B42;
  --navy-400: #6E7C83;
  --navy-300: #B7C1C6;

  /* light theme — cool grey canvas, white floating cards */
  --bg: #ECEFF2;
  --bg-accent: #E4E8EC;      /* wells, pressed states, inactive pills */
  --surface: #FFFFFF;
  --surface-2: #F4F6F8;
  --ink: #0F1A1F;
  --ink-2: #56626B;
  --ink-3: #646E76;  /* 5.2:1 on white, 4.5:1 on the canvas: secondary text stays readable */
  --line: rgba(15, 26, 31, .09);
  --line-strong: rgba(15, 26, 31, .15);
  --line-soft: rgba(15, 26, 31, .055);

  /* status (GCAA tag alignment: green release / amber pending / red reject / grey u-s) */
  --ok: #15804A;        --ok-bg: #EAF6EF;    --ok-line: #B5DDC5;
  --warn: #9A6400;      --warn-bg: #FFF4DE;  --warn-line: #EACF97;
  --bad: #B42A32;       --bad-bg: #FDEDEE;   --bad-line: #F0C0C3;
  --info: #2F63C8;      --info-bg: #EBF1FD;  --info-line: #C3D4F3;
  --muted: #66727B;     --muted-bg: #EEF1F4; --muted-line: #D5DBE0;

  --brand: var(--navy-800);
  --brand-2: var(--navy-600);
  --accent: #E07A2E; /* hangar amber — AeroStores signature */
  --accent-ink: #9A4B12;
  --focus: #2F63C8;

  --font: "Jakarta", -apple-system, BlinkMacSystemFont, "SF Pro Text", "Segoe UI", Roboto, sans-serif;
  --mono: ui-monospace, "SF Mono", SFMono-Regular, Menlo, Consolas, monospace;

  /* radius steps: tight on inner controls, soft on the cards that hold them */
  --r-xs: 10px; --r-sm: 14px; --r-md: 22px; --r-lg: 28px; --r-pill: 999px;
  /* shadows carry the canvas hue and stay low: cards float, they do not hover */
  --shadow-1: 0 1px 2px rgba(15, 26, 31, .035);
  --shadow-2: 0 10px 30px rgba(15, 26, 31, .06);
  --shadow-3: 0 18px 44px rgba(15, 26, 31, .14);
  --speed: 180ms;
  --speed-fast: 120ms;
  --ease: cubic-bezier(.2, 0, 0, 1);
  --pad: clamp(16px, 2.8vw, 32px);
  --touch: 48px;

  /* type scale; micro-labels share one voice */
  --t-xs: 11px; --t-sm: 13px; --t-base: 15px; --t-lg: 17px; --t-xl: 21px; --t-2xl: 30px;
  --label-font: 600 11.5px/1.3 var(--font);
  --label-track: .06em;
}

[data-theme="dark"] {
  /* deliberate dark: the same cool family at night, not an inversion */
  --bg: #0D1316;
  --bg-accent: #172024;
  --surface: #151D21;
  --surface-2: #1B252A;
  --ink: #E9EEF0;
  --ink-2: #AEB9BF;
  --ink-3: #8E9AA1;
  --line: rgba(210, 225, 232, .10);
  --line-strong: rgba(210, 225, 232, .17);
  --line-soft: rgba(210, 225, 232, .06);
  --ok: #7CC99A;   --ok-bg: #13291C;   --ok-line: #2A5539;
  --warn: #E3B65E; --warn-bg: #2E240E; --warn-line: #66512A;
  --bad: #EE8B90;  --bad-bg: #351719;  --bad-line: #6A2C30;
  --info: #93B3EE; --info-bg: #14223A; --info-line: #2E4870;
  --muted: #98A4AB; --muted-bg: #1D272C; --muted-line: #34424A;
  --brand: #2B3B42;
  --brand-2: #3A4C54;
  --accent: #EE8C45;
  --accent-ink: #F4B585;
  --focus: #93B3EE;
  --shadow-1: 0 1px 2px rgba(0, 0, 0, .45);
  --shadow-2: 0 10px 30px rgba(0, 0, 0, .4);
  --shadow-3: 0 18px 44px rgba(0, 0, 0, .55);
}

/* ---------- base ---------- */
* { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }
body {
  margin: 0; background: var(--bg); color: var(--ink);
  font: 400 16px/1.55 var(--font);
  -webkit-font-smoothing: antialiased;
  min-height: 100dvh;
}
a { color: var(--info); text-decoration: none; }
a:hover { text-decoration: underline; }
h1, h2, h3 { line-height: 1.22; margin: 0 0 .4em; }
h1 { font-size: clamp(22px, 3vw, var(--t-2xl)); font-weight: 700; letter-spacing: -.015em; }
h2 { font-size: var(--t-lg); font-weight: 700; letter-spacing: -.005em; }
h3 { font-size: var(--t-base); font-weight: 600; }
:focus-visible { outline: 3px solid var(--focus); outline-offset: 2px; border-radius: 4px; }
::selection { background: var(--navy-300); color: var(--navy-900); }
svg.ico { width: 20px; height: 20px; stroke: currentColor; fill: none; stroke-width: 1.75; stroke-linecap: round; stroke-linejoin: round; flex: none; }

.mono, .pn, .ref { font-family: var(--mono); font-variant-numeric: tabular-nums; }
.pn { font-weight: 600; letter-spacing: .01em; }
.ref {
  display: inline-block; padding: 1px 8px; border: 1px dashed var(--muted-line);
  border-radius: 6px; background: var(--surface-2); font-size: 13px; color: var(--ink-2);
  white-space: nowrap;
}
/* .ref draws a pill around inline content. Ledger views also hang it straight on
   a <td>, where inline-block lifts the cell out of the row box and the dashed
   border is left floating over the row rule. A cell stays a cell: it keeps the
   mono voice above and nothing else. */
td.ref, th.ref {
  display: table-cell; border-width: 0 0 1px; border-style: solid; border-color: var(--line-soft);
  border-radius: 0; background: none;
}
.dim { color: var(--ink-3); } .small { font-size: 13px; }
/* A number or a timestamp is one indivisible datum. Left wrappable it breaks
   mid-value when a neighbouring free-text column gets a long entry — one 94-char
   ledger reason was enough to fold every "2026-08-04 17:56" in the movement
   table onto two lines and add a third to the page height. */
.num { font-family: var(--mono); font-variant-numeric: tabular-nums; white-space: nowrap; }

/* ---------- app shell ---------- */
.shell { display: grid; grid-template-rows: auto 1fr; min-height: 100dvh; }
/* soft tinted header surface — light chrome, dark ink */
.topbar {
  position: sticky; top: 0; z-index: 40;
  display: flex; align-items: center; gap: 10px;
  background: color-mix(in srgb, var(--bg-accent) 72%, var(--bg));
  color: var(--ink); padding: 0 var(--pad); height: 60px;
  border-bottom: 1px solid var(--line);
  -webkit-backdrop-filter: saturate(1.4) blur(10px); backdrop-filter: saturate(1.4) blur(10px);
}
.topbar .brand { display: flex; align-items: center; gap: 10px; color: inherit; text-decoration: none; }
.brand-name { display: flex; flex-direction: column; line-height: 1.05; }
.brand-name b { font-size: 15px; letter-spacing: .01em; color: var(--ink); }
.brand-name span { font-size: 10px; letter-spacing: .22em; color: var(--ink-3); font-weight: 600; }

.top-search { flex: 1; max-width: 640px; margin-inline: auto; position: relative; }
.topbar .top-search input {
  width: 100%; height: 42px; min-height: 42px; border-radius: var(--r-sm); border: 1px solid var(--line);
  background: var(--surface); color: var(--ink); padding: 0 40px 0 44px; font: inherit; font-size: 15px;
}
.top-search input::placeholder { color: var(--ink-3); }
.topbar .top-search input:focus { border-color: var(--focus); outline: 2px solid color-mix(in srgb, var(--focus) 30%, transparent); outline-offset: 0; }
.top-search .ico { position: absolute; left: 14px; top: 11px; color: var(--ink-3); pointer-events: none; }
.top-search kbd {
  position: absolute; right: 10px; top: 11px; font: 600 11px/18px var(--mono);
  padding: 0 6px; border: 1px solid var(--line-strong); border-radius: 5px; color: var(--ink-3);
  background: var(--surface-2);
}
.top-actions { display: flex; align-items: center; gap: 2px; }
.top-quick {
  display: inline-flex; align-items: center; gap: 6px; height: 38px; padding: 0 14px; margin-right: 4px;
  border: 1px solid var(--line-strong); border-radius: var(--r-pill); color: var(--ink-2);
  background: var(--surface);
  font: 600 13px/1 var(--font); text-decoration: none; white-space: nowrap;
  transition: background var(--speed), border-color var(--speed);
}
.top-quick:hover { background: var(--surface-2); border-color: var(--navy-400); color: var(--ink); text-decoration: none; }
.top-quick .ico { width: 15px; height: 15px; }
/* header condenses on scroll (adds depth, no height change — nothing jumps) */
.topbar { transition: box-shadow 180ms ease, background 180ms ease; }
.topbar.condensed { box-shadow: var(--shadow-2); background: color-mix(in srgb, var(--bg-accent) 88%, var(--surface)); }
.brand-name b { transition: font-size 180ms ease; }
.topbar.condensed .brand-name b { font-size: 14px; }
.top-btn {
  position: relative; display: grid; place-items: center; width: 44px; height: 44px; border-radius: var(--r-sm);
  color: var(--ink-2); background: transparent; border: 0; cursor: pointer;
}
.top-btn:hover { background: color-mix(in srgb, var(--surface) 70%, var(--bg-accent)); color: var(--ink); }
.top-btn .dotbadge {
  position: absolute; top: 5px; right: 5px; min-width: 16px; height: 16px; padding: 0 4px;
  border-radius: var(--r-pill); background: var(--bad); color: #fff; font: 700 10px/16px var(--font);
  text-align: center; border: 2px solid var(--bg-accent);
}

.frame { display: grid; grid-template-columns: 224px 1fr; min-height: 0; }
.rail {
  position: sticky; top: 60px; align-self: start; height: calc(100dvh - 60px);
  overflow-y: auto; padding: 16px 12px 22px; border-right: 1px solid var(--line);
  background: color-mix(in srgb, var(--bg-accent) 45%, var(--bg)); scrollbar-width: thin;
}

main.content { padding: var(--pad); min-width: 0; }
/* room for the bottom nav + raised scan button + iPhone home bar */
@media (max-width: 900px) {
  main.content { padding-bottom: calc(116px + env(safe-area-inset-bottom)); }
}
.page-head { display: flex; flex-wrap: wrap; align-items: center; gap: 10px 14px; margin-bottom: 18px; }

/* ---------- contextual sub-navigation (pages within the active section) ---------- */
.subnav {
  display: flex; gap: 2px; margin: -4px 0 16px; border-bottom: 1px solid var(--line-soft);
  overflow-x: auto; scrollbar-width: none;
}
.subnav::-webkit-scrollbar { display: none; }
.subnav a {
  display: inline-flex; align-items: center; gap: 7px; padding: 10px 13px; min-height: 44px;
  font-size: 13px; font-weight: 600; color: var(--ink-2); text-decoration: none; white-space: nowrap;
  border-bottom: 2px solid transparent; margin-bottom: -1px;
}
.subnav a:hover { color: var(--ink); text-decoration: none; }
.subnav a.on { color: var(--navy-700); border-bottom-color: var(--accent); }
[data-theme="dark"] .subnav a.on { color: var(--navy-300); }
.subnav .count {
  font: 600 11px/18px var(--mono); background: var(--bad-bg); color: var(--bad);
  border-radius: var(--r-pill); padding: 0 6px;
}
.page-head .spacer { flex: 1; }
.crumbs { font-size: 13px; color: var(--ink-3); margin-bottom: 4px; }
.crumbs a { color: var(--ink-3); }

/* bottom nav (mobile) */
/* A floating pill rather than a bar welded to the bottom edge. Every part of this
   is something the surface already owns: --r-pill from the chips and .top-quick,
   the saturate+blur the mobile header uses, --shadow-2, --bg-accent for the
   active state. Nothing new was invented for it, it is the same chrome standing
   away from the edge so the app reads as sitting on the phone rather than
   ending at it. */
.bottom-nav {
  position: fixed; z-index: 40; display: none;
  inset: auto max(10px, env(safe-area-inset-left)) calc(10px + env(safe-area-inset-bottom)) max(10px, env(safe-area-inset-right));
  /* auto-flow: a role missing the scan button still fills the bar evenly */
  grid-auto-flow: column; grid-auto-columns: 1fr; gap: 2px;
  padding: 6px; border-radius: var(--r-pill);
  background: color-mix(in srgb, var(--surface) 86%, transparent);
  border: 1px solid var(--line);
  -webkit-backdrop-filter: saturate(1.5) blur(14px); backdrop-filter: saturate(1.5) blur(14px);
  box-shadow: var(--shadow-2), 0 10px 26px rgba(27, 25, 80, .10);
}
[data-theme="dark"] .bottom-nav {
  background: color-mix(in srgb, var(--surface) 82%, transparent);
  box-shadow: var(--shadow-2), 0 10px 30px rgba(0, 0, 0, .45);
}
.bottom-nav a, .bottom-nav button.navlike {
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 3px;
  padding: 7px 2px 5px; min-height: var(--touch);
  border-radius: var(--r-pill); font-size: 11px; font-weight: 600; color: var(--ink-3); text-decoration: none;
  background: none; border: 0; cursor: pointer; font-family: inherit;
  transition: color var(--speed) var(--ease), background var(--speed) var(--ease);
}
/* The active item is a pill inside a pill, which is what makes the shape read as
   deliberate instead of as a rounded rectangle that happens to be very round. */
.bottom-nav a.on, .bottom-nav button.navlike.on { color: var(--navy-700); background: var(--bg-accent); }
[data-theme="dark"] .bottom-nav a.on, [data-theme="dark"] .bottom-nav button.navlike.on { color: var(--navy-300); background: var(--bg-accent); }
.bottom-nav a:active, .bottom-nav button.navlike:active { background: var(--bg-accent); }
.bottom-nav a.core { position: relative; top: -18px; background: none; }
.bottom-nav a.core:active { background: none; }
.bottom-nav a.core .corebtn {
  display: grid; place-items: center; width: 54px; height: 54px; border-radius: var(--r-pill);
  background: var(--navy-800); color: #fff;
  box-shadow: var(--shadow-2), 0 6px 18px color-mix(in srgb, var(--navy-800) 38%, transparent);
  transition: transform var(--speed) var(--ease);
}
.bottom-nav a.core:active .corebtn { transform: scale(.94); }
[data-theme="dark"] .bottom-nav a.core .corebtn { background: var(--navy-700); }

@media (max-width: 900px) {
  .frame { grid-template-columns: 1fr; }
  .rail { display: none; }
  .bottom-nav { display: grid; }
  /* iOS anchors position:fixed to the LAYOUT viewport, and that viewport does not
     shrink when the software keyboard opens. So with the keyboard up the bar
     stopped being "the bottom of the screen" and became a pill parked in the
     middle of the results, drawn straight through them.
     While a field has focus the keyboard owns the bottom of the phone and the bar
     is unreachable anyway, so it stands down and gives the list the room. It
     comes back the moment the field is dismissed. Browsers without :has() keep
     today's behaviour, which is the bug, not a worse one. */
  body:has(input:focus, textarea:focus, select:focus) .bottom-nav { display: none; }
  .top-search { position: static; }
  .topbar .brand-name { display: none; }
}

/* ---------- tag cards (the signature) ----------
   A neutral card is a plain quiet surface. When it has status to declare it
   takes the stores-tag colour across the whole card — 1px border plus the tint
   the chips already use — so the meaning reads at arm's length instead of
   hiding in a stripe down one edge. */
.tagcard {
  position: relative; background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--r-md); padding: 16px 18px; overflow: hidden;
}
.tagcard.t-ok     { border-color: var(--ok-line);   background: var(--ok-bg); }
.tagcard.t-warn   { border-color: var(--warn-line); background: var(--warn-bg); }
.tagcard.t-bad    { border-color: var(--bad-line);  background: var(--bad-bg); }
.tagcard.t-info   { border-color: var(--info-line); background: var(--info-bg); }
.tagcard.t-accent { border-color: var(--accent);    background: color-mix(in srgb, var(--accent) 8%, var(--surface)); }

/* Secondary text on a TINTED card cannot be the global grey. --ink-3 on a t-warn
   card in dark mode measures 4.37:1, under the floor, and the same grey against
   four different tinted grounds reads as a colour clash rather than a hierarchy.
   Each tone therefore derives its own quiet ink from its own hue mixed toward the
   card's foreground, so it stays quiet, stays on-tone, and stays legible in both
   themes. Plain cards keep --ink-3, which is what it was designed against. */
.tagcard.t-ok     { --card-dim: color-mix(in srgb, var(--ok-line)   42%, var(--ink)); }
.tagcard.t-warn   { --card-dim: color-mix(in srgb, var(--warn-line) 42%, var(--ink)); }
.tagcard.t-bad    { --card-dim: color-mix(in srgb, var(--bad-line)  42%, var(--ink)); }
.tagcard.t-info   { --card-dim: color-mix(in srgb, var(--info-line) 42%, var(--ink)); }
.tagcard.t-accent { --card-dim: color-mix(in srgb, var(--accent)    42%, var(--ink)); }
.tagcard.t-ok .dim, .tagcard.t-warn .dim, .tagcard.t-bad .dim,
.tagcard.t-info .dim, .tagcard.t-accent .dim { color: var(--card-dim); }
/* the punch hole was a paper-tag souvenir — the tag colour carries it now */
.tagcard .punch { display: none; }

/* home hero (mobile greeting header) */
.home-hero { display: flex; flex-direction: column; gap: 2px; margin-bottom: 4px; }
.home-hero .hh-row { display: flex; align-items: center; gap: 12px; }
.home-hero .hh-face {
  width: 44px; height: 44px; border-radius: 50%; background: var(--navy-800); color: #fff;
  display: grid; place-items: center; font-weight: 700; font-size: 16px; flex: none;
}
[data-theme="dark"] .home-hero .hh-face { background: var(--navy-600); }
.home-hero .hh-greet { font-size: 14px; color: var(--ink-2); }
.home-hero h1 { margin: 0; font-size: clamp(24px, 5vw, 30px); font-weight: 800; letter-spacing: -.02em; }
.home-hero .hh-meta { display: flex; flex-wrap: wrap; gap: 4px 14px; font-size: 13px; color: var(--ink-3); margin-top: 4px; }
.home-hero .hh-meta span { display: inline-flex; align-items: center; gap: 5px; }
.home-hero .hh-meta .ico { width: 14px; height: 14px; }

/* ---------- action rack ----------
   Not a card grid. A rack of labelled slots a clerk reaches into: one row, one
   height, content-width slots that wrap without leaving a hole, and the icon
   reading as the tool head rather than sitting in a decorative well. The one
   action this role reaches for first is filled and leads the rack. The
   explainer sentences are gone — every one of them restated its own label. */
.actions-grid { display: flex; flex-wrap: wrap; gap: 8px; }
.action-tile {
  display: inline-flex; align-items: center; gap: 9px;
  min-height: var(--touch); padding: 0 16px 0 13px;
  border-radius: var(--r-sm); border: 1px solid var(--line); background: var(--surface);
  color: var(--ink); font-size: 14px; font-weight: 600; text-decoration: none;
  transition: border-color var(--speed) var(--ease), background var(--speed) var(--ease);
}
.action-tile:hover { background: var(--surface-2); border-color: var(--accent); text-decoration: none; }
.action-tile:active { transform: scale(.99); }
.action-tile .ico { width: 18px; height: 18px; color: var(--ink-3); transition: color var(--speed) var(--ease); }
.action-tile:hover .ico { color: var(--accent); }
.action-tile b { font-weight: 600; }
.action-tile span { display: none; }
/* the one action this role reaches for first — a single filled slot */
.action-tile.primary {
  background: var(--navy-800); border-color: var(--navy-800); color: #fff;
  padding: 0 22px 0 18px; font-size: 15px;
}
.action-tile.primary .ico, .action-tile.primary:hover .ico { color: #fff; }
.action-tile.primary:hover { background: var(--navy-700); border-color: var(--navy-700); }
[data-theme="dark"] .action-tile.primary { background: var(--navy-600); border-color: var(--navy-600); }
[data-theme="dark"] .action-tile.primary:hover { background: var(--navy-700); border-color: var(--navy-700); }
/* Phones: the slots share the row width so the rack never ends ragged. */
@media (max-width: 900px) {
  .action-tile { flex: 1 1 auto; justify-content: center; padding-inline: 12px; }
  .action-tile.primary { flex-basis: 100%; }
}

/* ---------- needs attention ----------
   One ruled list, two weights — not eleven boxes. Eleven tinted bars of equal
   height is a wall, and a wall has no hierarchy: "Out of stock 711" ends up
   looking exactly like "Fleet hours stale". Here a crisis row takes the full
   measure, a ringed marker and a large red count; the long tail of cautions is
   the same row at a quieter pitch and columns up beside itself on a wide
   screen. The counts are the content, so they sit on a shared right edge in
   tabular mono and read down as a column. Nothing is tinted: the count colour
   and the marker carry severity, which is the only thing tint was doing. */
.queues {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(min(100%, 320px), 1fr));
  column-gap: 30px; border-top: 1px solid var(--line);
}
.queue-link {
  display: flex; align-items: center; gap: 10px; min-height: 46px; padding: 7px 2px;
  border-bottom: 1px solid var(--line-soft); background: none;
  color: var(--ink-2); font-size: 14px; font-weight: 500; text-decoration: none;
  transition: color var(--speed-fast) var(--ease);
}
.queue-link::before {
  content: ""; flex: none; width: 7px; height: 7px; border-radius: 50%; background: var(--muted-line);
}
/* drawn, not a glyph — and the one place this page moves: it leans on hover */
.queue-link::after {
  content: ""; flex: none; width: 6px; height: 6px; margin-left: 4px;
  border-right: 1.5px solid var(--ink-3); border-top: 1.5px solid var(--ink-3);
  transform: rotate(45deg); transition: transform var(--speed-fast) var(--ease);
}
.queue-link:hover { color: var(--ink); text-decoration: none; }
.queue-link:hover::after { transform: rotate(45deg) translate(2px, -2px); }
.queue-link .n {
  margin-left: auto; font: 600 15px/1 var(--mono); font-variant-numeric: tabular-nums; color: var(--ink-2);
}
.queue-link.q-warn::before { background: var(--warn); }
/* crisis takes the full measure of the block and the number does the shouting */
.queue-link.q-bad { grid-column: 1 / -1; min-height: 54px; color: var(--ink); font-weight: 600; }
.queue-link.q-bad::before {
  content: "!"; display: grid; place-items: center; width: 20px; height: 20px;
  background: none; border: 1.5px solid var(--bad); color: var(--bad); font: 800 12px/1 var(--font);
}
.queue-link.q-bad .n { font-size: 21px; color: var(--bad); }
@media (max-width: 900px) { .queue-link { min-height: var(--touch); } }
/* Some queue views hang .queue-link on a clickable <tr>. A table row is not an
   alert row — display:flex there detaches the body from its own header. */
tr.queue-link { display: table-row; min-height: 0; border: 0; font: inherit; color: inherit; cursor: pointer; }
tr.queue-link::before, tr.queue-link::after { content: none; }

/* ---------- home panels: movement ledger + fleet roster ----------
   Both are scan-first read-outs, not card grids. One dominant value per row
   (part number, registration), one dominant number per row (quantity, hours
   remaining), everything else recedes to a supporting tier. Rows are divided
   by hairlines instead of boxed individually, so twelve of them still read as
   one object. Every figure is mono + tabular so columns line up on the digit. */
.panel {
  border: 1px solid var(--line); border-radius: var(--r-md); background: var(--surface);
  overflow: hidden;
}
.panel-head {
  display: flex; align-items: baseline; gap: 9px; padding: 13px 16px 11px;
  border-bottom: 1px solid var(--line-soft);
}
.panel-head h2 { margin: 0; font-size: 15px; font-weight: 700; letter-spacing: -.006em; }
.panel-head .sub { font-size: 12px; font-weight: 500; color: var(--muted); }
.panel-head .more {
  margin-left: auto; font-size: 13px; font-weight: 600; color: var(--ink-2);
  display: inline-flex; align-items: center; gap: 2px; white-space: nowrap;
}
.panel-head .more .ico { width: 14px; height: 14px; }
.panel-head .more:hover { color: var(--info); text-decoration: none; }
.panel-foot {
  padding: 9px 16px; border-top: 1px solid var(--line-soft); font-size: 12px; color: var(--muted);
  background: color-mix(in srgb, var(--surface-2) 60%, var(--surface));
}
.panel-foot.is-stale { color: var(--warn); background: var(--warn-bg); border-top-color: var(--warn-line); }
.panel-rows > * + * { border-top: 1px solid var(--line-soft); }

/* movement ledger row: direction well | part + context | signed quantity + age */
.led-row {
  display: grid; grid-template-columns: 28px minmax(0, 1fr) auto;
  grid-template-areas: "dir main qty" "dir meta age";
  gap: 1px 12px; align-items: center; padding: 10px 16px;
  color: var(--ink); text-decoration: none;
  transition: background var(--speed-fast) var(--ease);
}
a.led-row:hover { background: var(--surface-2); text-decoration: none; }
a.led-row:active { background: color-mix(in srgb, var(--surface-2) 60%, var(--bg-accent)); }
.led-row .dir {
  grid-area: dir; width: 28px; height: 28px; border-radius: 9px; box-sizing: border-box;
  display: grid; place-items: center;
}
.led-row .dir .ico { width: 15px; height: 15px; stroke-width: 2; }
.led-row.neg .dir { background: var(--bad-bg); color: var(--bad); box-shadow: inset 0 0 0 1px var(--bad-line); }
.led-row.pos .dir { background: var(--ok-bg);  color: var(--ok);  box-shadow: inset 0 0 0 1px var(--ok-line); }
.led-row.neu .dir { background: var(--muted-bg); color: var(--muted); box-shadow: inset 0 0 0 1px var(--muted-line); }
/* Nothing on this line may wrap: a part number broken across two lines stops
   being scannable. When the row runs out of width the movement type gives way
   first (the sign and colour of the quantity already carry direction), then the
   part number, and the aircraft reference is never dropped. */
.led-row .main { grid-area: main; display: flex; align-items: baseline; gap: 7px; min-width: 0; flex-wrap: nowrap; }
.led-row .main > * { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.led-row .main .pn { font-size: 14px; font-weight: 600; color: var(--ink); flex-shrink: 1; }
.led-row .main .kind {
  font-size: 11px; font-weight: 700; letter-spacing: .08em; text-transform: uppercase;
  color: var(--ink-2); flex-shrink: 999;
}
.led-row .main .ref { font-size: 11px; padding: 0 6px; flex: none; overflow: visible; }
/* Only the description gives way. Part descriptions here run long enough to
   swallow a whole trailing name, and "who moved it" is the point of the line. */
.led-row .meta { grid-area: meta; display: flex; gap: 5px; min-width: 0; font-size: 12px; color: var(--muted); }
.led-row .meta .what { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.led-row .meta .who { flex: none; white-space: nowrap; }
.led-row .meta .who::before { content: "· "; }
.led-row .qty {
  grid-area: qty; justify-self: end; white-space: nowrap;
  font: 600 15px var(--mono); font-variant-numeric: tabular-nums;
}
.led-row.neg .qty { color: var(--bad); } .led-row.pos .qty { color: var(--ok); }
.led-row .qty small { font-size: 11px; font-weight: 500; color: var(--muted); }
.led-row .age { grid-area: age; justify-self: end; font-size: 12px; color: var(--muted); white-space: nowrap; }

/* fleet roster row: registration + type | airframe hours | remaining-to-due */
/* Slack goes to the countdown column, not the registration column: on a wide
   panel a fixed due column strands ~450px of white between the type and the
   airframe figure, and a longer bar reads the staircase better anyway. */
.fleet-row {
  display: grid; grid-template-columns: minmax(0, 1.4fr) auto minmax(124px, .8fr);
  grid-template-areas: "reg hrs due" "type hrs due";
  gap: 1px 14px; align-items: center; padding: 9px 16px;
  color: var(--ink); text-decoration: none;
  transition: background var(--speed-fast) var(--ease);
}
a.fleet-row:hover { background: var(--surface-2); text-decoration: none; }
.fleet-row .reg { grid-area: reg; font: 600 14px var(--mono); letter-spacing: .01em; }
.fleet-row .type {
  grid-area: type; font-size: 12px; color: var(--muted);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.fleet-row .hrs {
  grid-area: hrs; justify-self: end; color: var(--ink-2);
  font: 500 13px var(--mono); font-variant-numeric: tabular-nums;
}
.fleet-row .due { grid-area: due; display: flex; flex-direction: column; gap: 4px; }
.fleet-row .due .lbl { font-size: 11px; font-weight: 600; line-height: 1.3; }
.fleet-row .due .lbl b { display: block; font-weight: 700; font-family: var(--mono); font-variant-numeric: tabular-nums; }
.fleet-row .due .meter, .fleet-row .due .meter > i { height: 5px; border-radius: 3px; }
.fleet-row .due .meter { background: var(--muted-bg); }
/* Floor the fill so the tightest job reads as a short bar, not a dot: a 5px
   round-capped fill at 4% of the column is indistinguishable from a bullet. */
.fleet-row .due .meter > i { min-width: 11px; }

/* Column heading: the airframe figure is a bare number otherwise, and nothing
   on the row says the right-hand block is a countdown rather than a due date. */
.fleet-row.is-head {
  grid-template-areas: "reg hrs due"; padding-top: 7px; padding-bottom: 7px;
  border-bottom: 1px solid var(--line-soft); background: color-mix(in srgb, var(--surface-2) 60%, var(--surface));
}
.fleet-row.is-head > * {
  font: var(--label-font); letter-spacing: var(--label-track); text-transform: uppercase; color: var(--muted);
  white-space: nowrap;
}
.fleet-row.is-head .due { display: block; }
/* bar length is share-of-longest across the visible fleet: a pure ranking cue,
   so the sorted list falls away as a staircase with the tightest jobs at top */
.fleet-row .due .meter > i { width: var(--w, 0%); }
.fleet-row .due.t-bad .lbl { color: var(--bad); }
.fleet-row .due.t-warn .lbl { color: var(--warn); }
.fleet-row .due.t-ok .lbl { color: var(--ok); }
.fleet-row .due.t-muted .lbl { color: var(--muted); font-weight: 500; }
.fleet-row .due.t-bad .meter > i { background: var(--bad); }
.fleet-row .due.t-warn .meter > i { background: var(--warn); }
.fleet-row .due.t-ok .meter > i { background: color-mix(in srgb, var(--ok) 60%, var(--ok-line)); }
.fleet-row .due.t-muted .meter { visibility: hidden; }

@media (max-width: 420px) {
  .led-row, .fleet-row { padding-left: 12px; padding-right: 12px; }
  .fleet-row { grid-template-columns: minmax(0, 1fr) auto 108px; column-gap: 10px; }
}

/* Rows fade in along their own sort order: newest-first in the ledger, most
   urgent first in the roster. One 240ms pass, transform and opacity only. */
@media (prefers-reduced-motion: no-preference) {
  .panel-rows > * { animation: row-in 240ms var(--ease) both; animation-delay: calc(var(--i, 0) * 26ms); }
  @keyframes row-in { from { opacity: 0; transform: translateY(4px); } }
}

/* ---------- chips & badges ---------- */
.chip {
  display: inline-flex; align-items: center; gap: 6px; padding: 3px 10px 3px 8px;
  border-radius: var(--r-pill); font-size: 12px; font-weight: 600; white-space: nowrap; border: 1px solid;
}
.chip::before { content: ""; width: 7px; height: 7px; border-radius: 50%; background: var(--tone, currentColor); flex: none; }
.chip-ok   { --tone: var(--ok);   color: var(--ok);   background: var(--ok-bg);   border-color: var(--ok-line); }
.chip-warn { --tone: var(--warn); color: var(--warn); background: var(--warn-bg); border-color: var(--warn-line); }
.chip-bad  { --tone: var(--bad);  color: var(--bad);  background: var(--bad-bg);  border-color: var(--bad-line); }
.chip-info { --tone: var(--info); color: var(--info); background: var(--info-bg); border-color: var(--info-line); }
.chip-muted{ --tone: var(--muted);color: var(--muted);background: var(--muted-bg);border-color: var(--muted-line); }
/* When one status is the answer on most rows, the filled pill turns the whole
   column into a single colour field and the exceptions stop being visible.
   .chip-quiet drops the fill and keeps the tag colour in the dot, so the
   majority recedes to a legible label and the odd one out is what you see. */
.chip-quiet { background: none; border-color: transparent; color: var(--ink-2); padding-left: 0; font-weight: 500; }

/* ---------- buttons ---------- */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 7px;
  min-height: 44px; padding: 10px 18px; border-radius: var(--r-sm); border: 1px solid transparent;
  font: 600 14px/1.2 var(--font); cursor: pointer; text-decoration: none; white-space: nowrap;
  background: var(--navy-800); color: #fff;
  transition: background var(--speed), transform 80ms;
  touch-action: manipulation;
}
.btn:hover { background: var(--navy-700); text-decoration: none; }
.btn:active { transform: scale(.98); }
.btn:disabled { opacity: .45; cursor: not-allowed; }
.btn .ico { width: 17px; height: 17px; }
.btn-ghost { background: var(--surface); color: var(--ink-2); border-color: var(--line-strong); }
.btn-ghost:hover { background: var(--surface-2); color: var(--ink); }
.btn-danger { background: var(--bad); }
.btn-danger:hover { background: color-mix(in srgb, var(--bad) 84%, #000); }
.btn-accent { background: var(--accent); color: #221302; }
.btn-accent:hover { background: color-mix(in srgb, var(--accent) 88%, #000); }
[data-theme="dark"] .btn { background: var(--navy-600); }
[data-theme="dark"] .btn:hover { background: var(--navy-700); }
[data-theme="dark"] .btn-ghost { background: var(--surface); }
[data-theme="dark"] .btn-ghost:hover { background: var(--surface-2); }
[data-theme="dark"] .btn-danger { background: #A1373E; }
[data-theme="dark"] .btn-accent { background: var(--accent); color: #221302; }
.btn-sm { min-height: 36px; padding: 6px 13px; font-size: 13px; border-radius: 10px; }
.btn-block { width: 100%; }

.row-actions { display: flex; gap: 6px; flex-wrap: wrap; }
@media (min-width: 901px) { table.data td .row-actions { flex-wrap: nowrap; justify-content: flex-end; } }
.row-actions .btn-sm { min-height: 36px; }
/* glove floor: on touch layouts every button meets 44px, row actions included */
@media (max-width: 900px) {
  .btn { min-height: var(--touch); }
  .btn-sm, .row-actions .btn-sm { min-height: 44px; padding: 8px 15px; font-size: 13px; }
}

/* ---------- tables ----------
   Desktop-first ledger tables: soft sticky header, quiet zebra, aligned numerics.
   Mobile never sees these raw — .cards-mobile re-lays rows as cards. */
.tablewrap {
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--r-md); overflow-x: auto;
  /* A clipped last column under an overlay scrollbar reads as a broken page.
     These edge shadows are painted on the scroll container and only appear on
     the side that still has table left on it — the affordance is the truth. */
  background-image:
    linear-gradient(to right, var(--surface), transparent 36px),
    linear-gradient(to left,  var(--surface), transparent 36px),
    linear-gradient(to right, color-mix(in srgb, var(--ink) 13%, transparent), transparent 15px),
    linear-gradient(to left,  color-mix(in srgb, var(--ink) 13%, transparent), transparent 15px);
  background-repeat: no-repeat;
  background-size: 36px 100%, 36px 100%, 15px 100%, 15px 100%;
  background-position: 0 0, 100% 0, 0 0, 100% 0;
  background-attachment: local, local, scroll, scroll;
}
/* one density knob for the whole table rather than a variant class per screen */
table.data { width: 100%; border-collapse: collapse; font-size: 14px; min-width: 640px; --cell-x: 14px; }
table.data.dense { --cell-x: 9px; font-size: 13px; }
/* Below ~1500px an eleven-column ledger is in tight territory on a laptop and
   the generous cell gutter is what tips it past the viewport. Tighten the
   gutter before you start clipping columns off the right edge. */
@media (max-width: 1500px) { table.data { --cell-x: 10px; } }
table.data th {
  /* A two-word heading that folds onto two lines ("ON / HAND") makes the header
     row taller than it needs to be and stops the heading sitting square over its
     column. Headings here are short by design, so they never wrap. */
  text-align: left; white-space: nowrap; font: var(--label-font);
  letter-spacing: var(--label-track); text-transform: uppercase; color: var(--ink-3);
  background: var(--surface-2); padding: 10px var(--cell-x);
  border-bottom: 1px solid var(--line-strong);
}
table.data td { padding: 11px var(--cell-x); border-bottom: 1px solid var(--line-soft); vertical-align: middle; }
/* Columns that are an em-dash on most rows cost more width than they return.
   Marked .opt they leave before the table has to scroll; the fact still lives
   on the item's own page, which is where you go when you need it. */
@media (max-width: 1439px) { table.data th.opt, table.data td.opt { display: none; } }
table.data tbody tr:nth-child(even) td { background: color-mix(in srgb, var(--surface-2) 45%, transparent); }
table.data tbody tr { transition: background var(--speed-fast); }
table.data tbody tr:hover td { background: color-mix(in srgb, var(--info-bg) 55%, transparent); }
table.data tbody tr.selected td { background: var(--info-bg); box-shadow: inset 3px 0 0 var(--focus); }
table.data tr:last-child td { border-bottom: 0; }
table.data td.act, table.data th.act,
table.data td.c-act, table.data th.c-act { text-align: right; }
/* width:1% so the action column stops claiming free width. position:relative
   contains the .sr-only column name — it is position:absolute, and with no
   positioned ancestor it resolved against the initial containing block, escaped
   .tablewrap's scroll container and pushed the document to the table's own
   640px min-width on a phone. Only bites tables that are not .cards-mobile. */
table.data th.act, table.data th.c-act { width: 1%; position: relative; }
td.r, th.r { text-align: right; font-variant-numeric: tabular-nums; }
/* `table.data th` above is (0,1,2) and out-specified plain `th.r` (0,1,1), so
   every numeric heading sat left while its own figures sat right. A column
   whose heading does not stand over its numbers reads as broken before anyone
   can say why. Match the alignment at the same weight as the rule that broke it. */
table.data th.r { text-align: right; }
table.data th.c, table.data td.c { text-align: center; }
table.data tbody tr[data-href] { cursor: pointer; }

/* ---- the Inventory list reads as a ledger, so every row is the same height ----
   Two things made it ragged: a description that wraps to two lines on some rows
   and one on others, and a status cell that stacks a second chip only where a
   photograph is missing. Both are now given their space whether they use it or
   not, so the eye can run down a column of 50 rows without the rhythm breaking. */
table.data.items tbody td { height: 58px; }
/* Description is the column that should absorb the slack. Stating that stops the
   auto layout handing spare width to whichever column happens to hold the longest
   string on the page, which is what made the spacing move as you paged through. */
/* Above 1439px all eleven columns are showing (below that .opt drops four), and
   at that point the column grid is declared outright rather than negotiated with
   the content. Auto layout re-measures every page load, so the columns shifted
   depending on which part number happened to be longest on the page you were
   looking at; a stated grid puts every heading over the same edge on every page.
   Below the breakpoint the table falls back to auto layout with fewer columns. */
@media (min-width: 1440px) {
  /* Eleven columns is the widest table in the product. A 14px gutter on both
     sides of every cell is 308px of the width spent on air, which is what forced
     part numbers and status chips to truncate. Ten is still comfortably legible. */
  table.data.items { table-layout: fixed; --cell-x: 10px; }
  /* Each width is the heading's own width plus the cell gutter on both sides, so
     no heading ever runs into the one beside it. Description takes what is left. */
  table.data.items thead th:nth-child(1)  { width: 4.5%; }  /* photo     */
  table.data.items thead th:nth-child(2)  { width: 12%; }   /* part no   */
  table.data.items thead th:nth-child(3)  { width: 15%; }   /* desc      */
  table.data.items thead th:nth-child(4)  { width: 6.5%; }  /* aircraft  */
  table.data.items thead th:nth-child(5)  { width: 6%; }    /* on hand   */
  table.data.items thead th:nth-child(6)  { width: 6%; }    /* reserved  */
  table.data.items thead th:nth-child(7)  { width: 4%; }    /* min       */
  table.data.items thead th:nth-child(8)  { width: 6.5%; }  /* condition */
  table.data.items thead th:nth-child(9)  { width: 9%; }    /* location  */
  table.data.items thead th:nth-child(10) { width: 13.5%; } /* status    */
  table.data.items thead th:nth-child(11) { width: 17%; }   /* actions   */
  table.data.items td.c-act .row-actions {
    flex-wrap: wrap; justify-content: flex-end; row-gap: 6px;
  }
}
@media (min-width: 1440px) {
  table.data.items.has-value { table-layout: fixed; --cell-x: 10px; }
  table.data.items.has-value thead th:nth-child(1)  { width: 4.5%; }  /* photo     */
  table.data.items.has-value thead th:nth-child(2)  { width: 12%; }   /* part no   */
  table.data.items.has-value thead th:nth-child(3)  { width: 13%; }   /* desc      */
  table.data.items.has-value thead th:nth-child(4)  { width: 6.5%; }  /* aircraft  */
  table.data.items.has-value thead th:nth-child(5)  { width: 6%; }    /* on hand   */
  table.data.items.has-value thead th:nth-child(6)  { width: 6%; }    /* reserved  */
  table.data.items.has-value thead th:nth-child(7)  { width: 4%; }    /* min       */
  table.data.items.has-value thead th:nth-child(8)  { width: 9.5%; }  /* value     */
  table.data.items.has-value thead th:nth-child(9)  { width: 6%; }    /* condition */
  table.data.items.has-value thead th:nth-child(10) { width: 8.5%; }  /* location  */
  table.data.items.has-value thead th:nth-child(11) { width: 12%; }   /* status    */
  table.data.items.has-value thead th:nth-child(12) { width: 12%; }   /* actions   */
}
/* The cost and the words that say where it came from each stay on one line: a
   cost source broken across two lines reads as two different answers. */
table.data.items td.c-value .num { white-space: nowrap; }
table.data.items td.c-value .small {
  display: block; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
/* A part number is one token you read character by character, so it never breaks
   across lines; if it cannot fit it truncates and the row still opens the item. */
table.data.items td.c-pn { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* The link is the thing that holds the number, so it truncates too — otherwise a
   long part number paints past the cell the ellipsis belongs to. */
table.data.items td.c-pn > a { display: block; max-width: 100%; overflow: hidden; text-overflow: ellipsis; }
.desc-clamp {
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden; line-height: 1.35;
}
.desc-clamp .sub { display: block; font-size: 12px; color: var(--ink-3); }
/* Reserves the second line on every row, so a missing photograph adds a chip
   rather than adding height. */
.chipstack { display: flex; flex-direction: column; align-items: flex-start; gap: 3px; min-height: 40px; justify-content: center; }
@media (max-width: 900px) {
  table.data.items tbody td { height: auto; }
  .chipstack { min-height: 0; flex-direction: row; flex-wrap: wrap; align-items: center; }
}
/* One action column, fifty rows: at full strength that is fifty buttons
   competing with the data they belong to, and it is what pushes the table past
   the viewport. They stay present, labelled, focusable and 4.5:1 legible, and
   take their own variant back on the row you are actually on. */
@media (min-width: 901px) {
  table.data tbody tr:not(:hover):not(:focus-within) td.act .btn,
  table.data tbody tr:not(:hover):not(:focus-within) td.c-act .btn {
    background: transparent; border-color: var(--line); color: var(--ink-2); box-shadow: none;
  }
}
tr.hot td, table.data tbody tr.hot:nth-child(even) td { background: color-mix(in srgb, var(--bad-bg) 45%, transparent); }
/* freshly changed row flash (e.g. after a stock action) */
@keyframes row-flash { from { background: var(--ok-bg); } }
tr.flash td { animation: row-flash 1.6s ease-out; }

/* Data tables → cards on phones: identity, quantity, status and one action
   visible without horizontal scrolling. Optional c-meta row for detail lines. */
@media (max-width: 900px) {
  .cards-mobile { overflow-x: visible; }
  .cards-mobile table.data { min-width: 0; display: block; }
  .cards-mobile thead { display: none; }
  .cards-mobile tbody { display: block; }
  .cards-mobile tbody tr {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    grid-template-areas: "pn avail" "desc desc" "meta meta" "status act";
    gap: 5px 10px; align-items: center;
    padding: 14px 16px; border-bottom: 1px solid var(--line-soft);
  }
  .cards-mobile tbody tr:last-child { border-bottom: 0; }
  .cards-mobile table.data td { display: block; padding: 0; border-bottom: 0; background: none !important; }
  .cards-mobile td.c-pn { grid-area: pn; }
  .cards-mobile td.c-desc { grid-area: desc; max-width: none !important; font-size: 13px; color: var(--ink-2); }
  .cards-mobile td.c-avail { grid-area: avail; text-align: right; }
  .cards-mobile td.c-meta { grid-area: meta; font-size: 13px; color: var(--ink-3); }
  .cards-mobile td.c-status { grid-area: status; }
  .cards-mobile td.c-act { grid-area: act; justify-self: end; }
  .cards-mobile td.hide-mobile { display: none; }
  .cards-mobile td:has(.empty) { grid-column: 1 / -1; }
  /* action-heavy rows (item detail stock lines): actions get their own full row */
  .cards-mobile.act-row tbody tr {
    grid-template-areas: "pn avail" "desc desc" "meta meta" "status status" "act act";
  }
  .cards-mobile.act-row td.c-act { justify-self: stretch; }
  .cards-mobile.act-row td.c-act .row-actions { justify-content: flex-start; }
  .cards-mobile.act-row td.c-act .row-actions .btn-sm { flex: 1 1 auto; }

  /* Fleet table → cards: registration + remaining chip lead, hours and the
     next-inspection detail follow. Remaining is never scrolled out of sight. */
  .fleet-mobile { overflow-x: visible; }
  .fleet-mobile table.data { min-width: 0; display: block; }
  .fleet-mobile thead { display: none; }
  .fleet-mobile tbody { display: block; }
  .fleet-mobile tbody tr {
    display: grid;
    grid-template-columns: 1fr auto;
    grid-template-areas: "reg rem" "type hours" "insp insp";
    gap: 4px 10px; align-items: center;
    padding: 14px 16px; border-bottom: 1px solid var(--line-soft);
  }
  .fleet-mobile tbody tr:last-child { border-bottom: 0; }
  .fleet-mobile table.data td { display: block; padding: 0; border-bottom: 0; background: none !important; }
  .fleet-mobile td.c-reg { grid-area: reg; font-size: 15px; white-space: nowrap; }
  .fleet-mobile td.c-rem { grid-area: rem; justify-self: end; }
  .fleet-mobile td.c-type { grid-area: type; font-size: 13px; color: var(--ink-3); }
  .fleet-mobile td.c-hours { grid-area: hours; justify-self: end; }
  .fleet-mobile td.c-insp { grid-area: insp; font-size: 13px; }
  .fleet-mobile td.hide-mobile { display: none; }
  .fleet-mobile td:has(.empty) { grid-column: 1 / -1; }
}

/* ---------- real-time search results ---------- */
.search-pop {
  position: absolute; top: calc(100% + 6px); left: 0; right: 0; z-index: 80;
  background: var(--surface); color: var(--ink);
  border: 1px solid var(--line); border-radius: var(--r-md); box-shadow: var(--shadow-3);
  max-height: min(66dvh, 520px); overflow-y: auto; overscroll-behavior: contain;
  animation: pop-in 140ms var(--ease);
}
@keyframes pop-in { from { opacity: 0; transform: translateY(-4px); } }
.search-pop.inline { position: static; box-shadow: var(--shadow-1); animation: none; margin-top: 8px; max-height: none; }
.sp-group {
  position: sticky; top: 0; padding: 8px 14px 5px; font: var(--label-font);
  letter-spacing: var(--label-track); text-transform: uppercase; color: var(--ink-3);
  background: var(--surface-2); border-bottom: 1px solid var(--line-soft);
}
.sp-row {
  display: flex; align-items: center; gap: 10px; padding: 12px 14px; min-height: var(--touch);
  border-bottom: 1px solid var(--line-soft); color: var(--ink); text-decoration: none;
  transition: background 100ms;
}
.sp-row:last-child { border-bottom: 0; }
.sp-row:hover, .sp-row.on { background: var(--info-bg); text-decoration: none; }
.sp-row.on { outline: 2px solid var(--focus); outline-offset: -2px; }
.sp-thumb {
  width:34px;
}
.sp-main { display: flex; flex-direction: column; gap: 1px; min-width: 0; flex: 1; }
.sp-pn { font-family: var(--mono); font-weight: 600; font-size: 14px; letter-spacing: .01em; }
.sp-desc { font-size: 13px; color: var(--ink-2); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.sp-meta { font-size: 12px; color: var(--ink-3); }
.sp-avail { flex: none; }
.sp-row mark { background: color-mix(in srgb, var(--accent) 30%, transparent); color: inherit; border-radius: 2px; padding: 0 1px; }
.sp-empty { padding: 16px 14px; font-size: 13px; color: var(--ink-3); }
.sp-all { justify-content: center; font-size: 13px; font-weight: 600; color: var(--info); background: var(--surface-2); }
.lookup-results { position: relative; }
input.picked { border-color: var(--ok-line); background: var(--ok-bg); }
@media (max-width: 900px) {
  .search-pop:not(.inline) { position: fixed; top: 60px; left: 8px; right: 8px; max-height: 70dvh; }
}

/* ---------- walkthroughs ---------- */
.wt-overlay { position: fixed; inset: 0; z-index: 140; background: rgba(10, 11, 30, .38); }
.wt-target { position: relative; z-index: 145; outline: 3px solid var(--accent); outline-offset: 3px; border-radius: 6px; }
.wt-card {
  position: fixed; z-index: 150; width: min(340px, calc(100vw - 24px));
  background: var(--surface); color: var(--ink); border: 1px solid var(--line);
  border-radius: var(--r-md); box-shadow: var(--shadow-3); padding: 16px 18px;
  animation: pop-in 160ms var(--ease);
}
.wt-card.wt-center { top: 50%; left: 50%; transform: translate(-50%, -50%); }
.wt-card p { margin: 8px 0 10px; font-size: 14px; line-height: 1.5; color: var(--ink-2); }
.wt-head { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.wt-head b { font-size: 15px; }
.wt-head .x { width: 44px; height: 44px; font-size: 14px; flex: none; }
.wt-foot { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.wt-progress { font: 600 12px var(--mono); color: var(--ink-3); }
.wt-btns { display: flex; gap: 6px; }

/* ---------- present mode ----------
   Its own typeface: Plus Jakarta Sans, the JAGS family face (OFL, self-hosted because
   the CSP allows fonts from 'self' only). Browsers fetch it only when a presentation
   draws, so the everyday app pays nothing for it. */
.pz-card, .pz-bar, .pz-splash { --pz-font: "Jakarta", var(--font); font-family: var(--pz-font); }
.pz-card .btn { font-family: var(--pz-font); }
/*
   The spotlight is a transparent box with an enormous shadow, so the cut-out is
   real rather than four divs pretending to be one. The veil above everything is
   transparent and only catches clicks; hands-on mode hides it so the presenter can
   use the real app while the dimming stays. */
.pz-veil { position: fixed; inset: 0; z-index: 900; cursor: pointer; background: transparent; }
.pz-hole {
  position: fixed; z-index: 901; pointer-events: none; border-radius: 14px;
  box-shadow: 0 0 0 9999px rgba(8, 10, 28, .62), 0 0 0 2px var(--accent), 0 0 0 7px color-mix(in srgb, var(--accent) 26%, transparent);
  transition: top 320ms var(--ease), left 320ms var(--ease), width 320ms var(--ease), height 320ms var(--ease);
}
.pz-hole[hidden] { display: block !important; top: 50% !important; left: 50% !important; width: 0 !important; height: 0 !important; box-shadow: 0 0 0 9999px rgba(8, 10, 28, .72); }
.pz-hands-on .pz-hole { box-shadow: 0 0 0 9999px rgba(8, 10, 28, .34), 0 0 0 2px var(--accent); }
.pz-card {
  position: fixed; z-index: 902; width: min(420px, calc(100vw - 24px));
  background: var(--surface); color: var(--ink); border: 1px solid var(--line);
  border-radius: 20px; box-shadow: 0 24px 60px rgba(0, 0, 0, .35); padding: 20px 22px 16px;
  transition: top 320ms var(--ease), left 320ms var(--ease);
}
.pz-card.pz-center { top: 42% !important; left: 50% !important; transform: translate(-50%, -50%); width: min(560px, calc(100vw - 24px)); padding: 30px 32px 22px; }
.pz-card.pz-center h3 { font-size: 30px; }
.pz-card.pz-center p { font-size: 19px; }
.pz-card.pz-enter { animation: pz-in 260ms var(--ease) both; }
.pz-card.pz-center.pz-enter { animation-name: pz-in-center; }
@keyframes pz-in { from { opacity: 0; translate: 0 6px; } }
@keyframes pz-in-center { from { opacity: 0; translate: 0 10px; } }
.pz-ch { display: flex; align-items: baseline; gap: 8px; margin-bottom: 8px; font: 700 11.5px/1 var(--pz-font); letter-spacing: .08em; text-transform: uppercase; color: var(--ink-3); }
.pz-ch b { font: 800 12px/1 var(--pz-font); color: var(--accent-ink); font-variant-numeric: tabular-nums; }
.pz-card h3 { margin: 0 0 8px; font-size: 23px; font-weight: 750; line-height: 1.15; letter-spacing: -.02em; }
.pz-card p { margin: 0 0 16px; font-size: 17px; font-weight: 450; line-height: 1.5; color: var(--ink-2); text-wrap: pretty; }
.pz-acts { display: flex; align-items: center; gap: 6px; }
.pz-keys { flex: 1; font: 500 12px var(--pz-font); color: var(--ink-3); }

.pz-bar {
  position: fixed; z-index: 903; left: 50%; bottom: calc(14px + env(safe-area-inset-bottom)); translate: -50% 0;
  width: min(760px, calc(100vw - 24px)); display: flex; align-items: center; gap: 6px; padding: 6px;
  background: rgba(14, 17, 40, .92); color: #E8EBF8; border: 1px solid rgba(255, 255, 255, .1);
  border-radius: var(--r-pill); box-shadow: 0 14px 40px rgba(0, 0, 0, .4); backdrop-filter: blur(10px);
}
.pz-b {
  flex: none; height: 36px; min-width: 36px; padding: 0 12px; border: 0; border-radius: var(--r-pill);
  background: rgba(255, 255, 255, .08); color: inherit; font: 600 13px/1 var(--pz-font); cursor: pointer;
}
.pz-b:hover { background: rgba(255, 255, 255, .16); }
.pz-b[data-pz="back"], .pz-b[data-pz="next"] { font-size: 20px; padding: 0; }
.pz-hands[aria-pressed="true"] { background: var(--accent); color: #fff; }
.pz-track { flex: 1; display: flex; gap: 4px; min-width: 0; }
.pz-seg {
  position: relative; min-width: 0; height: 36px; padding: 0 8px; border: 0; border-radius: 10px; overflow: hidden;
  background: rgba(255, 255, 255, .06); color: rgba(232, 235, 248, .6); cursor: pointer; text-align: left;
}
.pz-seg i { position: absolute; left: 0; bottom: 0; height: 3px; background: var(--accent); transition: width 320ms var(--ease); }
.pz-seg span { display: block; font: 600 12px/36px var(--pz-font); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.pz-seg.on { background: rgba(255, 255, 255, .14); color: #fff; }
.pz-count { flex: none; font: 700 12.5px var(--pz-font); font-variant-numeric: tabular-nums; color: rgba(232, 235, 248, .7); padding: 0 4px; }

.pz-splash {
  position: fixed; inset: 0; z-index: 904; pointer-events: none; display: grid; place-content: center; text-align: center;
  color: #fff; opacity: 0;
}
.pz-splash b { display: block; font: 800 clamp(72px, 13vw, 150px)/1 var(--pz-font); color: var(--accent); letter-spacing: -.05em; font-variant-numeric: tabular-nums; }
.pz-splash span { display: block; margin-top: 8px; font: 750 clamp(28px, 4.6vw, 52px)/1.1 var(--pz-font); letter-spacing: -.025em; }
.pz-splash.play { animation: pz-splash 1250ms var(--ease) both; }
@keyframes pz-splash {
  0% { opacity: 0; background: rgba(8, 10, 28, 0); transform: scale(.96); }
  22%, 62% { opacity: 1; background: rgba(8, 10, 28, .9); transform: none; }
  100% { opacity: 0; background: rgba(8, 10, 28, 0); transform: scale(1.02); }
}
/* The bottom nav and the toasts sit under the veil; the presenter bar replaces them. */
.pz-on .bottom-nav { visibility: hidden; }
@media (max-width: 700px) {
  .pz-keys, .pz-seg span { display: none; }
  .pz-acts { justify-content: flex-end; }
  .pz-seg { padding: 0; height: 10px; border-radius: 5px; }
  .pz-seg i { height: 100%; }
  .pz-card h3 { font-size: 21px; }
  .pz-card p { font-size: 16px; }
  .pz-card.pz-center h3 { font-size: 24px; }
  .pz-card.pz-center p { font-size: 17px; }
}
@media (prefers-reduced-motion: reduce) {
  .pz-hole, .pz-card, .pz-seg i { transition: none; }
  .pz-card.pz-enter { animation: none; }
}
/* Live figures. A column on the right of a wide screen, with the app narrowed to
   make room so nothing being presented hides under it. Below 1100px it goes, and
   the first three figures ride in the caption card as chips instead. */
.pz-panel {
  position: fixed; z-index: 903; top: 12px; right: 12px; bottom: calc(12px + env(safe-area-inset-bottom)); width: 320px;
  display: none; flex-direction: column; gap: 10px; padding: 18px 18px 14px; overflow-y: auto;
  font-family: var(--pz-font, "Jakarta", var(--font));
  background: rgba(14, 17, 40, .94); color: #E8EBF8; border: 1px solid rgba(255, 255, 255, .1);
  border-radius: 22px; box-shadow: 0 20px 50px rgba(0, 0, 0, .4); backdrop-filter: blur(12px);
}
.pz-p-head { display: flex; align-items: center; justify-content: space-between; font: 700 11.5px/1 var(--pz-font, var(--font)); letter-spacing: .08em; text-transform: uppercase; color: rgba(232, 235, 248, .6); }
.pz-live { display: inline-flex; align-items: center; gap: 7px; color: #7EE0A8; }
.pz-live i { width: 8px; height: 8px; border-radius: 50%; background: #3CCB7F; box-shadow: 0 0 0 0 rgba(60, 203, 127, .6); animation: pz-pulse 1.8s ease-out infinite; }
@keyframes pz-pulse { to { box-shadow: 0 0 0 9px rgba(60, 203, 127, 0); } }
.pz-panel h4 { margin: 2px 0 2px; font-size: 20px; font-weight: 750; letter-spacing: -.02em; color: #fff; }
.pz-figs { display: flex; flex-direction: column; gap: 8px; }
.pz-fig {
  display: grid; grid-template-columns: auto 1fr; column-gap: 12px; align-items: baseline;
  padding: 12px 14px; border-radius: 14px; background: rgba(255, 255, 255, .06);
  animation: pz-fig-in 360ms var(--ease) both;
}
@keyframes pz-fig-in { from { opacity: 0; translate: 0 8px; } }
.pz-fig b { grid-row: span 2; font-size: 34px; font-weight: 800; line-height: 1; letter-spacing: -.03em; color: var(--accent); font-variant-numeric: tabular-nums; }
.pz-fig .l { font-size: 15px; font-weight: 700; color: #fff; line-height: 1.2; }
.pz-fig .h { font-size: 13px; font-weight: 450; color: rgba(232, 235, 248, .7); line-height: 1.35; }
.pz-none { margin: 4px 0; font-size: 14px; color: rgba(232, 235, 248, .6); }
.pz-seat { margin: auto 0 0; padding-top: 10px; border-top: 1px solid rgba(255, 255, 255, .1); font-size: 13px; line-height: 1.45; color: rgba(232, 235, 248, .72); }
.pz-seat b { color: #fff; }
.pz-brand { display: flex; align-items: center; gap: 8px; font-size: 11px; font-weight: 600; letter-spacing: .06em; text-transform: uppercase; color: rgba(232, 235, 248, .5); }
.pz-brand img { height: 30px; width: auto; filter: invert(1); opacity: .9; }
.pz-chips { display: flex; flex-wrap: wrap; gap: 6px; margin: -6px 0 14px; }
.pz-chip { display: inline-flex; align-items: baseline; gap: 5px; padding: 5px 10px; border-radius: var(--r-pill); background: var(--surface-2); font-size: 13px; font-weight: 600; color: var(--ink-2); }
.pz-chip b { font-size: 15px; font-weight: 800; color: var(--accent-ink); font-variant-numeric: tabular-nums; }
.pz-splash em { display: flex; align-items: center; justify-content: center; gap: 8px; margin-top: 18px; font: 600 15px/1 var(--pz-font, var(--font)); font-style: normal; letter-spacing: .02em; color: rgba(255, 255, 255, .75); }
.pz-splash em i { font-style: normal; opacity: .5; }
.pz-splash em img { height: 38px; width: auto; filter: invert(1); }
/* Example data. Swapped-in sections wear a label so nobody mistakes them for the
   live record; an example detail screen is laid over the page it belongs to. */
.pz-demo { position: relative; outline: 2px dashed color-mix(in srgb, var(--accent) 70%, transparent); outline-offset: 4px; border-radius: 10px; }
.pz-demo::after {
  /* Inside the element, not hanging off its edge: tables scroll in a wrapper that
     clips anything outside it, which cut the label in half. */
  content: "Example"; position: absolute; top: 6px; right: 8px; z-index: 5; padding: 3px 9px; border-radius: var(--r-pill);
  box-shadow: 0 2px 8px rgba(0, 0, 0, .25); pointer-events: none;
  background: var(--accent); color: #fff; font: 700 10.5px/1.2 "Jakarta", var(--font); letter-spacing: .08em; text-transform: uppercase;
}
.pz-frame { position: fixed; bottom: 0; z-index: 850; overflow-y: auto; background: var(--bg); border-top: 3px solid var(--accent); }
.pz-frame-body { padding-block: 20px 120px; }
.pz-frame-tag {
  position: sticky; top: 0; z-index: 3; display: block; padding: 6px 16px; text-align: center;
  background: var(--accent); color: #fff; font: 700 11px/1.3 "Jakarta", var(--font); letter-spacing: .1em; text-transform: uppercase;
}
.pz-brand { flex-wrap: wrap; }
.pz-switch {
  margin-left: auto; display: inline-flex; align-items: center; gap: 8px; height: 30px; padding: 0 10px 0 4px;
  border: 1px solid rgba(255, 255, 255, .16); border-radius: var(--r-pill); background: rgba(255, 255, 255, .06);
  color: #E8EBF8; font: 600 12px/1 var(--pz-font, var(--font)); letter-spacing: 0; text-transform: none; cursor: pointer;
}
.pz-switch i { position: relative; width: 34px; height: 20px; border-radius: 10px; background: rgba(255, 255, 255, .22); transition: background 180ms var(--ease); }
.pz-switch i::after { content: ""; position: absolute; top: 2px; left: 2px; width: 16px; height: 16px; border-radius: 50%; background: #fff; transition: transform 180ms var(--ease); }
.pz-switch[aria-checked="true"] i { background: var(--accent); }
.pz-switch[aria-checked="true"] i::after { transform: translateX(14px); }
.pz-panel.pz-sim .pz-live { color: #F3B37A; }
.pz-panel.pz-sim .pz-live i { background: var(--accent); animation: none; }
.pz-panel.pz-sim .pz-asof { visibility: hidden; }
.pz-simb[aria-pressed="true"] { background: var(--accent); color: #fff; }
@media (min-width: 1100px) {
  .pz-simb { display: none; }
  .pz-panel { display: flex; }
  .pz-chips { display: none; }
  .pz-on .shell { margin-right: 344px; }
  .pz-bar { left: calc((100vw - 344px) / 2); width: min(760px, calc(100vw - 344px - 24px)); }
  .pz-card.pz-center { left: calc((100vw - 344px) / 2) !important; }
}
@media (prefers-reduced-motion: reduce) { .pz-fig, .pz-live i { animation: none; } }
@media print { .pz-veil, .pz-hole, .pz-card, .pz-bar, .pz-splash, .pz-panel { display: none !important; } }

/* help topics */
#help-topics details { border-bottom: 1px solid var(--line-soft); padding: 4px 0; }
#help-topics details:last-child { border-bottom: 0; }
#help-topics summary { cursor: pointer; font-weight: 600; font-size: 14px; padding: 13px 0; min-height: 44px; box-sizing: border-box; }
#help-topics summary:hover { color: var(--info); }
#help-topics p { margin: 4px 0 10px; }

/* ---------- forms ---------- */
.field { display: flex; flex-direction: column; gap: 6px; margin-bottom: 14px; }
.field > label { font-size: 13px; font-weight: 600; color: var(--ink-2); }
.field .help { font-size: 12px; color: var(--ink-3); }
.field .err { font-size: 12px; color: var(--bad); font-weight: 600; }
input:not([type]), input[type="text"], input[type="search"], input[type="number"], input[type="date"], input[type="email"],
input[type="password"], select, textarea {
  font: inherit; font-size: 16px; color: var(--ink); background: var(--surface);
  border: 1px solid var(--line-strong); border-radius: var(--r-sm); padding: 11px 14px; min-height: var(--touch); width: 100%;
}
textarea { min-height: 84px; resize: vertical; }
input:focus, select:focus, textarea:focus { border-color: var(--focus); outline: 2px solid color-mix(in srgb, var(--focus) 35%, transparent); outline-offset: 0; }
input:disabled, select:disabled, textarea:disabled { background: var(--surface-2); color: var(--ink-3); }
.grid-2 { display: grid; grid-template-columns: 1fr 1fr; gap: 0 14px; }
.grid-3 { display: grid; grid-template-columns: repeat(3, 1fr); gap: 0 14px; }
.grid-2 > *, .grid-3 > * { min-width: 0; }
@media (max-width: 640px) { .grid-2, .grid-3 { grid-template-columns: 1fr; } }
.seg { display: inline-flex; position: relative; background: var(--muted-bg); border-radius: var(--r-pill); padding: 3px; gap: 2px; flex-wrap: wrap; }
.seg label {
  display: inline-flex; align-items: center; padding: 8px 14px; border-radius: var(--r-pill);
  font-size: 13px; font-weight: 600; color: var(--ink-2); cursor: pointer; min-height: 36px;
}
.seg input { position: absolute; left: 4px; top: 4px; width: 1px; height: 1px; opacity: 0; pointer-events: none; }
.seg input:checked + label, .seg label:has(input:checked) { background: var(--surface); color: var(--ink); box-shadow: var(--shadow-1); }
[data-theme="dark"] .seg input:checked + label, [data-theme="dark"] .seg label:has(input:checked) { background: var(--navy-600); color: #fff; }
@media (max-width: 900px) { .seg label { min-height: 44px; } }
/* filter bars on phones: search full width, segments scroll on one line */
@media (max-width: 900px) {
  form.filters > input[type="search"] { flex: 1 1 100%; max-width: none !important; }
  form.filters .seg { flex-wrap: nowrap; overflow-x: auto; max-width: 100%; scrollbar-width: none; }
  form.filters .seg label { white-space: nowrap; }
}

/* ---------- sheet / modal ---------- */
.scrim {
  position: fixed; inset: 0; z-index: 90; background: rgba(10, 11, 30, .55);
  opacity: 0; pointer-events: none; transition: opacity var(--speed);
}
[data-theme="dark"] .scrim { background: rgba(0, 0, 0, .68); }
.scrim.open { opacity: 1; pointer-events: auto; }
.sheet {
  position: fixed; z-index: 95; inset: auto 0 0 0; margin: 0 auto; max-width: 560px;
  background: var(--surface); border-radius: var(--r-lg) var(--r-lg) 0 0; box-shadow: var(--shadow-3);
  transform: translateY(102%); transition: transform 240ms cubic-bezier(.32,.72,.33,1);
  max-height: 88dvh; display: flex; flex-direction: column;
  /* A closed sheet must never take the pointer. On desktop it is centred with
     opacity:0 (see the 720px rule) and would otherwise swallow every click in
     a 560x720 box in the middle of the page. Same pattern as .scrim above. */
  pointer-events: none;
}
.sheet.open { transform: translateY(0); pointer-events: auto; }
.sheet-grip { width: 44px; height: 5px; border-radius: 3px; background: var(--muted-line); margin: 10px auto 2px; flex: none; }
.sheet-head { display: flex; align-items: center; gap: 10px; padding: 8px 20px 12px; border-bottom: 1px solid var(--line-soft); flex: none; }
.sheet-head h2 { margin: 0; font-size: 17px; }
.sheet-head .x { margin-left: auto; }
.sheet-body { padding: 16px 20px calc(20px + env(safe-area-inset-bottom)); overflow-y: auto; }
@media (min-width: 720px) {
  .sheet { inset: 0; margin: auto; border-radius: var(--r-lg); max-height: min(86dvh, 720px); height: fit-content; transform: translateY(14px); opacity: 0; }
  .sheet.open { transform: none; opacity: 1; }
}
.x {
  display: grid; place-items: center; width: 44px; height: 44px; border-radius: var(--r-sm);
  border: 0; background: var(--muted-bg); color: var(--ink-2); cursor: pointer; font-size: 17px;
}
/* Drawn, at the icon set's own stroke, rather than whatever the platform font
   renders for U+2715. */
.x .ico { width: 17px; height: 17px; }
.x:hover { background: var(--surface-2); color: var(--ink); }
/* the primary action of a sheet form stays reachable above the keyboard */
.sheet-body form > .btn-block:last-child, .sheet-body > .btn-block:last-child {
  position: sticky; bottom: 0; box-shadow: 0 -10px 14px -8px var(--surface);
}

/* the More menu sheet */
.more-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
.more-grid a, .more-grid button {
  display: flex; align-items: center; gap: 10px; padding: 12px 14px; min-height: 52px;
  border: 1px solid var(--line); border-radius: var(--r-md); background: var(--surface);
  color: var(--ink); font-size: 13px; font-weight: 600; text-decoration: none;
}
.more-grid a:hover, .more-grid button:hover { background: var(--surface-2); text-decoration: none; }
.more-grid button { cursor: pointer; font-family: inherit; }
/* The one item here that is not navigation: it takes the full row and carries a
   little more weight, because a person who has just hit something confusing
   should not have to hunt for where to say so. Emphasis by border and colour,
   not by an accent rail. */
.more-grid .more-suggest { grid-column: 1 / -1; border-color: var(--navy-400); color: var(--navy-700); }
[data-theme="dark"] .more-grid .more-suggest { border-color: var(--navy-500); color: var(--navy-200); }
.more-grid a .ico, .more-grid button .ico { width: 19px; height: 19px; color: var(--navy-700); flex: none; }
[data-theme="dark"] .more-grid a .ico, [data-theme="dark"] .more-grid button .ico { color: var(--navy-300); }
.more-grid a .count { margin-left: auto; font: 600 11px/18px var(--mono); background: var(--bad-bg); color: var(--bad); border-radius: var(--r-pill); padding: 0 7px; }
.more-label { font: var(--label-font); letter-spacing: var(--label-track); color: var(--ink-3); text-transform: uppercase; margin: 14px 0 8px; }
.more-label:first-child { margin-top: 0; }
.more-foot { display: flex; gap: 8px; margin-top: 16px; }
.more-foot form { flex: 1; }

/* toasts */
/* top respects the safe area: installed to the home screen, a plain 12px puts
   every toast under the Dynamic Island. */
.toasts { position: fixed; z-index: 120; top: calc(12px + env(safe-area-inset-top)); right: 12px; left: 12px; display: flex; flex-direction: column; gap: 8px; align-items: flex-end; pointer-events: none; }
.toast {
  display: flex; gap: 9px; align-items: flex-start; max-width: 420px; padding: 12px 16px;
  border-radius: var(--r-sm); background: var(--surface); color: var(--ink);
  border: 1px solid var(--line); box-shadow: var(--shadow-3);
  font-size: 14px; font-weight: 500; pointer-events: auto; animation: toast-in 220ms var(--ease);
}
.toast.ok { border-color: var(--ok-line); } .toast.err { border-color: var(--bad-line); color: var(--bad); }
@keyframes toast-in { from { transform: translateY(-8px); opacity: 0; } }

/* ---------- figures (.stat on a card, .metric standalone) ----------
   Status speaks through the figure itself. A tinted tile behind every number
   is wallpaper, so a .stat opts its card out of the tag tint and colours the
   value instead — the same rule .metric has always followed. */
.stat { padding: 13px 15px; }
.tagcard.stat { background: var(--surface); border-color: var(--line); }
.stat .v { font: 700 23px/1.15 var(--mono); }
.stat .l { font: var(--label-font); color: var(--ink-3); letter-spacing: var(--label-track); text-transform: uppercase; }
.stat.s-bad  .v, .tagcard.t-bad.stat  .v, .metric-bad  .metric-v { color: var(--bad); }
.stat.s-warn .v, .tagcard.t-warn.stat .v, .metric-warn .metric-v { color: var(--warn); }
.stat.s-ok   .v, .tagcard.t-ok.stat   .v, .metric-ok   .metric-v { color: var(--ok); }
.tagcard.t-info.stat .v { color: var(--info); }


/* item hero — identity header for detail pages */
.item-hero {
  background: linear-gradient(180deg, var(--bg-accent), var(--surface) 78%);
  border: 1px solid var(--line); border-radius: var(--r-lg); box-shadow: var(--shadow-1);
  padding: 20px var(--pad) 16px; margin-bottom: 16px;
}
[data-theme="dark"] .item-hero { background: linear-gradient(180deg, var(--bg-accent), var(--surface) 78%); }
.item-hero .ih-top { display: flex; flex-wrap: wrap; align-items: flex-start; gap: 10px 14px; }
.item-hero .ih-id { flex: 1; min-width: 240px; }
.item-hero h1 { margin: 0 0 2px; font-size: clamp(21px, 3vw, 26px); }
.item-hero .ih-desc { font-size: 15px; color: var(--ink-2); margin: 0 0 8px; }
.item-hero .ih-chips { display: flex; flex-wrap: wrap; gap: 6px; }
.item-hero .ih-chips .ref { font-size: 12px; }
.item-hero .ih-actions { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }
@media (max-width: 900px) {
  .item-hero .ih-actions { width: 100%; }
  .item-hero .ih-actions .btn { flex: 1; }
}

/* ============================================================
   Catalogue pictures
   Parts are photographed on a bench, not in a studio — shared picture boxes
   keep the crop, centre and empty state consistent everywhere.
   ============================================================ */
.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}
/* The uncropped original is always one tap away: every picture links to it. */
.pic { display:block; height:auto; aspect-ratio:1/1; object-fit:cover; object-position:50% 50%; background:var(--bg-accent); border:1px solid var(--line-soft); border-radius:var(--r-sm); flex:none; }
.pic-empty { display: grid; place-items: center; color: var(--ink-3); }
.pic-empty .ico { width: 45%; height: 45%; opacity: .5; }
.pic-thumb { width:46px; }
.pic-edit { width:96px; }
.pic-hero { width:190px; border-radius:var(--r-md); }
.pic-fill { width:100%; }
.pic-wide { aspect-ratio:4/3; }
.pic-free { aspect-ratio:auto; max-height:60vh; object-fit:contain; background:var(--surface-2); }
a:has(> .pic) { display:block; }

/* line item + its picture: invoice lines, request summary, anywhere a row names a part */
.line-row { display: flex; align-items: center; gap: 10px; }
/* the picture column costs the least horizontal room it can: this table already
   scrolls sideways on a laptop and every pixel here comes off the description */
td.c-thumb, th.c-thumb { width: 1%; padding-right: 0; padding-left: 10px; }

.item-hero .ih-photo {
  display: flex; flex-direction: column; align-items: center; gap: 6px;
  max-width: 280px; text-decoration: none; color: var(--ink-3);
}
.item-hero .ih-photo .pic { background: var(--surface); }
/* What the picture claims, directly under it. A reference has to be recognisable
   as one at a glance — the provenance panel below is for the detail, not the claim. */
.pic-claim {
  align-self: flex-start;
  margin-top: 2px;
  white-space: normal;
  text-align: left;
  line-height: 1.25;
  max-width: min(280px, 100%);
}
.pic-credit {
  font-size: 11px; color: var(--ink-3); text-decoration: none; max-width: 190px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.pic-credit:hover { color: var(--ink-2); text-decoration: underline; }
.photo-provenance { width: min(280px, 100%); font-size: 11px; color: var(--ink-3); }
.photo-provenance summary { cursor: pointer; color: var(--ink-2); }
.photo-provenance dl { margin: 6px 0 0; padding: 7px 9px; border: 1px solid var(--line-soft); border-radius: var(--r-sm); background: var(--surface-2); }
.photo-provenance dl div { display: grid; grid-template-columns: 68px 1fr; gap: 8px; padding: 2px 0; }
.photo-provenance dt { font-weight: 700; }
.photo-provenance dd { margin: 0; overflow-wrap: anywhere; }

/* Reporting a catalogue picture as the wrong part. Deliberately small and quiet:
   it sits under a picture that is probably fine, and it must not read like an
   alarm about the stock. The open report is the loud state, not the offer. */
/* Both sit in the 190px picture column and must not widen it: the identity block
   beside them is what the page is actually about. */
.pic-report { width: 190px; max-width: 100%; font-size: 11px; margin-top: 2px; }
.pic-report summary { cursor: pointer; color: var(--ink-3); }
.pic-report summary:hover { color: var(--ink-2); }
.pic-report form { display: grid; gap: 6px; margin-top: 6px; }
.pic-report input { font-size: 12px; min-height: 34px; }
.pic-flagged {
  width: 190px; max-width: 100%; margin: 6px 0 0; padding: 7px 9px; font-size: 11px; line-height: 1.4;
  border: 1px solid var(--warn-line); background: var(--warn-bg); border-radius: var(--r-sm); color: var(--ink-2);
}
.item-hero .ih-photo form { width: 190px; max-width: 100%; }
.item-hero .ih-photo form .btn { width: 100%; }
.item-hero .ih-photo-add .pic { border-style: dashed; border-color: var(--line-strong); background: var(--bg-accent); }
.photo-field { display: flex; align-items: flex-start; gap: 14px; flex-wrap: wrap; }
.photo-field input[type="file"] { max-width: 320px; }

@media (max-width: 900px) {
  /* the photo leads the card on mobile — recognition first, then the numbers */
  .cards-mobile tbody tr:has(td.c-thumb) {
    grid-template-columns: auto minmax(0, 1fr) auto;
    /* The actions get their own row rather than sharing the third column. That
       column is sized by whatever is widest in it, and two buttons are 156px, so
       sharing it left roughly 90px for the part number and a real one wrapped
       four lines deep. On its own row the part number gets the width it needs and
       renders on one line, which is the whole point of showing it. */
    grid-template-areas: "thumb pn avail" "thumb desc desc" "thumb meta meta" "status status status" "act act act";
  }
  .cards-mobile td.c-thumb { grid-area: thumb; align-self: start; }
  .cards-mobile table.data.items td.c-thumb { width: auto; min-width: 0; }
  .cards-mobile table.data.items td.c-thumb .pic-thumb { max-width: 100%; height: auto; }
  /* A truncated part number identifies nothing. On a card there is no second
     place to read it, so it wraps to as many lines as it needs and the ellipsis
     that belongs to the desktop table is turned off here. Breaking mid-token is
     allowed because the alternative is 06-04005-05... which names no part. */
  /* Selector carries .cards-mobile AND table.data.items so it out-specifies the
     table's own `td.c-pn { white-space: nowrap }`, which is (0,3,2) and would
     otherwise keep winning inside this media query. */
  .cards-mobile table.data.items td.c-pn {
    white-space: normal; overflow: visible; text-overflow: clip;
    overflow-wrap: anywhere; word-break: break-word; line-height: 1.25;
  }
  .item-hero .ih-photo { width: 100%; }
  .pic-hero { width:100%; max-width:280px; margin-inline:auto; }
}

/* item availability meter */
.meter { height: 6px; border-radius: 3px; background: var(--muted-bg); overflow: hidden; }
.meter > i { display: block; height: 100%; border-radius: 3px; background: var(--ok-line); }
.meter.low > i { background: var(--warn-line); } .meter.zero > i { background: var(--bad-line); }

/* movement timeline */
.timeline { list-style: none; margin: 0; padding: 0; }
.timeline li { position: relative; padding: 10px 0 10px 26px; border-bottom: 1px solid var(--line-soft); }
.timeline li::before {
  content: ""; position: absolute; left: 7px; top: 17px; width: 9px; height: 9px; border-radius: 50%;
  background: var(--surface); border: 2.5px solid var(--info-line);
}
.timeline li.neg::before { border-color: var(--bad-line); } .timeline li.pos::before { border-color: var(--ok-line); }
.timeline li::after { content: ""; position: absolute; left: 11px; top: 28px; bottom: -8px; width: 1.5px; background: var(--line-soft); }
.timeline li:last-child::after { display: none; }

/* stock count — progress + location-driven flow */
.count-progress { height: 8px; border-radius: 4px; background: var(--muted-bg); overflow: hidden; }
.count-progress > i { display: block; height: 100%; border-radius: 4px; background: var(--navy-600); transition: width var(--speed) var(--ease); }
[data-theme="dark"] .count-progress > i { background: var(--navy-400); }
.loc-row {
  display: grid; grid-template-columns: 1fr auto; grid-template-areas: "name meta" "bar bar";
  gap: 8px 10px; align-items: center; padding: 14px 16px; min-height: 56px;
  border: 1px solid var(--line); border-radius: var(--r-md); background: var(--surface);
  color: var(--ink); text-decoration: none;
}
.loc-row:hover { border-color: var(--info-line); text-decoration: none; }
.loc-row.is-done { opacity: .72; }
.loc-row .loc-name { grid-area: name; font-weight: 600; font-size: 14px; }
.loc-row .loc-meta { grid-area: meta; display: flex; align-items: center; gap: 8px; }
.loc-row .loc-bar { grid-area: bar; height: 5px; }
.count-continue { min-height: 56px; font-size: 15px; }
.count-line { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 14px; }
.count-line .cl-main { flex: 1 1 220px; min-width: 0; display: flex; flex-direction: column; gap: 1px; }
.count-line .cl-desc { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.count-line .cl-data { display: flex; flex-wrap: wrap; align-items: center; gap: 4px 12px; }
.count-line .cl-entry { display: flex; gap: 8px; flex: none; align-items: center; }
.count-line .cl-entry input[name="qty"] { width: 110px; min-height: var(--touch); text-align: center; font-weight: 700; }
@media (max-width: 900px) {
  .count-line .cl-entry { width: 100%; }
  .count-line .cl-entry input[name="qty"] { flex: 1; }
}

/* login */
.auth-wrap { min-height: 100dvh; display: grid; place-items: center; padding: 20px; background:
  radial-gradient(1100px 640px at 88% -10%, color-mix(in srgb, var(--navy-300) 26%, transparent), transparent 60%),
  radial-gradient(900px 600px at -10% 110%, color-mix(in srgb, var(--navy-400) 18%, transparent), transparent 55%), var(--bg); }
.auth-card { width: 100%; max-width: 400px; background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-lg); box-shadow: var(--shadow-3); overflow: hidden; }
.auth-head { padding: 26px 26px 20px; background: linear-gradient(150deg, var(--navy-900), var(--navy-700)); color: #fff; }
.auth-head .brand-mark { width: 46px; height: 46px; border-radius: var(--r-sm); margin-bottom: 12px; }
.auth-head .brand-mark img { width: 34px; height: 34px; }
.auth-head h1 { font-size: 20px; margin: 0; } .auth-head p { margin: 4px 0 0; font-size: 13px; color: var(--navy-300); }
.auth-body { padding: 22px 26px 26px; }

/* error page */
.error-page { display: grid; place-items: center; min-height: 100dvh; background: var(--bg); }
.error-card { text-align: center; max-width: 420px; padding: 40px 28px; }
.error-tag { font: 700 11px/1 var(--mono); letter-spacing: .3em; color: var(--accent-ink); margin-bottom: 14px; }
.error-card h1 { font-size: 54px; margin: 0 0 6px; color: var(--navy-600); }
[data-theme="dark"] .error-card h1 { color: var(--navy-300); }
.error-actions { display: flex; gap: 8px; justify-content: center; margin-top: 18px; }

/* ---------- empty states ----------
   One treatment, two densities. The block form owns a whole panel or table
   body that has nothing in it. The inline form is a *section* with nothing in
   it — one line of text, and never a bordered card wrapped around one line of
   grey text, which is a box drawn around an absence. */
.empty { text-align: center; padding: 44px 18px; color: var(--ink-3); }
.empty .ico {
  width: 52px; height: 52px; margin-bottom: 10px; color: var(--navy-400);
  background: var(--bg-accent); border-radius: var(--r-md); padding: 13px; box-sizing: border-box;
}
.empty b { display: block; color: var(--ink-2); font-size: 15px; margin-bottom: 3px; }
.empty-inline { text-align: left; padding: 0 0 14px; font-size: 13px; }
.empty-inline .ico { display: none; }
.empty-inline b { display: inline; font-size: 13px; margin: 0; }
.empty-inline b::after { content: " "; }

/* utility */
.stack { display: flex; flex-direction: column; gap: 12px; }
.cluster { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }
.sep { height: 1px; background: var(--line-soft); border: 0; margin: 14px 0; }
.mt { margin-top: 14px; } .mb { margin-bottom: 14px; }
.right { margin-left: auto; }
.only-mobile { display: none; }
@media (max-width: 900px) { .hide-mobile { display: none !important; } .only-mobile { display: initial; } }

/* reduced motion kill switch */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration: .01ms !important; transition-duration: .01ms !important; }
}

/* ---------- print (labels, vouchers, invoices, forms) ---------- */
@media print {
  .topbar, .rail, .bottom-nav, .no-print, .toasts { display: none !important; }
  body { background: #fff; color: #000; font-size: 12px; }
  main.content { padding: 0; }
  .tagcard, .tablewrap { box-shadow: none; border-color: #999; }
  a { color: #000; text-decoration: none; }
  .print-page { page-break-after: always; }
  /* A scrollable box prints at whatever offset it happens to be scrolled to, so
     a wide table somebody had nudged sideways on screen came out of the printer
     with its first columns and its letterhead sliced off the left of the sheet.
     Nothing scrolls on paper: every scroller opens up for the print run. */
  .doc, .doc .lines-wrap, .tablewrap, .print-report, .an-scroll { overflow: visible !important; }
}

/* label sheet (Avery-style grid) */
.label-sheet { display: grid; grid-template-columns: repeat(auto-fill, minmax(230px, 1fr)); gap: 10px; }
.label {
  position: relative; border: 1.5px solid #223; border-radius: 8px; padding: 10px 12px 10px 18px;
  background: #fff; color: #000; display: grid; grid-template-columns: 1fr auto; gap: 4px 10px; align-items: center;
  break-inside: avoid; min-height: 106px;
}
.label::before { content: ""; position: absolute; inset: 0 auto 0 0; width: 9px; border-radius: 6px 0 0 6px; background: var(--label-band, #E4E7EE); }
.label .l-pn { font: 700 15px var(--mono); grid-column: 1 / -1; letter-spacing: .01em; }
.label .l-desc { font-size: 11px; line-height: 1.3; color: #222; }
.label .l-meta { font: 600 11px var(--mono); color: #444; grid-column: 1; }
.label .l-qr { grid-row: 2 / span 2; grid-column: 2; }
.label .l-qr canvas, .label .l-qr img, .label .l-qr svg { width: 74px; height: 74px; display: block; }
.label.band-ok { --label-band: #4C8A44; } .label.band-warn { --label-band: #D9A441; }
.label.band-bad { --label-band: #B83235; } .label.band-plain { --label-band: #E4E7EE; }

/* invoice / voucher documents */
.doc { background: #fff; color: #111; max-width: min(820px, 100%); margin: 0 auto; padding: clamp(18px, 4.4vw, 38px); border-radius: 12px; box-shadow: var(--shadow-1); overflow-x: auto; }
[data-theme="dark"] .doc { box-shadow: none; }
.doc-head { display: flex; justify-content: space-between; gap: 18px; border-bottom: 3px solid var(--navy-800); padding-bottom: 14px; margin-bottom: 18px; flex-wrap: wrap; }
.doc-head .brand-mark { width: 46px; height: 46px; border: 1px solid #DDD; }
.doc-head .brand-mark img { width: 36px; height: 36px; }
.doc-title { text-align: right; }
.doc-title h1 { font-size: 22px; color: var(--navy-800); margin: 0; letter-spacing: .04em; }
.doc-title .ref { font-size: 14px; margin-top: 4px; }
.doc .lines-wrap { overflow-x: auto; }
.doc table.lines { width: 100%; border-collapse: collapse; margin: 14px 0; font-size: 13px; }
.doc table.lines th { background: var(--navy-800); color: #fff; padding: 7px 10px; text-align: left; font-size: 11px; letter-spacing: .06em; }
.doc table.lines td { padding: 7px 10px; border-bottom: 1px solid #E3E6EE; }
.doc .totals { margin-left: auto; width: 260px; font-size: 13px; }
.doc .totals div { display: flex; justify-content: space-between; padding: 4px 0; }
.doc .totals .grand { border-top: 2px solid var(--navy-800); font-weight: 800; font-size: 16px; margin-top: 4px; padding-top: 8px; }
.doc .foot { margin-top: 26px; font-size: 11px; color: #666; border-top: 1px solid #E3E6EE; padding-top: 10px; display: flex; justify-content: space-between; gap: 10px; }
.sig-row { display: grid; grid-template-columns: 1fr 1fr; gap: 30px; margin-top: 38px; }
.sig-row .sig { border-top: 1.5px solid #333; padding-top: 6px; font-size: 12px; color: #333; }
@media print { .doc { padding: 34px 38px; border-radius: 0; } }

/* ---------- analytics ---------- */
.section-h { font: var(--label-font); letter-spacing: var(--label-track); text-transform: uppercase; color: var(--ink-3); margin: 6px 0 10px; }
.metric-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); gap: 12px; }
/* A KPI card earns its space by being readable across a Stores counter: the
   figure leads at desk-distance size, the label sits under it, and the note
   says what the number actually counts. Tone is carried by the figure's own
   colour (see .metric-v rules above). No accent rail: a coloured stripe down
   the edge of a card is decoration that repeats information the number already
   states, and the client reads it as filler. */
.metric { display: block; padding: 18px 18px 16px; background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--r-lg); text-decoration: none; color: var(--ink); position: relative; overflow: hidden;
  transition: border-color var(--speed) var(--ease), transform var(--speed) var(--ease), box-shadow var(--speed) var(--ease); }
a.metric:hover { border-color: var(--info-line); text-decoration: none; transform: translateY(-1px); box-shadow: var(--shadow-1); }
a.metric::after { content: ""; position: absolute; right: 14px; top: 18px; width: 6px; height: 6px;
  border-right: 1.5px solid var(--ink-3); border-top: 1.5px solid var(--ink-3); transform: rotate(45deg); opacity: .45; }
a.metric:hover::after { opacity: 1; border-color: var(--info); }
/* tone lives with .stat above — one rule for both figure components */
.metric-v { font: 800 32px/1.05 var(--mono); font-variant-numeric: tabular-nums; letter-spacing: -.022em; }
.metric-l { font-size: 12px; color: var(--ink-2); font-weight: 700; margin-top: 6px; }
.metric-l.dim { color: var(--ink-3); }

/* An insight strip: one plain sentence under a panel saying what it means.
   A chart nobody can read a conclusion out of is decoration. */
.insight { display: flex; align-items: center; gap: 10px; margin-top: 10px; padding: 10px 12px;
  background: var(--bg-accent); border: 1px solid var(--line-soft); border-radius: var(--r-sm);
  font-size: 13px; color: var(--ink-2); }
.insight b { color: var(--ink); }
.insight .right { margin-left: auto; font: 700 13px/1 var(--mono); white-space: nowrap; }
.insight.i-bad { background: var(--bad-bg); border-color: var(--bad-line); }
.insight.i-warn { background: var(--warn-bg); border-color: var(--warn-line); }
.insight.i-ok { background: var(--ok-bg); border-color: var(--ok-line); }

/* Pipeline stages read as a row of banded counts, not another table. */
/* Five pipeline stages should sit on one line where there is room — a 4+1 wrap
   reads as five unrelated tiles again, which is the thing this replaced. */
.bandrow { display: grid; grid-template-columns: repeat(auto-fit, minmax(118px, 1fr)); gap: 8px; }
@media (max-width: 560px) { .bandrow { grid-template-columns: 1fr 1fr; } }
.band { display: block; padding: 13px 14px 11px; background: var(--surface); border: 1px solid var(--line);
  border-bottom: 3px solid var(--muted-line); border-radius: var(--r-sm); text-decoration: none; color: var(--ink); }
a.band:hover { text-decoration: none; border-color: var(--info-line); border-bottom-color: var(--info); }
.band-v { font: 800 24px/1.1 var(--mono); font-variant-numeric: tabular-nums; }
.band-l { font: var(--label-font); letter-spacing: var(--label-track); text-transform: uppercase; color: var(--ink-3); margin-top: 4px; }
.band.b-bad  { border-bottom-color: var(--bad); }
.band.b-warn { border-bottom-color: var(--warn); }
.band.b-ok   { border-bottom-color: var(--ok); }
.band.b-info { border-bottom-color: var(--info); }
@media (max-width: 900px) { .metric-grid { grid-template-columns: 1fr 1fr; gap: 10px; }
  .metric { padding: 14px; } .metric-v { font-size: 26px; } }
@media (prefers-reduced-motion: reduce) { .metric, a.metric:hover { transition: none; transform: none; } }
.an-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 12px; align-items: start; }
/* two-column detail layout (main + side); stacks on phones */
.an-grid.split-2 { grid-template-columns: 1.4fr 1fr; }
@media (max-width: 720px) { .an-grid.split-2 { grid-template-columns: 1fr; } }
.an-wide { grid-column: span 2; }
@media (max-width: 1100px) { .an-grid { grid-template-columns: 1fr 1fr; } .an-wide { grid-column: span 2; } }
@media (max-width: 720px) { .an-grid { grid-template-columns: 1fr; } .an-wide { grid-column: span 1; } }
.an-head { display: flex; align-items: baseline; justify-content: space-between; gap: 8px; margin-bottom: 10px; }
.an-head h3 { margin: 0; }
.chart-wrap { width: 100%; }
svg.chart { width: 100%; height: auto; display: block; }
.donut-row { display: flex; align-items: center; gap: 16px; flex-wrap: wrap; }
svg.donut { flex: none; }
.legend { list-style: none; margin: 0; padding: 0; flex: 1; min-width: 150px; font-size: 13px; }
.legend li { display: flex; align-items: center; gap: 8px; padding: 3px 0; border-bottom: 1px solid var(--line-soft); }
.legend .dot { width: 10px; height: 10px; border-radius: 3px; flex: none; }
.legend .right, .ranklist .right, .statlist .num { margin-left: auto; }
.ranklist { list-style: none; margin: 0; padding: 0; }
.ranklist li { padding: 7px 0; border-bottom: 1px solid var(--line-soft); }
.ranklist li:last-child { border-bottom: 0; }
.rank-top { display: flex; align-items: baseline; gap: 8px; }
.statlist { list-style: none; margin: 0 0 8px; padding: 0; }
.statlist li { display: flex; align-items: center; justify-content: space-between; gap: 12px;
  padding: 8px 0; border-bottom: 1px solid var(--line-soft); font-size: 14px; }
/* A value without .num used to sit flush against its label; space-between plus a
   gap fixes every statlist rather than the one screen it was noticed on. */
.statlist li > :last-child { text-align: right; }
.statlist li:last-child { border-bottom: 0; }
.statlist li.bad b { color: var(--bad); } .statlist li.warn b { color: var(--warn); }

/* ---------- Scan, Collect & Issue ---------- */
.warn { color: var(--warn); }
.field > span { font-size: 13px; font-weight: 600; color: var(--ink-2); }
.resume-card { display: block; text-decoration: none; color: inherit; }
.resume-card:hover { border-color: var(--accent); }

.ctx-bar {
  position: sticky; top: 0; z-index: 30; width: 100%;
  display: flex; flex-direction: column; gap: 2px; align-items: flex-start;
  background: var(--navy-800); color: #fff; border: 0; border-radius: var(--r-md);
  padding: 12px 16px; margin-bottom: 12px; cursor: pointer; text-align: left;
  font: inherit; box-shadow: var(--shadow-2); min-height: 56px;
}
[data-theme="dark"] .ctx-bar { background: var(--navy-700); }
.ctx-bar:disabled { cursor: default; opacity: .92; }
.ctx-bar .ctx-who { font-weight: 700; font-size: 15px; letter-spacing: .4px; }
.ctx-bar .ctx-what { font-size: 12px; letter-spacing: .8px; color: var(--navy-300); }

.ctx-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(160px, 1fr)); gap: 8px; }
.ctx-opt { display: block; }
.ctx-opt input { position: absolute; opacity: 0; }
.ctx-opt span {
  display: flex; align-items: center; justify-content: center; text-align: center;
  min-height: var(--touch); padding: 10px 12px; border: 1px solid var(--line-strong);
  border-radius: var(--r-sm); background: var(--surface-2);
  font-size: 13px; font-weight: 600; cursor: pointer; transition: border-color var(--speed), background var(--speed);
}
.ctx-opt input:checked + span { border-color: var(--accent); background: var(--surface); box-shadow: inset 0 0 0 1px var(--accent); }
.ctx-opt input:focus-visible + span { outline: 2px solid var(--focus); outline-offset: 1px; }
.ctx-link { text-decoration: none; color: var(--ink-3); }

.staff-picked { display: flex; align-items: center; gap: 12px; padding: 12px 14px; min-height: 56px;
  border: 1px solid var(--ok-line); background: var(--ok-bg); border-radius: var(--r-sm); margin-bottom: 12px; }
.staff-face { width: 40px; height: 40px; border-radius: 50%; background: var(--navy-800); color: #fff;
  display: flex; align-items: center; justify-content: center; font-weight: 700; flex: none; }
[data-theme="dark"] .staff-face { background: var(--navy-600); }
.staff-results { display: flex; flex-direction: column; gap: 4px; margin-top: 6px; }
.staff-row { display: flex; align-items: center; gap: 10px; padding: 10px 12px; min-height: 52px; text-align: left;
  border: 1px solid var(--line); border-radius: var(--r-sm); background: var(--surface);
  font: inherit; cursor: pointer; width: 100%; }
.staff-row:hover, .staff-row:focus-visible { border-color: var(--accent); }

.scan-wrap { max-width: 560px; }
.scan-stage { position: relative; border-radius: var(--r-lg); overflow: hidden; background: #0B0C18; }
.scan-stage video { display: block; width: 100%; aspect-ratio: 4 / 3; object-fit: cover; }
.scan-target { position: absolute; inset: 0; margin: auto; width: min(58%, 240px); aspect-ratio: 1;
  border: 2px solid rgba(255, 255, 255, .85); border-radius: var(--r-md);
  box-shadow: 0 0 0 2000px rgba(0, 0, 0, .28); pointer-events: none; }
.scan-torch { position: absolute; right: 12px; bottom: 12px; min-width: var(--touch); min-height: var(--touch);
  background: rgba(0,0,0,.55); color: #fff; border-color: rgba(255,255,255,.4); }
/* camera-off / permission-denied stage state */
.scan-stage .scan-off {
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 8px;
  aspect-ratio: 4 / 3; color: #C7CCE5; text-align: center; padding: 20px;
}
.scan-stage .scan-off .ico { width: 40px; height: 40px; color: #5A5F84; }
.scan-stage .scan-off b { color: #fff; }

.basket { display: flex; flex-direction: column; gap: 8px; max-width: 720px; }
.b-line { display: flex; align-items: center; gap: 10px; padding: 12px 14px;
  border: 1px solid var(--line); border-radius: var(--r-md); background: var(--surface); }
.b-line .b-main { flex: 1; min-width: 0; }
.b-qty { font-weight: 700; }
.b-qtyform, .sr-qty { display: flex; align-items: center; gap: 6px; }
.qty-btn { width: var(--touch); height: var(--touch); border-radius: var(--r-sm); border: 1px solid var(--line-strong);
  background: var(--surface-2); font-size: 20px; font-weight: 700; cursor: pointer; color: var(--ink); flex: none; }
.qty-btn:hover { border-color: var(--accent); }
.qty-in { width: 70px; min-height: var(--touch); text-align: center; font-weight: 700; font-size: 16px;
  border: 1px solid var(--line-strong); border-radius: var(--r-sm); background: var(--surface); color: var(--ink); }
.sr-qty { justify-content: center; margin-top: 12px; }
.sr-qty .qty-btn { width: 56px; height: 56px; }
.sr-qty .qty-in { width: 88px; min-height: 56px; font-size: 20px; }

.sr-lines { display: flex; flex-direction: column; gap: 6px; margin: 8px 0; }
.sr-line { display: flex; gap: 10px; align-items: flex-start; padding: 11px 13px; min-height: var(--touch);
  border: 1px solid var(--line); border-radius: var(--r-sm); cursor: pointer; font-size: 13px; }
.sr-line:has(input:checked) { border-color: var(--accent); box-shadow: inset 0 0 0 1px var(--accent); }
.sr-line input { margin-top: 3px; accent-color: var(--accent); }

.ack-panel { border-color: var(--info-line); }
.ack-scan-overlay { position: fixed; inset: 0; z-index: 300; background: rgba(0,0,0,.9);
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 14px; padding: 20px; }
.ack-scan-overlay video { width: min(92vw, 480px); aspect-ratio: 1; object-fit: cover; border-radius: var(--r-md); }
.ack-scan-overlay p { color: #fff; margin: 0; }

@media (prefers-reduced-motion: reduce) {
  .ctx-opt span, .qty-btn, .staff-row, .resume-card { transition: none; }
}
@media print {
  .ctx-bar, .scan-wrap, .basket { display: none !important; }
}

/* ---------- unified mobile command header (AMS-family hierarchy) ---------- */
.mhead {
  display: none;
  position: sticky; top: 0; z-index: 40;
  grid-template-areas: "menu role act" "menu id id";
  grid-template-columns: auto minmax(0, 1fr) auto; align-items: start; gap: 2px 12px;
  padding: calc(10px + env(safe-area-inset-top)) var(--pad) 12px;
  background: color-mix(in srgb, var(--bg-accent) 72%, var(--bg));
  border-bottom: 1px solid var(--line);
  -webkit-backdrop-filter: saturate(1.4) blur(10px); backdrop-filter: saturate(1.4) blur(10px);
  transition: box-shadow 180ms ease, background 180ms ease;
}
.mhead.condensed { box-shadow: var(--shadow-2); background: color-mix(in srgb, var(--bg-accent) 88%, var(--surface)); }
.mh-btn {
  position: relative; display: grid; place-items: center; flex: none;
  width: var(--touch); height: var(--touch); border-radius: 14px;
  background: var(--surface); border: 1px solid var(--line); box-shadow: var(--shadow-1);
  color: var(--ink-2); cursor: pointer; text-decoration: none;
}
.mh-btn:hover { color: var(--ink); background: var(--surface-2); text-decoration: none; }
.mh-btn .dotbadge {
  position: absolute; top: -5px; right: -5px; min-width: 18px; height: 18px; padding: 0 5px;
  border-radius: var(--r-pill); background: var(--bad); color: #fff; font: 700 11px/18px var(--font);
  text-align: center; border: 2px solid var(--surface);
}
.mhead > .mh-btn { grid-area: menu; }
.mh-id { grid-area: id; min-width: 0; margin-top: 2px; }
.mh-actions { grid-area: act; }
.mh-role {
  grid-area: role; align-self: center; min-width: 0;
  display: block; font: var(--label-font); letter-spacing: var(--label-track); text-transform: uppercase; color: var(--ink-3);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.mh-title {
  margin: 0; font-size: clamp(20px, 5.6vw, 24px); font-weight: 800; letter-spacing: -.02em; line-height: 1.15;
  color: var(--ink); overflow-wrap: anywhere; transition: font-size 180ms ease;
}
.mhead.condensed .mh-title { font-size: 18px; }
.mh-date { display: block; font-size: 12px; color: var(--ink-3); margin-top: 1px; }
.mh-actions { display: flex; gap: 8px; }
.mh-avatar {
  display: grid; place-items: center; flex: none; width: var(--touch); height: var(--touch);
  border-radius: 50%; background: var(--navy-800); color: #fff;
  font: 700 15px/1 var(--font); letter-spacing: .02em; text-decoration: none;
  box-shadow: 0 0 0 2px var(--surface), 0 0 0 3.5px var(--line-strong);
}
.mh-avatar:hover { text-decoration: none; filter: brightness(1.08); }
[data-theme="dark"] .mh-avatar { background: var(--navy-700); }
@media (max-width: 900px) {
  .mhead { display: grid; }
  /* header owns role/title/date on mobile — page-level h1 and home hero stand down */
  .page-head h1, .home-hero { display: none; }
}
@media (prefers-reduced-motion: reduce) {
  .mhead, .mh-title { transition: none; }
}

[hidden] { display: none !important; }

/* Import batch: why a line was refused, shown beside the part it belongs to. */
.pv-refused { display: block; margin-top: 2px; font-size: var(--t-xs); color: var(--bad); font-weight: 600; }

/* ============================================================
   Page-level appends. One reserved block per view agent so two
   appends cannot land on the same line. Add rules inside your own
   marker only, and only when the shared component layer above
   genuinely cannot express what the page needs.
   ============================================================ */

/* === page: home + collect === */

/* Home's page title is now the date. The brand lockup already names the product
   and the greeting carried nothing, so the hero sizing belonged to a name this
   page no longer repeats — the date is a page title like every other page's. */
.home-hero h1 { font-size: clamp(22px, 3vw, var(--t-2xl)); font-weight: 700; letter-spacing: -.02em; }

/* The one attention row with no count (fleet hours) still ends on the shared
   right edge, so the chevron column does not jump left for a single row. */
.queue-link:not(:has(.n))::after { margin-left: auto; }

/* Purpose grid: the 160px minimum lands two pixels short of two columns inside
   the card at 390px, so nine choices became nine full-width rows and pushed
   "Start scanning" off a phone screen. Two columns, still well over --touch. */
@media (max-width: 480px) { .ctx-grid { grid-template-columns: repeat(2, 1fr); } }


/* === page: tables === */
/* A part number is a stamped reference. Auto table layout takes the hyphen inside
   it as the first break opportunity, so once the duplicate pending badge stopped
   propping the column open, 01-0770006-09 rendered as two lines and read as two
   references. Hold the identity column at the width of a typical one; the long
   descriptive part names still wrap, but at their spaces. */
/* Scoped to the table, which is where that reasoning applies. Below 900px the row
   is a card and this column is a grid track: an unconditional 116px floor there
   held the track open, and the row overflowed its own card by 25px, which showed
   as the quantity hanging past the card edge. In card mode td.c-pn's own overflow
   and text-overflow ellipsise it instead. */
@media (min-width: 901px) { table.data.items td.c-pn { min-width: 116px; } }

@media (max-width: 900px) {
  /* "Pending verification" is a wider chip than the "Unverified" it replaced, and
     on a 390px card that was enough for the two row actions to take a wrap and
     stack — 94px of button where 44px was, over fifty rows. They do fit beside
     the chip; stop the flex container from taking the easy way out. */
  .cards-mobile table.data.items td.c-act .row-actions { flex-wrap: nowrap; }
}

/* /tools — the crib laid every tool out as a five-slot stack with the actions on a
   full-width row of their own: 206px of card for four short facts, 79 tools, and a
   phone page three times the height of the same page on a laptop.
   Two 44px controls and a two-line identity do not share a line at 390px, so the
   identity block spans instead: the part number and its name run down the left
   against the serial and the calibration chip stacked on the right, and the section
   heading carries the location a tool in the crib no longer has to repeat. */
@media (max-width: 900px) {
  .cards-mobile table.data.tools tbody tr {
    grid-template-columns: minmax(0, 1fr) minmax(0, auto);
    grid-template-areas: "pn desc" "pn status" "act act";
    padding: 11px 16px; gap: 4px 10px;
  }
  .cards-mobile table.data.tools.tools-loc tbody tr {
    grid-template-areas: "pn desc" "pn status" "meta meta" "act act";
  }
  .cards-mobile table.data.tools td.c-desc,
  .cards-mobile table.data.tools td.c-status { justify-self: end; text-align: right; }
  .cards-mobile table.data.tools td.c-desc { align-self: start; }
  /* "No record — cannot be issued" is the widest string in the column and the two
     tools that carry it are the two nobody may miss: let the reason wrap rather
     than crush the part number it belongs to. */
  .cards-mobile table.data.tools td.c-status .chip { white-space: normal; }
  .cards-mobile table.data.tools td.c-act { justify-self: stretch; }
  .cards-mobile table.data.tools td.c-act .row-actions { flex-wrap: nowrap; justify-content: flex-start; }
  .cards-mobile table.data.tools td.c-act .btn-sm { flex: 1 1 auto; }
}

/* /shelf-life — the register is a diary and it was read sideways: seven columns at a
   640px minimum inside a 390px viewport put the expiry chip, the quantity and the
   status off the right edge, so the one fact worth scrolling for was the one you had
   to scroll for. Batch and cure date pair on one line, expiry and condition on the
   next, and nothing needs a horizontal swipe. */
@media (max-width: 900px) {
  .cards-mobile table.data.shelf-reg tbody tr {
    grid-template-areas: "pn avail" "desc desc" "meta cure" "status state";
    padding: 11px 16px; gap: 4px 10px;
  }
  .cards-mobile table.data.shelf-reg td.c-cure { grid-area: cure; text-align: right; font-size: 13px; }
  .cards-mobile table.data.shelf-reg td.c-state { grid-area: state; justify-self: end; }
  /* the card has no header row, so the batch and the cure date name themselves —
     and a batch that has neither drops the line rather than printing two dashes */
  .cards-mobile table.data.shelf-reg td.c-meta::before { content: "Batch "; color: var(--ink-3); font-family: var(--font); }
  .cards-mobile table.data.shelf-reg td.c-cure::before { content: "Cure/Mfg "; color: var(--ink-3); font-family: var(--font); }
  .cards-mobile table.data.shelf-reg td.c-meta:empty,
  .cards-mobile table.data.shelf-reg td.c-cure:empty { display: none; }
}
/* An expired batch is the one thing on this page that has to be pulled off a shelf
   today, so its group carries the tag colour the rows inside it carry. */
.shelf.s-bad { border-color: var(--bad-line); }
.shelf.s-bad > summary { color: var(--bad); }
.shelf.s-bad > summary::before { border-right-color: var(--bad); border-bottom-color: var(--bad); }

/* === page: detail + queues === */


/* /receiving — the live queue and the archive are two short lists. Stacked they
   put H1 + explainer + two H2s over two rows of data and left 900px of nothing
   under them; side by side the headings sit on one line. */
@media (min-width: 1100px) {
  .recv-cols { display: grid; grid-template-columns: minmax(0, 1.15fr) minmax(0, 1fr); gap: 4px 28px; align-items: start; }
  .recv-cols > section { min-width: 0; }
  .recv-cols table.data { min-width: 0; }
}

/* /imports — 1,946 lines pending and 306 exceptions on one page rendered
   37,700px on a laptop and 60,755px on a phone. Same defect the stock-count
   page was fixed for, same answer: a clerk walks one shelf, so the queue is an
   index of shelves and one opens at a time. Nothing is removed — every line,
   checkbox and decision button is still in the page, one disclosure away, and
   the checkboxes still post with #pv-batch from inside a closed shelf. */
.shelves { display: flex; flex-direction: column; gap: 6px; }
.shelf {
  border: 1px solid var(--line); border-radius: var(--r-md);
  background: var(--surface); box-shadow: var(--shadow-1);
}
.shelf > summary, .qsection > summary {
  display: flex; align-items: center; gap: 12px; cursor: pointer;
  min-height: var(--touch); padding: 10px 16px; box-sizing: border-box;
  font-size: 14px; font-weight: 600; color: var(--ink);
}
/* the flex summary drops the native marker in Chromium; suppress it everywhere
   and draw the chevron from borders, the way .queue-link already does */
.shelf > summary::-webkit-details-marker,
.qsection > summary::-webkit-details-marker { display: none; }
.shelf > summary::marker, .qsection > summary::marker { content: ""; }
.shelf > summary::before, .qsection > summary::before {
  content: ""; flex: none; width: 7px; height: 7px; margin-right: 2px;
  border-right: 1.5px solid var(--ink-3); border-bottom: 1.5px solid var(--ink-3);
  transform: rotate(-45deg); transition: transform var(--speed) var(--ease);
}
.shelf[open] > summary::before, .qsection[open] > summary::before { transform: rotate(45deg); }
.shelf > summary:hover, .qsection > summary:hover { color: var(--info); }
.shelf > summary:hover::before, .qsection > summary:hover::before {
  border-right-color: var(--info); border-bottom-color: var(--info);
}
.sh-name { overflow-wrap: anywhere; }
.sh-n {
  margin-left: auto; flex: none;
  font: var(--label-font); letter-spacing: var(--label-track);
  text-transform: uppercase; color: var(--ink-2);
}
/* the shelf is the card; the table inside it is not a second one */
.sh-body {
  overflow-x: auto; border-top: 1px solid var(--line-soft);
  border-radius: 0 0 var(--r-md) var(--r-md);
}
.sh-body table.data th { background: none; border-bottom: 1px solid var(--line-soft); }

/* the exceptions queue is a whole section, so its summary carries the heading */
.qsection { margin-bottom: 14px; }
.qsection > summary { padding: 10px 0; }
.qsection > summary h2 { display: inline; margin: 0; }

@media (prefers-reduced-motion: reduce) {
  .shelf > summary::before, .qsection > summary::before { transition: none; }
}

/* ============================================================
   DASHBOARD SURFACE — figures, deltas, attention cards, motion
   ------------------------------------------------------------
   Two numeric voices, deliberately: mono is for IDENTIFIERS
   (part numbers, batch refs, timestamps — things you read
   character by character and might key back in), and the sans
   tabular below is for MAGNITUDES (counts, totals, percentages
   — things you compare at a glance down a column). Mixing them
   by accident looks careless; splitting them by job is what
   makes a figure read as a figure.

   Motion here needs no per-rule reduced-motion guard: the kill
   switch above zeroes every duration under `*` with !important.
   Anything JS-driven must check the media query itself.
   ============================================================ */
:root {
  --speed-slow: 280ms;
  /* magnitudes: tight, heavy, tabular so columns of them line up */
  --fig: 800 30px/1.05 var(--font);
  --fig-sm: 800 22px/1.1 var(--font);
}
.fig, .kpi-v, .att-v {
  font: var(--fig); letter-spacing: -.022em;
  font-variant-numeric: tabular-nums; color: var(--ink);
}

/* ---------- icon / initial tile ----------
   The square behind an icon is what stops a card header reading as
   a paragraph. Tone tints follow the stores tag colours, so a red
   tile means rejected here exactly as it does on a physical tag. */
.tile {
  display: grid; place-items: center; width: 36px; height: 36px; flex: none;
  border-radius: 10px; background: var(--muted-bg); color: var(--muted);
  box-shadow: inset 0 0 0 1px var(--muted-line);
  font: 700 14px/1 var(--font); letter-spacing: .01em; text-transform: uppercase;
}
.tile .ico { width: 19px; height: 19px; }
.tile.t-ok   { background: var(--ok-bg);   color: var(--ok);   box-shadow: inset 0 0 0 1px var(--ok-line); }
.tile.t-warn { background: var(--warn-bg); color: var(--warn); box-shadow: inset 0 0 0 1px var(--warn-line); }
.tile.t-bad  { background: var(--bad-bg);  color: var(--bad);  box-shadow: inset 0 0 0 1px var(--bad-line); }
.tile.t-info { background: var(--info-bg); color: var(--info); box-shadow: inset 0 0 0 1px var(--info-line); }
.tile.t-accent {
  background: color-mix(in srgb, var(--accent) 14%, var(--surface)); color: var(--accent-ink);
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--accent) 40%, transparent);
}
.tile.sm { width: 28px; height: 28px; border-radius: 8px; font-size: 12px; }
.tile.sm .ico { width: 15px; height: 15px; }

/* ---------- delta chip ----------
   Only render one where a real prior period exists. A delta with
   nothing behind it is a lie told in green. */
.delta {
  display: inline-flex; align-items: center; gap: 4px;
  font: 700 12px/1 var(--font); font-variant-numeric: tabular-nums;
  color: var(--muted); white-space: nowrap;
}
.delta::before {
  content: ""; width: 0; height: 0; flex: none;
  border-left: 4px solid transparent; border-right: 4px solid transparent;
}
.delta.up   { color: var(--ok); }
.delta.up::before   { border-bottom: 5px solid currentColor; }
.delta.down { color: var(--bad); }
.delta.down::before { border-top: 5px solid currentColor; }
.delta.flat::before {
  border: 0; width: 8px; height: 2px; border-radius: 1px; background: currentColor; opacity: .6;
}
.delta-vs { font-size: 12px; color: var(--ink-3); font-variant-numeric: tabular-nums; }

/* ---------- KPI strip ----------
   One bordered container divided by hairlines, not four floating
   cards. The container says "these four belong to one question";
   detached cards say "here are four unrelated things". */
.kpi-strip {
  display: grid; grid-template-columns: repeat(4, 1fr);
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--r-md); box-shadow: var(--shadow-1); overflow: hidden;
}
.kpi { padding: 0; min-width: 0; border-left: 1px solid var(--line); }
.kpi:first-child { border-left: 0; }
.kpi-head {
  display: flex; align-items: center; gap: 8px; padding: 12px 16px;
  border-bottom: 1px solid var(--line-soft); background: color-mix(in srgb, var(--bg-accent) 34%, var(--surface));
}
.kpi-head .ico { width: 16px; height: 16px; color: var(--ink-3); }
.kpi-l {
  font-size: 13px; font-weight: 600; color: var(--ink-2); min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.kpi-body { padding: 14px 16px 15px; }
.kpi-foot { display: flex; align-items: center; gap: 8px; margin-top: 7px; min-height: 16px; }
.kpi-foot .delta-vs { margin-left: auto; }
a.kpi { text-decoration: none; color: inherit; display: block; transition: background var(--speed) var(--ease); }
a.kpi:hover { background: var(--surface-2); text-decoration: none; }
a.kpi:hover .kpi-v { color: var(--navy-700); }
[data-theme="dark"] a.kpi:hover .kpi-v { color: var(--navy-300); }
@media (max-width: 900px) {
  .kpi-strip { grid-template-columns: 1fr 1fr; }
  .kpi:nth-child(3), .kpi:nth-child(4) { border-top: 1px solid var(--line); }
  .kpi:nth-child(odd) { border-left: 0; }
  .kpi-v { font: var(--fig-sm); letter-spacing: -.018em; }
}
@media (max-width: 400px) {
  .kpi-strip { grid-template-columns: 1fr; }
  .kpi { border-left: 0; border-top: 1px solid var(--line); }
  .kpi:first-child { border-top: 0; }
}

/* ---------- attention cards ----------
   The 3-up "what is waiting on you" row. Same information the flat
   queue links carried, given the weight it actually has: a tile you
   can identify by colour across the room, a figure at arm's length,
   and the action sitting inside the card that owns it. */
.att-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 12px; }
.att-card {
  display: flex; flex-direction: column; background: var(--surface);
  border: 1px solid var(--line); border-radius: var(--r-md); box-shadow: var(--shadow-1);
  overflow: hidden; min-width: 0;
  transition: transform var(--speed) var(--ease), box-shadow var(--speed) var(--ease), border-color var(--speed) var(--ease);
}
.att-card:hover { transform: translateY(-1px); box-shadow: var(--shadow-2); border-color: var(--line-strong); }
.att-head {
  display: flex; align-items: center; gap: 10px; padding: 12px 14px;
  border-bottom: 1px solid var(--line-soft);
}
.att-head b { font-size: 14px; font-weight: 600; color: var(--ink); min-width: 0; overflow-wrap: anywhere; }
.att-body { display: flex; align-items: center; gap: 12px; padding: 13px 14px 14px; }
.att-fig { display: flex; align-items: baseline; gap: 7px; min-width: 0; }
.att-u { font-size: 13px; font-weight: 500; color: var(--ink-3); }
.att-body .btn, .att-body .btn-sm { margin-left: auto; flex: none; }
.att-sub {
  display: flex; align-items: center; gap: 6px; padding: 0 14px 13px;
  font-size: 12px; color: var(--ink-3); margin-top: -6px;
}
.att-sub .ico { width: 14px; height: 14px; flex: none; }
.att-card.t-bad  .att-sub { color: var(--bad); }
.att-card.t-bad  { border-color: var(--bad-line); }
.att-card.t-warn { border-color: var(--warn-line); }

/* ---------- meter ----------
   Segment ticks over the fill: a bar reads as a measurement rather
   than a decoration once you can count what it is made of. Fill
   animates from 0 on first paint via the width transition. */
.meter {
  position: relative; height: 7px; border-radius: var(--r-pill);
  background: var(--muted-bg); overflow: hidden;
}
.meter > i {
  display: block; height: 100%; width: var(--w, 0%); border-radius: inherit;
  background: var(--navy-600);
  transition: width var(--speed-slow) var(--ease);
}
.meter.ticked > i {
  background-image: repeating-linear-gradient(90deg,
    transparent 0 5px, color-mix(in srgb, var(--surface) 55%, transparent) 5px 6px);
}
.meter.t-ok   > i { background-color: var(--ok); }
.meter.t-warn > i { background-color: var(--warn); }
.meter.t-bad  > i { background-color: var(--bad); }
.meter.t-accent > i { background-color: var(--accent); }

/* ---------- progress ring (setup / walkthrough completion) ---------- */
.ring {
  --p: 0; --ring-size: 38px;
  position: relative; width: var(--ring-size); height: var(--ring-size); flex: none;
  border-radius: 50%; display: grid; place-items: center;
  background: conic-gradient(var(--accent) calc(var(--p) * 1%), var(--muted-bg) 0);
  transition: background var(--speed-slow) var(--ease);
}
.ring::after {
  content: ""; position: absolute; inset: 4px; border-radius: 50%; background: var(--surface);
}
.ring > span {
  position: relative; z-index: 1;
  font: 700 11px/1 var(--font); font-variant-numeric: tabular-nums; color: var(--ink-2);
}

/* ---------- hover affordances ----------
   Small, cheap, everywhere. The arrow that moves is the difference
   between a link and a door. */
.panel-head .more .ico, .att-head .more .ico { transition: transform var(--speed) var(--ease); }
.panel-head .more:hover .ico, .att-head .more:hover .ico { transform: translateX(3px); }
.lift { transition: transform var(--speed) var(--ease), box-shadow var(--speed) var(--ease); }
.lift:hover { transform: translateY(-1px); box-shadow: var(--shadow-2); }
.btn:active, .action-tile:active, .att-card .btn:active { transform: translateY(1px); }

/* row hover: an accent edge wipes in from the left rather than the
   whole row changing colour — cheaper to read, easier to track down
   a long ledger. */
.panel-rows > .led-row, .panel-rows > .fleet-row { position: relative; }
.panel-rows > .led-row::before, .panel-rows > .fleet-row::before {
  content: ""; position: absolute; left: 0; top: 0; bottom: 0; width: 2px;
  background: var(--accent); transform: scaleY(0); transform-origin: center;
  transition: transform var(--speed) var(--ease);
}
.panel-rows > .led-row:hover::before, .panel-rows > .fleet-row:hover::before { transform: scaleY(1); }

/* ---------- staggered entrance for card grids ----------
   Reuses the --i idiom the ledger rows already set. Cards arrive in
   reading order, which tells you where the top of the page is. */
@media (prefers-reduced-motion: no-preference) {
  .stagger > * { animation: card-in 300ms var(--ease) both; animation-delay: calc(var(--i, 0) * 40ms); }
  /* perf: no opacity, so the three attention cards on home are readable on the first frame */
  @keyframes card-in { from { transform: translateY(6px); } }
}

/* ---------- skeleton ---------- */
.skel {
  border-radius: 6px; color: transparent !important; pointer-events: none;
  background: linear-gradient(90deg, var(--muted-bg) 0 40%, var(--surface-2) 50%, var(--muted-bg) 60% 100%);
  background-size: 300% 100%; animation: skel 1.3s ease-in-out infinite;
}
@keyframes skel { from { background-position: 150% 0; } to { background-position: -150% 0; } }



/* ---------- page transitions ----------
   Cross-document view transitions: the browser does the work, there is no router,
   no JS, and no single-page rewrite. Browsers without it navigate exactly as
   before, which is the whole reason this is the version worth having.

   The chrome is NAMED, so it is treated as the same element across the
   navigation and stays put while only the content swaps. That is what makes it
   read as one app moving rather than two pages replacing each other. */
@view-transition { navigation: auto; }

/* The chrome is named so it is recognised across the navigation and holds still.
   main.content is deliberately NOT named: view-transition-name makes an element a
   stacking context AND a containing block for fixed-position descendants, at all
   times, not just while a transition runs. Naming main.content therefore trapped
   every .sheet (position:fixed, z-index 95) inside it, below the body-level
   scrim at z-index 90, so a tap on a sheet's own submit button landed on the
   backdrop and closed the sheet instead of submitting the form.
   Naming the chrome already lifts it out of the root snapshot, so what is left in
   root IS the content: animating root gives the content transition without
   putting a containing block around the sheets. */
.topbar      { view-transition-name: app-topbar; }
.rail        { view-transition-name: app-rail; }
.mhead       { view-transition-name: app-mhead; }
.bottom-nav  { view-transition-name: app-bottomnav; }

/* Content moves a few pixels as it fades, which reads as arrival rather than as a
   dissolve. Short enough that a fast navigation never feels held up: the whole
   thing is over before a person could notice waiting for it. */
::view-transition-old(root) {
  animation: vt-out 90ms cubic-bezier(.4, 0, 1, 1) both;
}
::view-transition-new(root) {
  animation: vt-in 220ms cubic-bezier(.16, 1, .3, 1) both;
}
@keyframes vt-out { to { opacity: 0; transform: translateY(-4px); } }
@keyframes vt-in { from { opacity: 0; transform: translateY(8px); } }

/* The chrome never animates: it is the fixed point the content moves against. */
::view-transition-group(app-topbar),
::view-transition-group(app-rail),
::view-transition-group(app-mhead),
::view-transition-group(app-bottomnav) { animation: none; }

@media (prefers-reduced-motion: reduce) {
  @view-transition { navigation: none; }
}

/* ---------- role preview ---------- */
/* Sticky because the way out has to be reachable from anywhere in a long page,
   and unmistakably not chrome: this is a temporary state, not a feature bar. */
.previewbar {
  position: sticky; top: 0; z-index: 30; display: flex; flex-wrap: wrap; align-items: center; gap: 10px;
  margin: 0 0 14px; padding: 9px 12px; border: 1px solid var(--warn-line); border-radius: var(--r-md);
  background: var(--warn-bg); color: var(--ink); font-size: 13.5px;
}
.previewbar .pv-eye { display: inline-flex; flex: none; color: var(--warn); }
.previewbar .pv-eye .ico { width: 17px; height: 17px; }
.previewbar .pv-txt { flex: 1 1 auto; min-width: 0; }
.previewbar .pv-exit { flex: none; }
/* Narrow enough that the role name and the way out cannot share a line without
   the text folding three deep: give the button its own row instead. */
@media (max-width: 560px) {
  .previewbar { gap: 8px 10px; }
  .previewbar .pv-txt { flex: 1 1 100%; }
  .previewbar .pv-exit { flex: 1 1 100%; }
  .previewbar .pv-exit .btn { width: 100%; }
}
.pv-grid { display: grid; gap: 8px; grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); }
.pv-card {
  display: block; width: 100%; text-align: left; cursor: pointer;
  padding: 11px 13px; min-height: 62px; border: 1px solid var(--line); border-radius: var(--r-md);
  background: var(--surface); color: var(--ink); font: inherit;
  transition: border-color var(--speed) var(--ease), transform var(--speed) var(--ease);
}
.pv-card b { display: block; font-size: 14px; }
.pv-card span { display: block; font-size: 12px; color: var(--ink-3); margin-top: 2px; line-height: 1.35; }
.pv-card:hover:not(:disabled) { border-color: var(--focus); transform: translateY(-1px); }
.pv-card:disabled { opacity: .55; cursor: default; }

/* ---------- profile + pending-account approval ---------- */
/* Two columns where there is room, one where there is not. No breakpoint juggling:
   the grid decides from the space it actually has. */
.profile-grid { display: grid; gap: 12px; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); align-items: start; }

/* Tick and cross. The glyph is the button on a phone and the word joins it on a
   desktop, so the same markup serves both without a second template. 46px keeps
   it above the 44px touch target with a hangar glove on. */
.yesno { display: inline-flex; align-items: center; justify-content: center; gap: 7px;
  min-width: 46px; min-height: 46px; line-height: 1; }
.yesno .ico { width: 19px; height: 19px; flex: none; }
.yesno span { display: none; font-size: 14px; }
.yesno.yes { background: var(--ok); border-color: var(--ok); color: #fff; }
.yesno.yes:hover { background: color-mix(in srgb, var(--ok) 86%, #000); }
/* Dark --ok is a light green, so white on it fails contrast the way .btn-danger
   already solved: darken the fill for the dark theme rather than the label. */
[data-theme="dark"] .yesno.yes { background: #2E6B47; border-color: #2E6B47; }
[data-theme="dark"] .yesno.yes:hover { background: #377C53; }
.yesno.no { color: var(--bad); border-color: var(--bad-line); }
.yesno.no:hover { background: var(--bad-bg); }
.approve-row { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }
@media (min-width: 620px) { .yesno span { display: inline; } }
/* On a phone the decision is the row: the two answers take the width and the
   "Choose roles" escape hatch sits under them rather than competing for the tap. */
@media (max-width: 619px) {
  .approve-row { width: 100%; margin-top: 10px; }
  .approve-row form { flex: 1 1 0; }
  .approve-row form .btn { width: 100%; }
  .approve-row > .btn { flex: 1 0 100%; }
}

/* ---------- printable current stock report (app/pages/stock_report.php) ----------
   This is a controlled document that leaves the building: it gets photocopied,
   signed, and filed against an audit. It is designed for A4 landscape, and the
   @page rule that says so lives in a <style> on the page itself, because @page
   cannot be nested or scoped to a selector.

   Every cell is ruled on all four sides. That is not decoration: a ruled grid
   is what makes a 60-sheet stock listing readable after a photocopier has eaten
   the contrast, and it is what gives every sheet a frame instead of leaving the
   middle pages looking like loose text. The heading band repeats on each sheet
   and prints its colour explicitly, so sheet 47 is still self-describing. */
.print-report { background: var(--bg); padding: 18px; }
.print-report .doc { max-width: 100%; overflow: visible; }
.print-report .doc-title h1 { font-size: 15px; }
.print-report .doc-head { border-bottom-width: 2px; padding-bottom: 10px; margin-bottom: 12px; }

.doc table.stock-report {
  table-layout: fixed; width: 100%; border-collapse: collapse;
  font-size: 8.4pt; line-height: 1.15; margin: 8px 0 0;
  border: 1.4pt solid var(--navy-800);
}
.doc table.stock-report th,
.doc table.stock-report td {
  padding: 2.2pt 3.5pt; vertical-align: top; text-align: left;
  border: 0.5pt solid #6C748A;
  word-break: break-word; overflow-wrap: anywhere; hyphens: auto;
}
.doc table.stock-report thead th {
  background: var(--navy-800); color: #fff;
  padding: 3.4pt 3.5pt; font-size: 7.2pt; font-weight: 700;
  letter-spacing: .03em; line-height: 1.15; text-transform: uppercase;
  border-color: var(--navy-800);
  -webkit-print-color-adjust: exact; print-color-adjust: exact;
}
.doc table.stock-report td.r, .doc table.stock-report th.r { text-align: right; }
.doc table.stock-report td.c, .doc table.stock-report th.c { text-align: center; }
.doc table.stock-report td.n { color: #666; font-size: 7.6pt; }
.doc table.stock-report td.mono { font-family: var(--mono); font-size: 8.2pt; letter-spacing: -.01em; }
.doc table.stock-report td.num { font-family: var(--mono); font-variant-numeric: tabular-nums; }
/* A quantity of nothing should not draw the eye the way a real figure does. */
.doc table.stock-report td.zero { color: #9AA1B0; }

/* Banding survives a photocopy better than a hairline does, and it is what stops
   the eye sliding a row on a 12-column sheet. */
.doc table.stock-report tbody tr:nth-child(even) td {
  background: #F2F4F9; -webkit-print-color-adjust: exact; print-color-adjust: exact;
}

/* Heading repeats on every sheet, and the footer line repeats with it, so a page
   that walks off on its own still says what report it came from. */
.doc table.stock-report thead { display: table-header-group; }
.doc table.stock-report tfoot { display: table-footer-group; }
.doc table.stock-report tfoot td {
  border: 0.5pt solid #6C748A; border-top: 1pt solid var(--navy-800);
  background: #fff; padding: 4pt; font-size: 7pt; color: #444;
  -webkit-print-color-adjust: exact; print-color-adjust: exact;
}
.doc table.stock-report tr { break-inside: avoid; page-break-inside: avoid; }

/* Status reads as a word first: a photocopy has no colour, and this report is
   photocopied. Weight is the second cue, colour is only the third. */
.doc table.stock-report td.st { font-weight: 700; font-size: 7.6pt; letter-spacing: .02em; white-space: nowrap; }
.doc table.stock-report td.st-outofstock { color: #8E2326; }
.doc table.stock-report td.st-low        { color: #8A6410; }
.doc table.stock-report td.st-instock    { font-weight: 500; color: #2F5B29; }
.doc table.stock-report td.ph-needed { font-weight: 700; font-size: 7pt; color: #8A6410; white-space: nowrap; }

.doc table.stock-report col.c-n     { width: 3.0%; }
.doc table.stock-report col.c-pn    { width: 12.5%; }
.doc table.stock-report col.c-desc  { width: 21.0%; }
.doc table.stock-report col.c-mfr   { width: 10.5%; }
.doc table.stock-report col.c-uom   { width: 4.0%; }
.doc table.stock-report col.c-qty   { width: 7.0%; }
.doc table.stock-report col.c-min   { width: 5.0%; }
.doc table.stock-report col.c-loc   { width: 12.0%; }
.doc table.stock-report col.c-st    { width: 8.0%; }
.doc table.stock-report col.c-photo { width: 5.5%; }

/* Signature block and the run summary that sits above the table. */
.print-report .doc-summary {
  display: flex; flex-wrap: wrap; gap: 6pt 18pt;
  border: 0.5pt solid #6C748A; border-left: 3pt solid var(--navy-800);
  padding: 6pt 9pt; margin: 0 0 8px; font-size: 8.4pt; color: #333;
  -webkit-print-color-adjust: exact; print-color-adjust: exact;
}
.print-report .doc-summary b { color: var(--navy-800); font-variant-numeric: tabular-nums; }

@media print {
  .print-report { padding: 0; background: #fff; }
  .print-report .doc { padding: 0; box-shadow: none; overflow: visible; }
  .print-report .sig-row { margin-top: 16px; break-inside: avoid; }
  .print-report .doc-summary { break-inside: avoid; }
  /* Browsers default <thead>/<tfoot> to these, so a 60-sheet stock report
     already repeats its column headings. Stated explicitly because the report is
     only readable if it keeps doing so: a requirement, not a happy accident. */
  .doc table.stock-report thead { display: table-header-group; }
  .doc table.stock-report tfoot { display: table-footer-group; }
  .doc table.stock-report th { position: static; }
}

/* ---------- departments ----------
   Each rail group is a department with its own colour, repeated as a dot on the
   page's department line and in the More sheet, so a screen can be placed at a
   glance: amber is Stores, blue Maintenance, green Quality, and so on. Colour is
   never the only signal; the department name is always written next to it. */
.z-maintenance { --z: #2F6FD6; }
.z-stores      { --z: #C9722E; }
.z-quality     { --z: #1F9D63; }
.z-procurement { --z: #7B4FD0; }
.z-accounts    { --z: #0E8C8C; }
.z-management  { --z: #5B6479; }
.z-photography { --z: #C2407A; }
.z-admin       { --z: #8791A3; }
.zdot { display: inline-block; flex: none; width: 8px; height: 8px; border-radius: 50%; background: var(--z, var(--ink-3)); }
.rail-mine {
  margin-left: 4px; padding: 1px 6px; border-radius: var(--r-pill); font-size: 9.5px; letter-spacing: .04em;
  text-transform: none; font-weight: 600; color: color-mix(in srgb, var(--z) 55%, var(--ink)); background: color-mix(in srgb, var(--z) 14%, transparent);
}
.dept-crumb {
  display: flex; align-items: center; gap: 7px; margin: -2px 0 10px;
  font: 600 12px/1.2 var(--font); color: var(--ink-3);
}
.dept-crumb b { color: var(--ink); font-weight: 700; letter-spacing: .01em; }
.more-zone {
  display: flex; align-items: center; gap: 7px; margin: 16px 0 8px;
  font: var(--label-font); letter-spacing: var(--label-track); text-transform: uppercase; color: var(--ink-2);
}
.more-zone:first-child { margin-top: 0; }
@media print { .dept-crumb { display: none; } }

/* ============================================================
   Workspace layer: shell, cards and controls in the calm neutral style.
   Sits last so it restyles the shared components every screen is built
   from; page-specific rules above keep their layout.
   ============================================================ */
body {
  background:
    radial-gradient(1100px 520px at 12% -8%, color-mix(in srgb, var(--surface) 80%, transparent), transparent 70%),
    radial-gradient(900px 480px at 100% 0%, color-mix(in srgb, var(--info-bg) 55%, transparent), transparent 70%),
    var(--bg);
  background-attachment: fixed;
  font-feature-settings: "ss01";
}
h1 { font-weight: 600; letter-spacing: -.03em; font-size: clamp(26px, 3.2vw, 38px); line-height: 1.08; text-wrap: balance; }
h2 { font-weight: 600; letter-spacing: -.015em; }
.page-head { margin-bottom: 22px; }
.page-head h1 { margin: 0; }
main.content { padding: 8px var(--pad) 40px; max-width: 1480px; width: 100%; }

/* ---- header ---- */
.topbar {
  height: 76px; gap: 14px; border-bottom: 0;
  background: color-mix(in srgb, var(--bg) 78%, transparent);
}
.topbar.condensed { background: color-mix(in srgb, var(--bg) 92%, transparent); box-shadow: 0 1px 0 var(--line); }
.brand-name b { font-size: 17px; font-weight: 700; letter-spacing: -.02em; }
.top-search { max-width: 560px; }
.topbar .top-search input {
  height: 48px; min-height: 48px; border-radius: var(--r-pill); border: 1px solid var(--line);
  background: color-mix(in srgb, var(--surface) 70%, transparent); padding: 0 48px 0 50px; font-size: 14.5px;
}
.topbar .top-search input:focus { background: var(--surface); }
.top-search .ico { left: 19px; top: 14px; }
.top-search kbd { right: 16px; top: 14px; border-radius: 7px; background: var(--surface); }
.top-actions { gap: 8px; }
.top-quick {
  height: 44px; padding: 0 18px; margin: 0; border-radius: var(--r-pill);
  border: 1px solid var(--line-strong); background: transparent; color: var(--ink); font-weight: 500;
}
.top-quick:hover { background: var(--surface); border-color: var(--line-strong); }
.top-btn {
  width: 46px; height: 46px; border-radius: 50%; background: var(--surface); color: var(--ink-2);
  box-shadow: var(--shadow-1); transition: transform var(--speed) var(--ease), color var(--speed);
}
.top-btn:hover { background: var(--surface); color: var(--ink); transform: translateY(-1px); }
.top-btn .dotbadge { top: 2px; right: 2px; border-color: var(--surface); }
.top-me { display: flex; align-items: center; gap: 10px; padding: 3px 14px 3px 3px; margin-left: 4px; border-radius: var(--r-pill); color: var(--ink); text-decoration: none; }
.top-me:hover { background: var(--surface); text-decoration: none; }
.top-avatar {
  display: grid; place-items: center; width: 44px; height: 44px; border-radius: 50%;
  background: linear-gradient(145deg, var(--accent), color-mix(in srgb, var(--accent) 55%, #7a2d00)); color: #fff;
  font: 700 14px/1 var(--font); letter-spacing: .02em;
}
.top-who { display: flex; flex-direction: column; line-height: 1.2; }
.top-who b { font-size: 14.5px; font-weight: 600; letter-spacing: -.01em; white-space: nowrap; }
.top-who span { font-size: 12.5px; color: var(--ink-3); }
@media (max-width: 1280px) { .top-who { display: none; } .top-me { padding-right: 3px; } }

/* ---- department dock ----
   A floating pill of circles. At rest it is 72px wide and every department reads by
   its tinted icon; hovered or focused it widens over the page to 244px and names each
   one on a single line, with Yours and a waiting count. Width animates on the dock
   alone, positioned inside the sticky rail, so the page never reflows. */
@media (min-width: 901px) { .frame { grid-template-columns: 104px 1fr; } }
.rail {
  top: 76px; height: calc(100dvh - 76px); padding: 0; border-right: 0; background: transparent; overflow: visible;
  z-index: 38; /* above page cards (their transforms make stacking contexts), below the header and sheets */
}
@media (min-width: 901px) { .rail { display: block; } }
.dock {
  position: absolute; z-index: 45; top: 6px; left: 16px; bottom: 16px; width: 72px;
  display: flex; flex-direction: column; gap: 4px; padding: 10px 10px;
  background: color-mix(in srgb, var(--surface) 88%, transparent);
  -webkit-backdrop-filter: blur(14px) saturate(1.3); backdrop-filter: blur(14px) saturate(1.3);
  border: 1px solid var(--line-soft); border-radius: 36px; box-shadow: var(--shadow-1), var(--shadow-2);
  overflow-x: hidden; overflow-y: auto; scrollbar-width: none;
  transition: width 260ms var(--ease), box-shadow 260ms var(--ease);
}
.dock::-webkit-scrollbar { display: none; }
.dock:hover, .dock:focus-within { width: 244px; background: var(--surface); box-shadow: var(--shadow-1), var(--shadow-3); }
.dock-item {
  position: relative; display: flex; align-items: center; gap: 12px; width: 100%; flex: none;
  padding: 2px; border: 0; border-radius: 28px; background: none; color: var(--ink-2);
  font: inherit; text-align: left; text-decoration: none; cursor: pointer;
  transition: background var(--speed) var(--ease);
}
.dock-item:hover { text-decoration: none; background: color-mix(in srgb, var(--ink) 5%, transparent); }
.dock-item:focus-visible { outline: 2px solid var(--focus); outline-offset: 1px; }
.dock-circle {
  position: relative; display: grid; place-items: center; flex: none; width: 48px; height: 48px; border-radius: 50%;
  background: color-mix(in srgb, var(--ink) 5%, transparent); color: var(--ink-2);
  transition: background var(--speed) var(--ease), color var(--speed), transform var(--speed) var(--ease);
}
.dock-circle .ico { width: 21px; height: 21px; }
.dock-item:active .dock-circle { transform: scale(.94); }
/* departments carry their colour in the icon at rest, and fill with it when current */
.dock-item[class*="z-"] .dock-circle { background: color-mix(in srgb, var(--z) 13%, transparent); color: var(--z); }
.dock-item.on .dock-circle { background: var(--navy-800); color: #fff; box-shadow: 0 8px 18px color-mix(in srgb, var(--navy-800) 32%, transparent); }
.dock-item[class*="z-"].on .dock-circle { background: var(--z); color: #fff; box-shadow: 0 8px 18px color-mix(in srgb, var(--z) 38%, transparent); }
[data-theme="dark"] .dock-item.on:not([class*="z-"]) .dock-circle { background: var(--ink); color: var(--bg); }
.dock-item.mine:not(.on) .dock-circle { box-shadow: inset 0 0 0 2px color-mix(in srgb, var(--z) 55%, transparent); }
.dock-badge {
  position: absolute; top: -2px; right: -3px; min-width: 18px; height: 18px; padding: 0 5px; border-radius: var(--r-pill);
  background: var(--bad); color: #fff; font: 700 10px/18px var(--font); text-align: center;
  box-shadow: 0 0 0 2px var(--surface);
}
.dock-label {
  display: flex; flex-direction: column; gap: 1px; min-width: 0; white-space: nowrap;
  opacity: 0; transform: translateX(-6px); transition: opacity 160ms var(--ease), transform 220ms var(--ease);
}
.dock:hover .dock-label, .dock:focus-within .dock-label { opacity: 1; transform: none; transition-delay: 70ms; }
.dock-label b { font-size: 14.5px; font-weight: 600; color: var(--ink); letter-spacing: -.01em; }
.dock-item.on .dock-label b { color: var(--ink); }
.dock-meta { display: flex; align-items: center; gap: 6px; font-size: 12px; line-height: 1.3; }
.dock-meta > span + span::before { content: "·"; margin-right: 6px; color: var(--ink-3); }
.dock-yours { color: color-mix(in srgb, var(--z) 55%, var(--ink)); font-weight: 600; }
.dock-waiting { color: var(--bad); font-weight: 500; }
.dock-caption { position: relative; display: block; height: 22px; flex: none; margin: 4px 0; }
.dock-caption::before { content: ""; position: absolute; left: 12px; right: 12px; top: 50%; height: 1px; background: var(--line); transition: right 260ms var(--ease); }
.dock-caption span {
  position: absolute; left: 12px; top: 50%; transform: translateY(-50%); padding-right: 8px;
  font: 600 10.5px/1 var(--font); letter-spacing: .08em; text-transform: uppercase; color: var(--ink-3);
  background: var(--surface); opacity: 0; transition: opacity 160ms;
}
.dock:hover .dock-caption span, .dock:focus-within .dock-caption span { opacity: 1; }
.dock-grow { flex: 1; min-height: 8px; }
.dock-out { margin: 0; }
/* short screens: tighter circles so every department still fits without scrolling */
@media (max-height: 820px) and (min-width: 901px) {
  .dock { gap: 2px; padding: 8px; }
  .dock-circle { width: 42px; height: 42px; }
  .dock { width: 64px; }
}
@media (prefers-reduced-motion: reduce) { .dock, .dock-label { transition: none; } }

/* ---- department bar and pill tabs ---- */
.dept-bar { display: flex; flex-wrap: wrap; align-items: center; gap: 10px 16px; margin: 4px 0 22px; }
.dept-bar .dept-crumb { margin: 0; text-decoration: none; font-size: 13px; }
.dept-bar .dept-crumb:hover b { text-decoration: underline; }
.pills { display: flex; gap: 8px; overflow-x: auto; scrollbar-width: none; padding: 2px; margin: 0; }
.pills::-webkit-scrollbar { display: none; }
.pills a {
  display: inline-flex; align-items: center; gap: 7px; height: 42px; padding: 0 18px; flex: none;
  border-radius: var(--r-pill); border: 1px solid var(--line-strong); color: var(--ink);
  font: 500 14px/1 var(--font); white-space: nowrap; text-decoration: none; background: transparent;
  transition: background var(--speed) var(--ease), color var(--speed), border-color var(--speed);
}
.pills a:hover { background: var(--surface); text-decoration: none; }
.pills a.on { background: var(--navy-800); border-color: var(--navy-800); color: #fff; }
[data-theme="dark"] .pills a.on { background: var(--ink); border-color: var(--ink); color: var(--bg); }
.pills .count { font: 700 11px/18px var(--font); min-width: 20px; padding: 0 6px; border-radius: var(--r-pill); background: var(--bad); color: #fff; text-align: center; }
.pills-solo { margin: 0 0 20px; }

/* ---- cards ---- */
.panel, .tagcard, .tablewrap, .card, .card-soft {
  border: 0; border-radius: var(--r-lg); background: var(--surface); box-shadow: var(--shadow-1), var(--shadow-2);
}
.tagcard { padding: 20px 22px; }
.tagcard.t-ok, .tagcard.t-warn, .tagcard.t-bad, .tagcard.t-info, .tagcard.t-accent { box-shadow: none; border: 0; }
.panel-head { padding: 20px 24px 14px; border-bottom: 0; }
.panel-head h2 { font-size: 19px; font-weight: 600; letter-spacing: -.02em; }
.panel-foot { border-top: 1px solid var(--line-soft); }
.tagcard.stat { border-radius: var(--r-md); }
.att-card { border: 0 !important; border-radius: var(--r-lg); box-shadow: none; padding: 20px 22px; }
.att-card .att-v { font-weight: 600; letter-spacing: -.04em; }
.att-card .btn { border-radius: var(--r-pill); }
.empty { border-radius: var(--r-lg); }

/* ---- controls ---- */
.btn { border-radius: var(--r-pill); min-height: 46px; padding: 10px 22px; font-weight: 600; letter-spacing: -.005em; }
.btn-sm { min-height: 38px; padding: 6px 16px; border-radius: var(--r-pill); }
.btn-ghost { background: transparent; border-color: var(--line-strong); color: var(--ink); }
.btn-ghost:hover { background: var(--surface); color: var(--ink); }
.chip { border-radius: var(--r-pill); border-color: transparent; font-weight: 600; }
input:not([type]), input[type="text"], input[type="search"], input[type="number"], input[type="date"], input[type="email"],
input[type="password"], select, textarea { border-radius: var(--r-sm); border-color: var(--line-strong); background: var(--surface); }
.sheet { border-radius: var(--r-lg) var(--r-lg) 0 0; }
@media (min-width: 901px) { .sheet { border-radius: var(--r-lg); } }

/* ---- tables ---- */
.tablewrap { padding: 6px 8px; }
table.data th {
  background: transparent; border-bottom: 1px solid var(--line-soft); color: var(--ink-3);
  font: 600 12px/1.3 var(--font); letter-spacing: .02em; text-transform: none;
}
table.data tbody tr:nth-child(even) td { background: transparent; }
table.data tbody tr:hover td { background: var(--surface-2); }
table.data tbody tr:hover td:first-child { border-radius: 14px 0 0 14px; }
table.data tbody tr:hover td:last-child { border-radius: 0 14px 14px 0; }

/* ---- quick actions, queue links ---- */
.action-tile { border: 0; border-radius: var(--r-pill); background: var(--surface); box-shadow: var(--shadow-1); padding: 0 20px; min-height: 48px; }
.action-tile:hover { border: 0; background: var(--surface); box-shadow: var(--shadow-1), var(--shadow-2); }
.action-tile.primary { background: var(--navy-800); }
.queue-link { border-radius: var(--r-md); }

/* ---- Stores desk ------------------------------------------------------ */
.stores-desk { margin-top: 18px; }
.stores-desk-head { display: flex; align-items: baseline; justify-content: space-between; margin-bottom: 10px; }
.stores-desk-head .section-h { margin: 0; }
.stores-desk-lead { margin: 3px 0 0; color: var(--ink-3); font-size: 13px; }
.stores-desk-grid, .stores-desk-more-grid { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 12px; }
.stores-desk-card, .stores-desk-more-card {
  display: flex; align-items: center; gap: 12px; min-width: 0; min-height: 96px; padding: 14px 16px;
  border-radius: var(--r-lg); background: var(--surface); color: var(--ink); box-shadow: var(--shadow-1);
  text-decoration: none; transition: transform var(--speed) var(--ease), box-shadow var(--speed) var(--ease), background var(--speed);
}
.stores-desk-card:hover, .stores-desk-more-card:hover { text-decoration: none; transform: translateY(-1px); box-shadow: var(--shadow-1), var(--shadow-2); }
.stores-desk-icon { display: grid; place-items: center; flex: none; width: 38px; height: 38px; border-radius: 12px; background: var(--bg-accent); color: var(--accent-ink); }
.stores-desk-icon .ico { width: 21px; height: 21px; }
.stores-desk-copy { display: flex; flex-direction: column; gap: 5px; min-width: 0; }
.stores-desk-copy b { font-size: 15px; line-height: 1.15; }
.stores-desk-copy small { overflow: hidden; color: var(--ink-3); font-size: 12px; line-height: 1.25; text-overflow: ellipsis; white-space: nowrap; }
.stores-desk-disabled { cursor: not-allowed; opacity: .55; background: var(--muted-bg); box-shadow: none; }
.stores-desk-more { margin-top: 16px; }
.stores-desk-more-grid { gap: 8px; }
.stores-desk-more-card { min-height: 64px; padding: 10px 12px; border-radius: var(--r-md); box-shadow: none; border: 1px solid var(--line-soft); background: transparent; }
.stores-desk-more-card .stores-desk-icon { width: 30px; height: 30px; border-radius: 9px; }
.stores-desk-more-card .stores-desk-icon .ico { width: 17px; height: 17px; }
.stores-desk-more-card .stores-desk-copy { gap: 3px; }
.stores-desk-more-card .stores-desk-copy b { font-size: 13px; }
.stores-desk-approvals { margin-top: 16px; padding: 14px 16px; border: 1px solid var(--warn-line); border-radius: var(--r-lg); background: var(--warn-bg); }
.stores-desk-approval-head { display: flex; align-items: baseline; gap: 8px; margin-bottom: 8px; }
.stores-desk-approval-head span { color: var(--ink-3); font-size: 12px; }
.stores-desk-approval-links { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px; }
.stores-desk-approval-links a { display: flex; align-items: center; gap: 8px; min-height: 56px; padding: 8px 10px; border-radius: var(--r-md); background: var(--surface); color: var(--ink); text-decoration: none; }
.stores-desk-approval-links a:hover { text-decoration: none; box-shadow: var(--shadow-1); }
.stores-desk-approval-links a > span { color: var(--warn); }
.stores-desk-approval-links a > span .ico { width: 18px; height: 18px; }
.stores-desk-approval-links a b { font-size: 18px; }
@media (max-width: 700px) {
  .stores-desk-grid, .stores-desk-more-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px; }
  .stores-desk-card { min-height: 92px; padding: 12px; gap: 9px; }
  .stores-desk-card .stores-desk-icon { width: 32px; height: 32px; }
  .stores-desk-card .stores-desk-icon .ico { width: 18px; height: 18px; }
  .stores-desk-copy b { font-size: 13px; }
  .stores-desk-copy small { font-size: 11px; }
  .stores-desk-approval-links { grid-template-columns: 1fr; }
}

/* ---- home and workspace headings ---- */
.home-hero .hh-date { margin: 8px 0 0; font-size: 15.5px; color: var(--ink-2); }
.home-hero h1 { font-size: clamp(32px, 4.4vw, 52px); font-weight: 600; letter-spacing: -.035em; }
.ws-head { margin: 6px 0 26px; }
.ws-head h1 { font-size: clamp(34px, 4.6vw, 56px); margin: 0 0 10px; }
.ws-lead { margin: 0; max-width: 62ch; font-size: 16px; color: var(--ink-2); text-wrap: pretty; }
.ws-figs { display: grid; grid-template-columns: repeat(auto-fit, minmax(210px, 1fr)); gap: 16px; margin-bottom: 30px; }
.ws-fig { display: flex; flex-direction: column; gap: 6px; padding: 22px 24px; animation: ws-in 420ms var(--ease) both; animation-delay: calc(var(--i, 0) * 60ms); }
.ws-fig-l { font-size: 14px; color: var(--ink-2); }
.ws-fig-v { font-size: clamp(34px, 3.6vw, 46px); font-weight: 600; letter-spacing: -.04em; line-height: 1; font-variant-numeric: tabular-nums; }
.ws-fig-h { font-size: 13px; color: var(--ink-3); }
.ws-asof { grid-column: 1 / -1; margin: -4px 0 0; font-size: 12.5px; color: var(--ink-3); }
@keyframes ws-in { from { opacity: 0; transform: translateY(8px); } }
.ws-screens h2 { font-size: 22px; margin: 0 0 14px; }
.ws-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)); gap: 16px; }
.ws-card {
  display: flex; flex-direction: column; gap: 6px; padding: 20px 22px 22px; color: var(--ink); text-decoration: none;
  transition: transform var(--speed) var(--ease), box-shadow var(--speed) var(--ease);
}
.ws-card:hover { text-decoration: none; transform: translateY(-2px); box-shadow: var(--shadow-1), var(--shadow-3); }
.ws-card:active { transform: translateY(0) scale(.99); }
.ws-card b { font-size: 18px; font-weight: 600; letter-spacing: -.02em; margin-top: 10px; }
.ws-about { font-size: 14px; color: var(--ink-2); line-height: 1.45; }
.ws-card-top { display: flex; align-items: center; gap: 8px; }
.ws-ico {
  display: grid; place-items: center; width: 46px; height: 46px; border-radius: 50%;
  background: color-mix(in srgb, var(--z) 13%, var(--surface)); color: var(--z);
}
.ws-ico .ico { width: 21px; height: 21px; }
.ws-badge { padding: 4px 10px; border-radius: var(--r-pill); background: var(--bad-bg); color: var(--bad); font: 600 12px/1.2 var(--font); }
.ws-go {
  margin-left: auto; display: grid; place-items: center; width: 38px; height: 38px; border-radius: 50%;
  border: 1px solid var(--line-strong); color: var(--ink-2); transition: background var(--speed), color var(--speed);
}
.ws-go .ico { width: 17px; height: 17px; }
.ws-card:hover .ws-go { background: var(--navy-800); color: #fff; border-color: var(--navy-800); }
@media (prefers-reduced-motion: reduce) { .ws-fig { animation: none; } .ws-card { transition: none; } }
@media (max-width: 900px) { .ws-figs { grid-template-columns: 1fr 1fr; } }

/* ---- workspace charts ---- */
.ws-chart { padding: 22px 26px 20px; margin-bottom: 30px; }
.ws-chart-head { display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between; gap: 8px 18px; margin-bottom: 12px; }
.ws-chart-head h2 { margin: 0; font-size: 21px; }
.ws-legend { display: flex; flex-wrap: wrap; gap: 6px 16px; font-size: 13px; color: var(--ink-2); }
.lg { display: inline-flex; align-items: center; gap: 7px; }
.lg i { width: 9px; height: 9px; border-radius: 50%; background: var(--c); flex: none; }
.lg-accent { --c: var(--accent); } .lg-info { --c: var(--info); } .lg-ok { --c: var(--ok); } .lg-muted { --c: var(--muted-line); }
.ws-lines { width: 100%; height: 220px; display: block; overflow: visible; }
.ws-lines .grid { stroke: var(--line-soft); stroke-dasharray: 3 5; }
.ws-lines .line { fill: none; stroke-width: 2.4; vector-effect: non-scaling-stroke; stroke-linecap: round; }
.ws-lines .l-accent { stroke: var(--accent); } .ws-lines .l-info { stroke: var(--info); }
.ws-lines .area { stroke: none; opacity: .14; }
.ws-lines .a-accent { fill: var(--accent); } .ws-lines .a-info { fill: var(--info); }
.ws-months { display: flex; justify-content: space-between; padding: 8px 2px 0; font-size: 12.5px; color: var(--ink-3); }
.ws-chart-empty { margin: 10px 0 0; font-size: 13.5px; color: var(--ink-3); max-width: 70ch; }
.ws-donut-row { display: flex; flex-wrap: wrap; align-items: center; gap: 18px 36px; }
.ws-donut { width: 190px; height: 190px; flex: none; }
.ws-donut circle { fill: none; stroke-width: 26; transform: rotate(-90deg); transform-origin: 90px 90px; }
.ws-donut .track { stroke: var(--surface-2); }
.ws-donut .s-ok { stroke: var(--ok); } .ws-donut .s-accent { stroke: var(--accent); } .ws-donut .s-muted { stroke: var(--muted-line); }
.ws-donut .c-v { text-anchor: middle; font: 600 26px var(--font); fill: var(--ink); letter-spacing: -.03em; }
.ws-donut .c-l { text-anchor: middle; font: 500 12px var(--font); fill: var(--ink-3); }
.ws-slices { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 12px; min-width: 240px; flex: 1; }
.ws-slices li { font-size: 15px; color: var(--ink-2); }
.ws-slices li span { flex: 1; }
.ws-slices li b { margin-left: auto; font-size: 17px; color: var(--ink); font-variant-numeric: tabular-nums; }

[data-theme="dark"] .btn-danger { background: #B83A42; color: #fff; }
[data-theme="dark"] .btn-danger:hover { background: #A5313A; }

/* ---- polish pass: one tab language, rows that stay inside the screen ----
   In-page filter tabs (.subnav) speak the same pill language as the department
   tabs; long rows fade at the edge so it is plain they scroll; nothing is allowed to
   push the page wider than a phone. */
.dept-bar > .pills { flex: 1 1 auto; min-width: 0; max-width: 100%; }
.pills, .subnav {
  -webkit-mask-image: linear-gradient(to right, #000 calc(100% - 28px), transparent);
          mask-image: linear-gradient(to right, #000 calc(100% - 28px), transparent);
  padding-right: 24px;
}
.subnav { gap: 8px; margin: 0 0 20px; border-bottom: 0; padding-block: 2px; }
.subnav a {
  height: 40px; min-height: 40px; padding: 0 16px; margin: 0; border: 1px solid var(--line-strong); border-radius: var(--r-pill);
  font: 500 13.5px/1 var(--font); color: var(--ink); background: transparent;
  transition: background var(--speed) var(--ease), color var(--speed), border-color var(--speed);
}
.subnav a:hover { background: var(--surface); }
.subnav a.on, [data-theme="dark"] .subnav a.on { background: var(--navy-800); border-color: var(--navy-800); color: #fff; }
[data-theme="dark"] .subnav a.on { background: var(--ink); border-color: var(--ink); color: var(--bg); }
.subnav .count { font: 700 11px/18px var(--font); background: var(--bad); color: #fff; min-width: 20px; text-align: center; }
.subnav a.on .count { box-shadow: 0 0 0 2px var(--navy-800); }
.seg { background: var(--bg-accent); padding: 4px; }
.seg input:checked + label, .seg label:has(input:checked) { background: var(--surface); color: var(--ink); box-shadow: var(--shadow-1); }
[data-theme="dark"] .seg input:checked + label, [data-theme="dark"] .seg label:has(input:checked) { background: var(--ink); color: var(--bg); }
.sh-body { overflow-x: auto; }
@media (max-width: 600px) {
  .doc table.lines { display: block; overflow-x: auto; white-space: nowrap; }
}
/* A table's screen-reader-only header cell is absolutely positioned. Without a
   positioned scroll box it escaped the box's clipping and widened the whole page on
   phones (Staff and Inspectors were 630px wide on a 375px screen). */
.tablewrap { position: relative; }
/* White on the dark theme's light red failed contrast; counts and badges take a
   deeper red at night. */
[data-theme="dark"] .pills .count, [data-theme="dark"] .subnav .count,
[data-theme="dark"] .dock-badge, [data-theme="dark"] .top-btn .dotbadge,
[data-theme="dark"] .mh-btn .dotbadge { background: #B83A42; color: #fff; }
/* Plain .card wrappers had no padding of their own: once they became white cards the
   rows inside sat flush against the edge. */
.card { padding: 20px 24px; }
.card > .section-h:first-child { margin-top: 0; }
/* The header gives the search box the room it needs: below 1360px the Request,
   Suggest and Present pills fold to circles (their names stay as labels). */
.top-quick span { margin-left: 2px; }
@media (max-width: 1360px) and (min-width: 901px) {
  .top-quick { width: 46px; padding: 0; justify-content: center; border-radius: 50%; background: var(--surface); border-color: transparent; box-shadow: var(--shadow-1); }
  .top-quick span { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
  .top-search { min-width: 200px; }
}
/* Seat tiles and form ticks in the same language as everything else. */
.pv-grid { gap: 10px; }
.pv-card { border: 0; border-radius: var(--r-md); background: var(--surface-2); padding: 14px 16px;
  transition: background var(--speed) var(--ease), transform var(--speed) var(--ease); }
.pv-card:hover { background: var(--bg-accent); transform: translateY(-1px); }
input[type="checkbox"], input[type="radio"] { accent-color: var(--navy-800); width: 18px; height: 18px; }
[data-theme="dark"] input[type="checkbox"], [data-theme="dark"] input[type="radio"] { accent-color: var(--ink); }

/* ============================================================
   Finish layer: brand tile, sign-in, micro-interactions, browser
   surfaces and a deeper dark theme.
   ============================================================ */

/* ---- brand tile ----
   The JAGS globe is printed on white, and its navy swoosh disappears on a dark
   surface. It always sits on its own white tile, in both themes, like a badge. */
.brand-mark {
  display: grid; place-items: center; width: 44px; height: 44px; flex: none;
  background: #fff; border-radius: 14px;
  box-shadow: 0 1px 2px rgba(15, 26, 31, .08), 0 6px 16px rgba(15, 26, 31, .08);
  transition: transform var(--speed) var(--ease);
}
.brand-mark img { width: 32px; height: 32px; object-fit: contain; }
.topbar .brand:hover .brand-mark { transform: rotate(-6deg) scale(1.04); }
[data-theme="dark"] .brand-mark { box-shadow: 0 0 0 1px rgba(255, 255, 255, .06), 0 8px 20px rgba(0, 0, 0, .45); }

/* ---- headings on home and workspaces ---- */
.ws-head h1 { display: flex; flex-wrap: wrap; align-items: center; gap: 14px; }
.ws-dot { width: 14px; height: 14px; flex: none; }
.ws-yours {
  font: 600 13px/1 var(--font); letter-spacing: 0; padding: 7px 12px; border-radius: var(--r-pill);
  color: color-mix(in srgb, var(--z) 55%, var(--ink)); background: color-mix(in srgb, var(--z) 14%, transparent);
}

/* ---- sign in ----
   Half the screen is the JAGS hangar at golden hour, the other half is the task.
   The photograph drifts very slowly; the card rises once. */
body:has(.auth-wrap) { background: var(--bg); }
.auth-wrap {
  min-height: 100dvh; padding: 0; display: grid; grid-template-columns: minmax(0, 1.15fr) minmax(420px, .85fr);
  place-items: stretch; background: var(--bg);
}
.auth-art { position: relative; overflow: hidden; margin: 14px 0 14px 14px; border-radius: 30px; background: #1a1410; isolation: isolate; }
.auth-art picture, .auth-art img { position: absolute; inset: 0; width: 100%; height: 100%; }
.auth-art img { object-fit: cover; object-position: 62% 50%; transform-origin: 70% 55%; animation: auth-drift 28s var(--ease) both; }
@keyframes auth-drift { from { transform: scale(1.1); } to { transform: scale(1); } }
.auth-art::after {
  content: ""; position: absolute; inset: 0; z-index: 1;
  background: linear-gradient(180deg, rgba(8, 10, 14, .05) 40%, rgba(8, 10, 14, .72) 100%);
}
.auth-art-copy { position: absolute; z-index: 2; left: 36px; right: 36px; bottom: 34px; display: flex; align-items: flex-end; gap: 16px; color: #fff; }
.auth-art-copy p { margin: 0; max-width: 24ch; font: 600 clamp(24px, 2.6vw, 34px)/1.12 var(--font); letter-spacing: -.03em; text-wrap: balance; text-shadow: 0 2px 18px rgba(0, 0, 0, .35); }
.auth-art-mark { display: none; place-items: center; width: 52px; height: 52px; flex: none; border-radius: 16px; background: #fff; box-shadow: 0 10px 30px rgba(0, 0, 0, .35); }
.auth-art-mark img { position: static; width: 34px; height: 34px; animation: none; object-fit: contain; }
.auth-card {
  align-self: center; justify-self: center; width: min(420px, calc(100% - 48px)); max-width: none; margin: 32px 0;
  background: transparent; border: 0; box-shadow: none; border-radius: 0; overflow: visible;
  animation: auth-rise 520ms var(--ease) both;
}
@keyframes auth-rise { from { opacity: 0; transform: translateY(14px); } }
.auth-head { padding: 0 0 26px; background: none; color: var(--ink); }
.auth-head .brand-mark { width: 60px; height: 60px; border-radius: 18px; margin-bottom: 26px; }
.auth-head .brand-mark img { width: 42px; height: 42px; }
.auth-head h1 { font-size: clamp(32px, 3.4vw, 42px); font-weight: 600; letter-spacing: -.035em; margin: 0 0 8px; }
.auth-head p { margin: 0; font-size: 15px; color: var(--ink-2); }
.auth-body { padding: 0; }
.auth-body .field > label { font-size: 13.5px; }
.auth-body input:not([type="checkbox"]) { min-height: 52px; border-radius: var(--r-sm); background: var(--surface); }
.auth-body .btn-block { min-height: 54px; font-size: 15.5px; margin-top: 4px; }
.auth-body p.small { color: var(--ink-2); }
@media (max-width: 860px) {
  .auth-wrap { grid-template-columns: 1fr; grid-template-rows: auto 1fr; }
  .auth-art { margin: 10px 10px 0; height: 34dvh; min-height: 220px; border-radius: 24px; }
  .auth-art-copy { left: 20px; right: 20px; bottom: 18px; gap: 12px; }
  .auth-art-copy p { font-size: 21px; }
  .auth-art-mark { display: grid; width: 42px; height: 42px; border-radius: 13px; }
  .auth-art-mark img { width: 28px; height: 28px; }
  .auth-card { width: calc(100% - 40px); margin: 26px 0 36px; }
  .auth-head .brand-mark { display: none; }
}

/* ---- micro-interactions ----
   The dock's language everywhere a hand goes: a small lift on hover, a press you can
   feel, eased on the same curve, and nothing that moves layout. */
.btn, .top-quick, .pills a, .subnav a, .seg label, .action-tile, .queue-link, .att-card .btn, .ws-go, .x {
  transition: background-color var(--speed) var(--ease), color var(--speed) var(--ease), border-color var(--speed) var(--ease),
              box-shadow var(--speed) var(--ease), transform 140ms var(--ease);
}
.btn:not(:disabled):hover { transform: translateY(-1px); box-shadow: 0 6px 16px color-mix(in srgb, var(--navy-800) 18%, transparent); }
.btn-ghost:not(:disabled):hover { box-shadow: 0 4px 12px rgba(15, 26, 31, .06); }
.btn:not(:disabled):active, .top-quick:active, .pills a:active, .subnav a:active, .seg label:active,
.action-tile:active, .top-btn:active, .x:active { transform: translateY(0) scale(.97); box-shadow: none; }
.btn:disabled { transform: none; }
a.tagcard, a.att-card, .att-card, a.card-soft, .rep-tile, .an-kpi { transition: transform var(--speed) var(--ease), box-shadow var(--speed) var(--ease), background-color var(--speed); }
a.tagcard:hover, a.an-kpi:hover { transform: translateY(-2px); box-shadow: var(--shadow-1), var(--shadow-3); text-decoration: none; }
a.tagcard:active, a.an-kpi:active, .rep-tile:active, .ws-card:active { transform: scale(.99); }
.att-card:hover { transform: translateY(-2px); }
.queue-link::after { transition: transform var(--speed) var(--ease); }
.queue-link:hover { background: var(--surface); }
table.data tbody tr[data-href]:active td { background: var(--bg-accent); }
input, select, textarea { transition: border-color var(--speed) var(--ease), box-shadow var(--speed) var(--ease), background-color var(--speed); }
input:not([type="checkbox"]):not([type="radio"]):focus, select:focus, textarea:focus {
  outline: 0; border-color: var(--focus); box-shadow: 0 0 0 4px color-mix(in srgb, var(--focus) 18%, transparent);
}
.topbar .top-search input:focus { box-shadow: 0 0 0 4px color-mix(in srgb, var(--focus) 14%, transparent); }
.chip { transition: background-color var(--speed); }
/* One authored arrival: the page body settles in once per navigation. */
main.content > * { animation: page-in 360ms var(--ease) both; }
main.content > :nth-child(2) { animation-delay: 40ms; }
main.content > :nth-child(3) { animation-delay: 80ms; }
main.content > :nth-child(n+4) { animation-delay: 120ms; }
/* perf: no opacity here. The view transition already fades the page in, and an
   opacity start held every section invisible for up to 480ms after it had painted. */
@keyframes page-in { from { transform: translateY(6px); } }

/* ---- browser surfaces ---- */
::selection { background: color-mix(in srgb, var(--accent) 30%, transparent); color: var(--ink); }
input, textarea { caret-color: var(--accent); }
a { text-underline-offset: .18em; text-decoration-thickness: from-font; }
:focus-visible { outline: 2px solid var(--focus); outline-offset: 3px; border-radius: 8px; }
html { scrollbar-color: color-mix(in srgb, var(--ink-3) 45%, transparent) transparent; scrollbar-width: thin; }
::-webkit-scrollbar { width: 10px; height: 10px; }
::-webkit-scrollbar-thumb { background: color-mix(in srgb, var(--ink-3) 40%, transparent); border-radius: 10px; border: 2px solid transparent; background-clip: padding-box; }
::-webkit-scrollbar-track { background: transparent; }
.num, td.num, .ws-fig-v, .att-v { font-variant-numeric: tabular-nums; }

/* ---- a deeper dark ----
   Night surfaces separate by light, not by lines: each raised layer is a step
   lighter with a faint top highlight, status tints are drawn from the status colour
   over the surface instead of muddy fixed browns, and the canvas carries a cool glow. */
[data-theme="dark"] {
  --bg: #0A0F12;
  --bg-accent: #141C20;
  --surface: #121A1E;
  --surface-2: #182227;
  --line: rgba(205, 225, 235, .09);
  --line-strong: rgba(205, 225, 235, .15);
  --line-soft: rgba(205, 225, 235, .055);
  --ok-bg: color-mix(in srgb, #7CC99A 10%, #121A1E);
  --warn-bg: color-mix(in srgb, #E3B65E 10%, #121A1E);
  --bad-bg: color-mix(in srgb, #EE8B90 10%, #121A1E);
  --info-bg: color-mix(in srgb, #93B3EE 10%, #121A1E);
  --muted-bg: #182227;
  --shadow-2: 0 12px 32px rgba(0, 0, 0, .42);
}
[data-theme="dark"] body {
  background:
    radial-gradient(1000px 520px at 8% -10%, rgba(120, 160, 190, .10), transparent 70%),
    radial-gradient(900px 500px at 100% 0%, rgba(224, 122, 46, .06), transparent 70%),
    var(--bg);
}
[data-theme="dark"] :is(.panel, .tagcard, .tablewrap, .card, .card-soft, .att-card, .rep-tile, .an-kpi, .dock) {
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .045), var(--shadow-2);
}
[data-theme="dark"] .topbar { background: color-mix(in srgb, var(--bg) 82%, transparent); }
[data-theme="dark"] .btn:not(.btn-ghost):not(.btn-danger):not(.btn-accent) { background: #E9EEF0; color: #0A0F12; }
[data-theme="dark"] .btn:not(.btn-ghost):not(.btn-danger):not(.btn-accent):hover { background: #FFFFFF; }
[data-theme="dark"] .btn:not(:disabled):hover { box-shadow: 0 8px 22px rgba(0, 0, 0, .45); }
[data-theme="dark"] ::selection { background: color-mix(in srgb, var(--accent) 38%, transparent); color: #fff; }

@media (prefers-reduced-motion: reduce) {
  .auth-art img, .auth-card, main.content > * { animation: none; }
  .btn:not(:disabled):hover, a.tagcard:hover, .att-card:hover, a.an-kpi:hover { transform: none; }
}

/* Pipeline bands: floating cards whose status reads from a dot, not a thick bottom rule. */
.band { position: relative; border: 0; border-radius: var(--r-md); padding: 16px 16px 14px 30px;
  box-shadow: var(--shadow-1), var(--shadow-2); transition: transform var(--speed) var(--ease), box-shadow var(--speed) var(--ease); }
.band::before { content: ""; position: absolute; left: 14px; top: 22px; width: 8px; height: 8px; border-radius: 50%; background: var(--muted-line); }
.band.b-bad::before { background: var(--bad); } .band.b-warn::before { background: var(--warn); }
.band.b-ok::before { background: var(--ok); } .band.b-info::before { background: var(--info); }
a.band:hover { transform: translateY(-2px); box-shadow: var(--shadow-1), var(--shadow-3); }
.band-v { font-weight: 700; font-family: var(--font); letter-spacing: -.03em; }
[data-theme="dark"] .band { box-shadow: inset 0 1px 0 rgba(255, 255, 255, .045), var(--shadow-2); }

/* ---- JAGS Aviation logo ----
   The logo always sits on white, as it is printed: a badge on the sign-in photograph
   and on the dashboard banner. */
.auth-logo {
  display: block; width: min(240px, 70%); margin: 0 0 28px; padding: 14px 18px; background: #fff;
  border-radius: 18px; box-shadow: 0 1px 2px rgba(15, 26, 31, .06), 0 10px 26px rgba(15, 26, 31, .08);
}
.auth-logo img { display: block; width: 100%; height: auto; }
[data-theme="dark"] .auth-logo { box-shadow: 0 0 0 1px rgba(255, 255, 255, .06), 0 12px 30px rgba(0, 0, 0, .45); }
@media (max-width: 860px) {
  .auth-logo { width: 170px; margin-bottom: 20px; padding: 10px 14px; border-radius: 14px; }
  .auth-art-mark { display: none; }
}


/* ---- dashboard banner ----
   The first thing on home: the JAGS hangar at golden hour under a tonal wash that
   keeps the title legible, the dashboard's name in white, and the JAGS Aviation logo
   on a white badge. Compact, so the work still starts above the fold. */
.hero-banner {
  position: relative; isolation: isolate; overflow: hidden; display: flex; align-items: flex-end; justify-content: space-between;
  gap: 20px; min-height: clamp(170px, 22vw, 230px); margin: 0 0 26px; padding: clamp(20px, 2.6vw, 30px) clamp(22px, 3vw, 34px);
  border-radius: var(--r-lg); background: #1a1410; color: #fff;
  box-shadow: var(--shadow-1), 0 18px 40px rgba(15, 26, 31, .14);
}
.hero-photo, .hero-photo img { position: absolute; inset: 0; width: 100%; height: 100%; z-index: -2; }
.hero-photo img { object-fit: cover; object-position: 50% 58%; transform: scale(1.04); animation: hero-settle 900ms var(--ease) both; }
@keyframes hero-settle { from { transform: scale(1.1); } }
.hero-banner::before {
  content: ""; position: absolute; inset: 0; z-index: -1;
  background:
    linear-gradient(95deg, rgba(8, 11, 15, .82) 0%, rgba(8, 11, 15, .55) 42%, rgba(8, 11, 15, .12) 78%, rgba(8, 11, 15, .04) 100%),
    linear-gradient(0deg, rgba(8, 11, 15, .35), transparent 55%);
}
.hero-copy { position: relative; display: flex; flex-direction: column; gap: 6px; min-width: 0; margin: 0; }
.hero-banner h1 { margin: 0; color: #fff; font-size: clamp(30px, 4vw, 50px); letter-spacing: -.035em; text-shadow: 0 2px 20px rgba(0, 0, 0, .25); }
.hero-banner .hh-date { margin: 0; color: rgba(255, 255, 255, .82); font-size: 15px; }
.hero-btn { align-self: flex-start; margin-top: 10px; background: rgba(255, 255, 255, .95); color: #0F1A1F; }
.hero-btn:hover { background: #fff; }
.hero-logo {
  position: relative; flex: none; align-self: flex-start; width: clamp(150px, 16vw, 220px); padding: 10px 14px;
  background: #fff; border-radius: 16px; box-shadow: 0 10px 28px rgba(0, 0, 0, .28);
  animation: hero-logo-in 700ms 150ms var(--ease) both;
}
@keyframes hero-logo-in { from { opacity: 0; transform: translateY(-8px); } }
.hero-logo img { display: block; width: 100%; height: auto; }
main.content > .hero-banner { animation: none; }
[data-theme="dark"] .hero-banner { box-shadow: inset 0 1px 0 rgba(255, 255, 255, .05), 0 18px 40px rgba(0, 0, 0, .45); }
@media (max-width: 700px) {
  .hero-banner { min-height: 208px; padding: 18px; border-radius: 22px; flex-direction: column; align-items: stretch; justify-content: flex-end; }
  .hero-logo { position: absolute; top: 14px; right: 14px; width: 108px; padding: 6px 9px; border-radius: 12px; }
  .hero-banner h1 { font-size: 28px; }
  .hero-banner .hh-date { font-size: 13.5px; }
}

/* ---- sign-in photograph: the logo on it, and a calmer wash ---- */
.auth-art::after {
  background:
    linear-gradient(180deg, rgba(8, 11, 15, .45) 0%, rgba(8, 11, 15, .08) 30%, rgba(8, 11, 15, .10) 55%, rgba(8, 11, 15, .78) 100%),
    radial-gradient(120% 90% at 70% 45%, transparent 40%, rgba(8, 11, 15, .35) 100%);
}
.auth-art img { filter: saturate(.92) contrast(1.03); }
.auth-art-logo {
  position: absolute; z-index: 2; top: 28px; left: 28px; width: clamp(160px, 17vw, 230px); padding: 10px 14px;
  background: #fff; border-radius: 16px; box-shadow: 0 14px 34px rgba(0, 0, 0, .32);
  animation: hero-logo-in 700ms 200ms var(--ease) both;
}
.auth-art-logo img { position: static; display: block; width: 100%; height: auto; animation: none; filter: none; }
.auth-mark { width: 56px; height: 56px; border-radius: 17px; margin-bottom: 24px; }
.auth-mark img { width: 40px; height: 40px; }
@media (max-width: 860px) {
  .auth-art-logo { top: 14px; left: 14px; width: 132px; padding: 7px 10px; border-radius: 12px; }
  .auth-mark { display: none; }
}

/* ---- Demo view bar ---- */
.demobar { background: color-mix(in srgb, var(--accent) 12%, var(--surface)); border-color: color-mix(in srgb, var(--accent) 35%, transparent); }
.demobar .pv-eye { color: var(--accent-ink); }
.pv-card.pv-demo { background: color-mix(in srgb, var(--accent) 10%, var(--surface-2)); }
@media (prefers-reduced-motion: reduce) { .hero-photo img, .hero-logo, .auth-art-logo { animation: none; } }

/* ---- phones: filter switches and paired buttons share the width evenly ----
   A three-way switch left-aligned under the title left a ragged gap on the right of
   every list page, and two card buttons sized by their words never lined up down the
   crib. Both now split the row evenly. */
@media (max-width: 700px) {
  .page-head > .seg { display: flex; flex: 1 1 100%; flex-wrap: nowrap; overflow-x: auto; scrollbar-width: none; }
  .page-head > .seg label { flex: 1 0 auto; justify-content: center; text-align: center; white-space: nowrap; padding-inline: 8px; }
  .cards-mobile table.data.tools td.c-act .btn-sm { flex: 1 1 0; }
}

/* ---- phones: the header never cuts a seat name short ----
   Beside four 44px buttons, a seat like PROCUREMENT was clipped to "PROC…" on phones up to
   about 400px. It moves onto the date line instead, where there is room. The dashboard
   banner drops the date the header already shows. */
.mh-role-inline, .hh-short { display: none; }
@media (max-width: 420px) {
  .mh-role { visibility: hidden; }
  .mh-role-inline { display: inline; }
}
@media (max-width: 900px) {
  .hero-banner .hh-day { display: none; }
  .hero-banner .hh-short { display: inline; }
}

/* ---- figure cards in a row share it ----
   Sized by their own words, the four figures on Imports stacked on a phone as four
   ragged widths. They split the row on desktop and sit two by two on a phone. */
.cluster > .tagcard.stat { flex: 1 1 150px; }
@media (max-width: 700px) {
  .cluster:has(> .tagcard.stat) { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); align-items: stretch; }
}

/* Stores floor labels: location and mapped shelf spot lead; no release state is
   printed because it changes while the label remains on the rack. */
.label:has(.l-location) { grid-template-rows: auto auto auto auto; }
.label:has(.l-location) .l-location { grid-column: 1 / -1; font: 700 16px/1.15 var(--mono); letter-spacing: .01em; }
.label:has(.l-location) .l-location span { font-size: 11px; font-weight: 600; letter-spacing: .04em; }
.label:has(.l-location) .l-pn { grid-column: 1; grid-row: 2; }
.label:has(.l-location) .l-desc { grid-column: 1; grid-row: 3; }
.label:has(.l-location) .l-meta { grid-column: 1; grid-row: 4; }

.label:has(.l-location) .l-qr { grid-row: 2 / span 3; }

/* Give-out lot chooser: replace browser fieldset defaults with page tokens. */
.lot-choice { min-inline-size: 0; margin: 0; border: 0; padding: 14px 0 0; }
.lot-choice legend { padding: 0; font-size: 13px; font-weight: 600; color: var(--ink-2); }
.lot-choice > .stack > label {
  min-height: 44px; border: 1px solid var(--line); border-radius: var(--r-sm);
  background: var(--surface-2); cursor: pointer;
  transition: border-color var(--speed) var(--ease), box-shadow var(--speed) var(--ease), background var(--speed) var(--ease);
}
.lot-choice > .stack > label input[type="radio"] { flex: none; margin-top: 2px; accent-color: var(--accent); }
.lot-choice > .stack > label:has(input[type="radio"]:checked) {
  border-color: var(--accent); background: var(--surface); box-shadow: inset 0 0 0 1px var(--accent);
}

/* ---------- Post book page grid ---------------------------------------
   The paper page is a wide, fixed ledger at a desk and a sequence of compact
   cards on a phone. The wrapper owns the desktop scroll so the application
   shell never becomes wider than the viewport. */
.book-form, .book-grid { min-width: 0; max-width: 100%; }
.book-grid {
  position: relative;
  overflow-x: auto; overscroll-behavior-inline: contain;
  border: 1px solid var(--line); border-radius: var(--r-md); background: var(--surface);
}
.book-table {
  --book-cell-x: 8px;
  --book-cell-y: 6px;
  width: 1342px; min-width: 1180px; table-layout: fixed;
  font-size: 13px;
}
.book-table th, .book-table td { padding: var(--book-cell-y) var(--book-cell-x); }
.book-table th { overflow: hidden; text-overflow: ellipsis; }
.book-table .book-line {
  width: 32px; padding-inline: 4px; text-align: center; color: var(--ink-3);
  font: 700 12px/1 var(--mono); font-variant-numeric: tabular-nums;
}
.book-line-mobile { display: none; }
.book-table thead th:nth-child(1)  { width: 32px; }
.book-table thead th:nth-child(2)  { width: 150px; }
.book-table thead th:nth-child(3)  { width: 190px; }
.book-table thead th:nth-child(4)  { width: 110px; }
.book-table thead th:nth-child(5)  { width: 64px; }
.book-table thead th:nth-child(6)  { width: 96px; }
.book-table thead th:nth-child(7)  { width: 120px; }
.book-table thead th:nth-child(8)  { width: 120px; }
.book-table thead th:nth-child(9)  { width: 110px; }
.book-table thead th:nth-child(10) { width: 110px; }
.book-table thead th:nth-child(11) { width: 130px; }
.book-table thead th:nth-child(12) { width: 110px; }
.book-table td { min-width: 0; overflow: hidden; }
.book-table input:not([type="checkbox"]):not([type="hidden"]) {
  display: block; width: 100%; max-width: 100%; min-width: 0; box-sizing: border-box; height: 36px; min-height: 36px;
  padding: 6px 8px; font-size: 13px; line-height: 1.25;
}
.book-table .book-lookup { display: block; width: 100%; max-width: 100%; min-width: 0; box-sizing: border-box; }
.book-table .book-ticks { overflow: visible; }
.book-table .book-ticks label {
  display: flex; align-items: center; gap: 5px; min-width: 0; max-width: 100%; box-sizing: border-box; min-height: 28px;
  color: var(--ink-2); font-size: 12px; line-height: 1.2; white-space: nowrap;
}
.book-table .book-ticks input[type="checkbox"] {
  flex: none; width: 16px; height: 16px; margin: 0; accent-color: var(--accent);
}

@media (max-width: 900px) {
  .book-grid { overflow-x: visible; border: 0; border-radius: 0; background: transparent; }
  table.data.book-table {
    display: block; width: 100%; min-width: 0; table-layout: auto; font-size: 14px;
  }
  .book-table thead { display: none; }
  .book-table tbody { display: grid; grid-template-columns: minmax(0, 1fr); gap: 12px; }
  .book-table tbody tr.book-row {
    display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
    grid-template-areas:
      "line line"
      "part part"
      "description description"
      "qty aircraft"
      "serial batch"
      "issued received"
      "auth date"
      "ticks ticks";
    gap: 10px 8px; min-width: 0; padding: 13px 12px 12px;
    border: 1px solid var(--line); border-radius: var(--r-md);
    background: var(--surface); box-shadow: var(--shadow-1);
  }
  .book-table tbody tr.book-row:hover td { background: none !important; }
  .book-table th.book-line, .book-table td {
    display: grid; grid-template-columns: minmax(0, 1fr); align-content: start;
    min-width: 0; width: auto; padding: 0; border: 0; background: none !important;
  }
  .book-table th.book-line { grid-area: line; display: block; width: auto; padding: 0 0 2px; text-align: left; }
  .book-line-desktop { display: none; }
  .book-line-mobile { display: inline; color: var(--accent-ink); font: var(--label-font); letter-spacing: var(--label-track); text-transform: none; }
  .book-table td::before {
    content: attr(data-label); display: block; margin-bottom: 4px;
    color: var(--ink-2); font: 600 12px/1.25 var(--font);
  }
  .book-table td[data-label="Part No."] { grid-area: part; }
  .book-table td[data-label="Description"] { grid-area: description; }
  .book-table td[data-label="Qty."] { grid-area: qty; }
  .book-table td[data-label="Aircraft"] { grid-area: aircraft; }
  .book-table td[data-label="Serial No."] { grid-area: serial; }
  .book-table td[data-label="Batch No."] { grid-area: batch; }
  .book-table td[data-label="Issued by"] { grid-area: issued; }
  .book-table td[data-label="Rec. By"] { grid-area: received; }
  .book-table td[data-label="Auth By"] { grid-area: auth; }
  .book-table td[data-label="Date"] { grid-area: date; }
  .book-table td.book-ticks { grid-area: ticks; display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px; }
  .book-table td.book-ticks::before { display: none; }
  .book-table .book-ticks label {
    min-width: 0; max-width: 100%; box-sizing: border-box; min-height: 44px; padding: 8px;
    border: 1px solid var(--line); border-radius: var(--r-sm); background: var(--surface-2);
  }
  .book-table .book-ticks label span { min-width: 0; white-space: nowrap; }
  .book-table input:not([type="checkbox"]):not([type="hidden"]) {
    width: 100%; max-width: 100%; min-width: 0; box-sizing: border-box;
    height: 40px; min-height: 40px; padding-inline: 9px; font-size: 14px;
  }
  .book-table .book-lookup { width: 100%; max-width: 100%; min-width: 0; box-sizing: border-box; }
}

/* Keep table content inside its own cell. `break-word` preserves auto-table
   min-content sizing; the narrower `anywhere` exception below is only for the
   action-code card on the audit screen. */
table.data td { overflow-wrap: break-word; }
table.data.items td .chip { white-space: normal; }
.cards-mobile[data-present="trace-audit"] table.data td.c-pn { overflow-wrap: anywhere; }

/* ---------- How a part moves: the walkthrough screen ----------------------
   Built to be read from across a room, so the type is larger than the rest of
   the app and the department colour does the work a legend would otherwise do. */
.jr-hero { padding: 8px 0 22px; border-bottom: 2px solid var(--ink); margin-bottom: 30px; }
.jr-hero .eyebrow {
  font-size: 11px; font-weight: 700; letter-spacing: .16em; text-transform: uppercase;
  color: var(--ink-3); margin: 0 0 10px;
}
.jr-hero h1 { font-size: clamp(1.9rem, 5vw, 2.9rem); line-height: 1.05; margin: 0 0 14px; }
.jr-thesis {
  font-size: clamp(1rem, 2.6vw, 1.5rem); font-weight: 700; letter-spacing: -.01em;
  margin: 0 0 12px; line-height: 1.3;
}
.jr-thesis span { color: var(--accent); padding: 0 .15em; }
.jr-sub { color: var(--ink-2); max-width: 64ch; margin: 0; line-height: 1.6; }

.jr-spine { list-style: none; margin: 0 0 44px; padding: 0; display: grid; gap: 14px; }
.jr-stage {
  display: grid; grid-template-columns: 56px 1fr; gap: 16px; align-items: start;
  background: var(--surface); border: 1px solid var(--line);
  border-left: 4px solid var(--z, var(--accent));
  border-radius: var(--r-md); padding: 18px 20px 20px; position: relative;
}
.jr-stage + .jr-stage::before {
  content: ""; position: absolute; left: 27px; top: -15px; width: 2px; height: 15px;
  background: var(--z, var(--accent)); opacity: .45;
}
.jr-num {
  font-size: 1.6rem; font-weight: 800; line-height: 1;
  color: var(--z, var(--accent)); font-variant-numeric: tabular-nums;
  border: 2px solid var(--z, var(--accent)); border-radius: 50%;
  width: 46px; height: 46px; display: grid; place-items: center;
}
.jr-body { min-width: 0; }
.jr-who {
  font-size: 11px; font-weight: 700; letter-spacing: .12em; text-transform: uppercase;
  color: var(--z, var(--accent)); margin: 0 0 6px;
}
.jr-body h2 { font-size: clamp(1.1rem, 2.4vw, 1.45rem); margin: 0 0 8px; line-height: 1.2; }
.jr-text { margin: 0 0 10px; color: var(--ink-2); max-width: 68ch; line-height: 1.6; }
.jr-holds {
  margin: 0; padding: 9px 12px; border-radius: var(--r-sm);
  background: color-mix(in srgb, var(--z, var(--accent)) 9%, var(--surface));
  border: 1px solid color-mix(in srgb, var(--z, var(--accent)) 30%, transparent);
  font-weight: 600; font-size: .95rem; line-height: 1.45;
}
.jr-figures { display: flex; flex-wrap: wrap; gap: 10px 26px; margin: 14px 0 0; }
.jr-fig b {
  display: block; font-size: 1.5rem; line-height: 1.1; font-variant-numeric: tabular-nums;
}
.jr-fig span {
  display: block; font-size: 11px; letter-spacing: .08em; text-transform: uppercase;
  color: var(--ink-3); margin-top: 3px;
}
.jr-open { display: inline-block; margin-top: 14px; font-size: .9rem; }

.jr-zones { margin-bottom: 44px; }
.jr-zone-grid {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(178px, 1fr));
  gap: 10px; margin-top: 16px;
}
.jr-zone {
  background: var(--surface); border: 1px solid var(--line);
  border-top: 3px solid var(--line-soft); border-radius: var(--r-md); padding: 14px 15px 15px;
}
/* The three a part passes through are the story; the rest are where it lives after. */
.jr-zone-receiving   { border-top-color: var(--info-line); }
.jr-zone-quarantine  { border-top-color: var(--warn-line); }
.jr-zone-serviceable { border-top-color: var(--ok-line); }
.jr-zone-name {
  margin: 0 0 8px; font-size: 11px; font-weight: 700; letter-spacing: .1em;
  text-transform: uppercase; color: var(--ink-2);
}
.jr-zone-qty { margin: 0; }
.jr-zone-qty b { font-size: 1.7rem; line-height: 1; font-variant-numeric: tabular-nums; }
.jr-zone-meta { margin: 6px 0 0; font-size: 12px; color: var(--ink-3); }
.jr-zone-issuable {
  margin: 8px 0 0; font-size: 12px; font-weight: 700; color: var(--ok);
}
.jr-zone-note { margin: 8px 0 0; font-size: 12px; color: var(--ink-3); line-height: 1.45; }

.jr-value { margin-bottom: 40px; }
.jr-end {
  border-top: 2px solid var(--ink); padding-top: 20px;
  display: flex; flex-wrap: wrap; gap: 12px; align-items: center;
}
.jr-end p { margin: 0; flex: 1 1 320px; color: var(--ink-2); max-width: 62ch; }

@media (max-width: 640px) {
  .jr-stage { grid-template-columns: 40px 1fr; gap: 12px; padding: 15px 15px 17px; }
  .jr-num { width: 36px; height: 36px; font-size: 1.2rem; }
  .jr-stage + .jr-stage::before { left: 22px; }
}
@media print {
  .jr-open, .jr-end .btn { display: none; }
  .jr-stage { break-inside: avoid; }
}
a.jr-zone { text-decoration: none; color: inherit; display: block; transition: border-color .12s, transform .12s; }
a.jr-zone:hover { border-color: var(--accent); transform: translateY(-1px); }
a.jr-zone:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.jr-zone-open { margin: 10px 0 0; font-size: 12px; font-weight: 700; color: var(--accent); }
@media (prefers-reduced-motion: reduce) { a.jr-zone:hover { transform: none; } }

/* The callout number from a parts-catalogue drawing. A storekeeper holding a bolt
   needs the number to look for, not the words "OEM illustration". */
.pic-callout {
  display: flex; align-items: center; gap: 10px; margin: 10px 0 0;
  padding: 9px 11px; border-radius: var(--r-sm);
  background: color-mix(in srgb, var(--accent) 10%, var(--surface));
  border: 1px solid color-mix(in srgb, var(--accent) 34%, transparent);
  max-width: min(280px, 100%); line-height: 1.35;
}
.pic-callout-n {
  flex: 0 0 auto; display: grid; place-items: center;
  width: 34px; height: 34px; border-radius: 50%;
  background: var(--accent); color: #fff;
  font-weight: 800; font-size: 15px; font-variant-numeric: tabular-nums;
}
