/* ============================================================================
   Blitz V2 — blitz.css · the ONE stylesheet (S0 scope)
   ----------------------------------------------------------------------------
   Reorganized from the old app's 2,108-line monolith stylesheet + the 53-line
   V2 shim that patched it. EVERY value is verbatim from those two files —
   this is a reorganization, not a redesign. The old app's look is the spec;
   its file structure is the anti-spec.

   Carried: only what the current consumers use —
     shell.js (topbar/sidebar/nav), motion.js (.am-*), hero-scene.js
     (#hero-scene), login.html, settings.html, user_management.html.
   Dropped: all page-specific sections for the 34 unbuilt pages (stat cards,
   machine tiles, progress bars, tabs, hero-band, org chart, workflow, kpi,
   chart containers, loading-overlay, view-toggle, status dots, …). They come
   back stage by stage from the old repo's stylesheet (the reference oracle).

   ONE approved deviation from the old app: body { margin-left } is scoped to
   body.with-sidebar (shell.js adds the class at mount), so the sidebar-less
   login page is truly centered instead of shoved sideways (old-app bug).
   Everything else is pixel-identical.

   Sections:  1 TOKENS · 2 BASE · 3 SHELL · 4 COMPONENTS · 5 MOTION
   ========================================================================== */


/* ============================================================================
   1. TOKENS
   ----------------------------------------------------------------------------
   Each custom property is defined ONCE per theme, directly with its resolved
   value. The old file kept two parallel names for most colors (--app- brand
   tokens aliased into the legacy --bg- / --btn- / --nav- names); the carried
   rules
   consume the legacy names (plus --app-focus-ring / --app-ember, which they
   use directly), so those are the names kept. Values identical to the old
   resolved chain. Dark overrides list only properties that actually change.
   ========================================================================== */
:root {
  /* Surfaces (S10.26: cool neutral light — page < section/card < inset < raised) */
  --bg-primary: #F4F6F9;        /* page */
  --bg-secondary: #FFFFFF;      /* section / card / container */
  --bg-tertiary: #EEF1F5;       /* inset: table header, stat card */
  --bg-raised: #E6EBF2;         /* an opened panel's head, an opened row */

  /* Text */
  --text-primary: #11141B;      /* headings / strong text */
  --text-secondary: #2E3440;    /* body text */
  --text-muted: #596173;        /* secondary / muted text */

  /* ── THE SHARED PALETTE (tidied 2026-10-17). Every colour a page needs is named here ONCE; page files use
     these names and never a hex of their own. ── */
  /* Text that went well / badly / needs a look — profit, a loss, a warning (each passes 4.5:1 on a card) */
  --good: #137535;
  --bad: #b91c1c;
  --warn-text: #A14B07;
  /* The amber edge on a "check this" note, and its wash */
  --warn-line: #d97706;
  --warn-tint: rgba(217,119,6,.10);
  /* A neutral wash behind a note, a picked row, a hover — reads on both themes */
  --tint: rgba(127,127,127,.07);
  /* Words on a filled colour, and words on the dashboard photograph */
  --on-fill: #fff;
  --on-photo: #FAF8F4;
  /* Solid tones for dots, swatches and filled badges (same in both themes) */
  --tone-red: #ef4444;
  --tone-amber: #f59e0b;
  --tone-yellow: #ca8a04;
  --tone-green: #22c55e;
  --tone-grey: #6b7280;
  --tone-violet: #8b5cf6;
  --tone-purple: #a855f7;
  --tone-orange: #f97316;
  --tone-cyan: #06b6d4;
  /* Tinted chips — background / words / edge — for badges and status notes */
  --chip-blue-bg: #dbeafe;   --chip-blue-fg: #1e40af;   --chip-blue-line: #93c5fd;
  --chip-violet-bg: #ede9fe; --chip-violet-fg: #5b21b6; --chip-violet-line: #c4b5fd;
  --chip-green-bg: #dcfce7;  --chip-green-fg: #166534;  --chip-green-line: #86efac;
  --chip-amber-bg: #fef3c7;  --chip-amber-fg: #92400e;  --chip-amber-line: #fcd34d;
  --chip-red-bg: #fee2e2;    --chip-red-fg: #991b1b;    --chip-red-line: #fca5a5;
  /* Filled backgrounds that carry white words (a toast) — same in both themes */
  --fill-good: #15803d;
  --fill-bad: #dc2626;
  /* The shadow behind words on the dashboard photograph */
  --photo-shadow: rgba(0,0,0,0.55);
  /* Energy page chart: eight series. 1 and 6 were the ember accent and its hover; S10.26 keeps them as they were (a data
     colour does not follow the accent). */
  --en-series-1: #E0531F;
  --en-series-2: #2FA36B;
  --en-series-3: #3E7CB1;
  --en-series-4: #E1A33A;
  --en-series-5: #8A5FB0;
  --en-series-6: #C2461A;
  --en-series-7: #5A8F7B;
  --en-series-8: #B0573F;
  /* Tally maps: what Tally fixes (f), what the page sets (c), where it lands (z), what is unmapped (x) */
  --map-f: #5a4f8c; --map-f-bg: #eeecf7;
  --map-c: #0f6e5a; --map-c-bg: #e1f2ec;
  --map-z: #8a5a12; --map-z-bg: #f7eddc;
  --map-x: #9a3412; --map-x-bg: #fbe9df;

  /* Borders */
  --border-color: #DDE2EA;      /* borders / dividers */
  --border-light: #C9D0DB;      /* input borders / scrollbar thumb */

  /* Sidebar (always-dark, both themes) */
  --nav-bg: #0B0D13;
  --nav-text: #D5DAE4;
  --nav-text-hover: #fff;       /* same in both themes */

  /* Buttons */
  --btn-primary: #0B6BB5;       /* sky-blue accent (focus, active tab/nav, an opened panel's edge) [USER, 2026-10-22] */
  --btn-primary-hover: #08589A; /* accent, hover */

  /* THE FILL WITH WORDS ON IT — teal, defined ONCE.
     The user picked #1c92b0, but white on it measures 3.6:1 — under the 4.5:1 that a 14px label
     has to meet — so the fill is the same hue one step deeper. Filled buttons only: the ember
     accent stays for rules, focus rings, the active nav bar and the row tints, none of which
     carry text.
     Deliberately NOT redefined for dark: white on this measures 5.1:1 whatever is behind it, and a
     second value would be a second thing to keep accessible. */
  --btn-fill: #0B6BB5;          /* --on-btn on this: 5.5:1 */
  --btn-fill-hover: #08589A;    /* --on-btn on this: 7.3:1 */
  --on-btn: #FFFFFF;            /* words on a filled button */
  --btn-secondary: #3F4654;
  --btn-secondary-hover: #2B303B;
  --on-secondary: #FFFFFF;      /* words on a secondary button */
  --btn-danger: #dc2626;
  --btn-danger-hover: #b91c1c;

  /* Shadows */
  --shadow: rgba(26,20,14,0.06);
  --shadow-lg: rgba(26,20,14,0.10);

  /* Accents consumed directly by carried rules */
  --app-focus-ring: rgba(11,107,181,0.25); /* accent focus glow */
  --app-ember: #0B6BB5;                    /* the brighter accent (moon icon) */

  /* Layout */
  --sidebar-width: 240px;

  /* Typography */
  --font-display: "Inter Tight", "Inter", system-ui, sans-serif;

  /* Motion (see section 5; phone override lives there) */
  --am-ease: cubic-bezier(.22,.61,.36,1);
  --am-dur: 480ms;
  --am-shift: 12px;
}

/* COST PARTS, one colour each, the same on every chart and list [USER, 2026-10-18]. Defined ONCE here; charts take
   them by name (BlitzCharts series `color: '--cost-…'`). The order material → electricity → labour → overhead → markup
   (and electricity → wages → indirect → overhead → depreciation for the machine rate) passed the dataviz palette check
   on the card colour #FFFFFF (light) and #211B14 (dark). Three light ones are under 3:1 against a white card, so every
   chart that uses them labels its values directly and offers a table. */
:root {
  --cost-material: #2a78d6;
  --cost-energy: #eb6834;
  --cost-labour: #1baf7a;
  --cost-overhead: #eda100;
  --cost-markup: #e87ba4;
  --cost-indirect: #6250d6;
  --cost-depreciation: #008300;

  /* THE TYPE SCALE for figure-heavy pages [USER, 2026-10-18: "the text is very small"]: rem only, nothing under 14 px. */
  --text-sm: .9rem;
  --text-base: 1rem;
  --text-lg: 1.15rem;
  --stat-value: 1.6rem;
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --cost-material: #3987e5; --cost-energy: #d95926; --cost-labour: #199e70; --cost-overhead: #c98500;
    --cost-markup: #d55181; --cost-indirect: #9085e9; --cost-depreciation: #008300;
  }
}
:root[data-theme="dark"] {
  --cost-material: #3987e5; --cost-energy: #d95926; --cost-labour: #199e70; --cost-overhead: #c98500;
  --cost-markup: #d55181; --cost-indirect: #9085e9; --cost-depreciation: #008300;
}

/* Collapsed icon-strip sidebar (desktop, toggled by shell.js / Ctrl+B) */
html[data-sidebar="collapsed"] {
  --sidebar-width: 56px;
}

/* Night world — S10.26: black and cool [USER, 2026-10-22: "a black background would look much better"], sky-blue accent */
[data-theme="dark"] {
  --bg-primary: #0A0C12;
  --bg-secondary: #141821;
  --bg-tertiary: #1C212C;
  --bg-raised: #232937;

  --text-primary: #F2F4F8;
  --text-secondary: #C8CDD8;
  --text-muted: #9AA1B2;

  --good: #4ade80;
  --bad: #f87171;
  --warn-text: #fbbf24;
  --warn-line: #fbbf24;
  --chip-blue-bg: #1e3a8a;   --chip-blue-fg: #dbeafe;   --chip-blue-line: #3b82f6;
  --chip-violet-bg: #4c1d95; --chip-violet-fg: #ede9fe; --chip-violet-line: #8b5cf6;
  --chip-green-bg: #166534;  --chip-green-fg: #dcfce7;  --chip-green-line: #22c55e;
  --chip-amber-bg: #451a03;  --chip-amber-fg: #fbbf24;  --chip-amber-line: #92400e;
  --chip-red-bg: #991b1b;    --chip-red-fg: #fee2e2;    --chip-red-line: #ef4444;
  --en-series-2: #34C285;
  --en-series-3: #5E9ED6;
  --en-series-4: #F0B450;
  --en-series-5: #A98BD0;
  --en-series-7: #76B49C;
  --en-series-8: #CF7456;
  --map-f: #b7adf0; --map-f-bg: #262240;
  --map-c: #6fd3b8; --map-c-bg: #153129;
  --map-z: #e8b86a; --map-z-bg: #33260f;
  --map-x: #f4a27a; --map-x-bg: #3a1d10;

  --en-series-1: #FF6B2C;
  --en-series-6: #e0531f;

  --border-color: #2A3040;
  --border-light: #3A4256;

  --nav-bg: #07080C;
  --nav-text: #C3C9D6;

  --btn-primary: #7CC4FF;
  --btn-primary-hover: #A6D8FF;
  --btn-fill: #7CC4FF;
  --btn-fill-hover: #A6D8FF;
  --on-btn: #06223A;
  --btn-secondary: #2A3142;
  --btn-secondary-hover: #3A4256;
  --on-secondary: #F2F4F8;
  --btn-danger: #DC2626;
  --btn-danger-hover: #B91C1C;

  --shadow: rgba(0,0,0,0.45);
  --shadow-lg: rgba(0,0,0,0.6);

  --app-focus-ring: rgba(124,196,255,0.32);
  --app-ember: #7CC4FF;
}


/* ============================================================================
   2. BASE
   ----------------------------------------------------------------------------
   Box model, theme cross-fade, scrollbars, body, headings, table numerals.
   The ember focus ring has no global rule (nor did the old app): it is applied
   per-control via --app-focus-ring on inputs (section 4), the sidebar search
   (section 3), and the login inputs (section 4 · LOGIN).
   ========================================================================== */
* { box-sizing: border-box; }

/* Smooth theme transitions on all elements: duration is 0s until a theme
   attribute exists, then color-ish properties cross-fade at 0.3s. */
*, *::before, *::after {
  transition-property: background-color, border-color, color, fill, stroke, box-shadow;
  transition-duration: 0s;
  transition-timing-function: ease;
}
[data-theme] *, [data-theme] *::before, [data-theme] *::after {
  transition-duration: 0.3s;
}

/* Custom scrollbars (WebKit) */
::-webkit-scrollbar {
  width: 8px;
  height: 8px;
}
::-webkit-scrollbar-track {
  background: var(--bg-primary);
  border-radius: 4px;
}
::-webkit-scrollbar-thumb {
  background: var(--border-light);
  border-radius: 4px;
}
::-webkit-scrollbar-thumb:hover {
  background: var(--text-muted);
}

/* Firefox scrollbar */
* {
  scrollbar-width: thin;
  scrollbar-color: var(--border-light) var(--bg-primary);
}

/* Body — Inter 16px. NOTE: no margin-left here; the sidebar offset is scoped
   to body.with-sidebar in section 3 (the one approved deviation). The
   margin-left transition still lives here so expand/collapse animates. */
body {
  margin: 0;
  font-family: "Inter", system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI",
               Roboto, Ubuntu, Arial, sans-serif;
  font-feature-settings: "cv11", "ss01";
  background: var(--bg-primary);
  color: var(--text-secondary);
  line-height: 1.6;
  font-size: 16px;
  transition: background 0.3s, color 0.3s, margin-left 0.25s ease;
}

/* Headings — Inter Tight, tightened tracking, h2 carries the divider rule */
h1, h2, h3 { margin-top: 0; color: var(--text-primary); letter-spacing: -0.015em; font-family: var(--font-display); }
h1 { font-size: 1.75rem; font-weight: 700; }
h2 { font-size: 1.4rem; font-weight: 700; padding-bottom: 8px; margin-bottom: 16px; border-bottom: 1px solid var(--border-color); }
h3 { font-size: 1.1rem; font-weight: 600; }

@media (max-width: 600px) {
  h2 { font-size: 20px; }
  h3 { font-size: 16px; }
}

/* Table numerals align in columns. (The old file also listed
   .stat-card .stat-value and .kpi-value here — dropped with their pages.) */
td { font-variant-numeric: tabular-nums; }

/* The hidden attribute must beat every display rule below (a.button/button.button are
   inline-block, which otherwise overrides the UA's hidden default and shows hidden buttons). */
[hidden] { display: none !important; }


/* ============================================================================
   3. SHELL
   ----------------------------------------------------------------------------
   Injected by shell.js on every page except login: mobile topbar + hamburger,
   overlay, always-dark sidebar (brand, theme toggle, collapse toggle, search,
   grouped nav links), desktop icon-strip collapse, mobile drawer.
   .nav-link-logout is a behavior hook only (no rule here or in the old app).
   ========================================================================== */

/* THE approved deviation: the old app put margin-left on every <body>, which
   also shoved the sidebar-less login page sideways. shell.js now tags the
   body it mounts into with .with-sidebar; only that body gets the offset. */
body.with-sidebar { margin-left: var(--sidebar-width); }

/* ---- Mobile top bar ---- */
.topbar {
  display: none;
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  height: 52px;
  background: var(--nav-bg);
  align-items: center;
  padding: 0 12px;
  z-index: 90;
  gap: 12px;
}

.topbar-title {
  font-weight: 700;
  color: var(--on-fill);
  font-size: 16px;
  flex: 1;
}

/* Hamburger button (three bars -> X when .open) */
.hamburger {
  background: none;
  border: none;
  cursor: pointer;
  padding: 8px;
  display: flex;
  flex-direction: column;
  gap: 5px;
}
.hamburger span {
  display: block;
  width: 22px;
  height: 2px;
  background: #fff;
  border-radius: 2px;
  transition: transform 0.3s, opacity 0.3s;
}
.hamburger.open span:nth-child(1) { transform: translateY(7px) rotate(45deg); }
.hamburger.open span:nth-child(2) { opacity: 0; }
.hamburger.open span:nth-child(3) { transform: translateY(-7px) rotate(-45deg); }

/* ---- Sidebar overlay (mobile drawer backdrop) ---- */
.sidebar-overlay {
  display: none;
  position: fixed;
  inset: 0;
  background: rgba(0,0,0,0.5);
  z-index: 95;
}
.sidebar-overlay.open { display: block; }

/* ---- Sidebar ---- */
.sidebar {
  position: fixed;
  top: 0;
  left: 0;
  width: var(--sidebar-width);
  height: 100vh;
  background: var(--nav-bg);
  display: flex;
  flex-direction: column;
  z-index: 100;
  overflow-y: auto;
  overflow-x: hidden;
  transition: transform 0.3s ease, width 0.25s ease;
}

.sidebar-header {
  padding: 16px 18px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  border-bottom: 1px solid rgba(255,255,255,0.1);
  flex-shrink: 0;
  gap: 6px;
}

.sidebar-brand {
  font-weight: 700;
  font-size: 16px;
  color: var(--on-fill);
}

.sidebar-header-actions {
  display: flex;
  align-items: center;
  gap: 4px;
}

/* ---- Theme toggle (sun / moon) — used in both topbar and sidebar ---- */
.theme-toggle {
  background: rgba(255,255,255,0.1);
  border: none;
  border-radius: 50%;
  width: 36px;
  height: 36px;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 18px;
  transition: background 0.2s;
}
.theme-toggle:hover { background: rgba(255,255,255,0.2); }

.theme-icon-light { display: inline; color: #fbbf24; }
.theme-icon-dark { display: none; color: var(--app-ember); }
[data-theme="dark"] .theme-icon-light { display: none; }
[data-theme="dark"] .theme-icon-dark { display: inline; }

/* ---- Collapse toggle button (desktop only) ---- */
.collapse-toggle {
  background: transparent;
  border: 1px solid rgba(255,255,255,0.15);
  color: rgba(255,255,255,0.7);
  width: 28px;
  height: 28px;
  border-radius: 6px;
  cursor: pointer;
  font-size: 16px;
  line-height: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  transition: background 0.15s, color 0.15s, border-color 0.15s;
}
.collapse-toggle:hover {
  background: rgba(255,255,255,0.08);
  color: var(--on-fill);
  border-color: rgba(255,255,255,0.3);
}
.collapse-icon-expanded { display: inline; }
.collapse-icon-collapsed { display: none; }
html[data-sidebar="collapsed"] .collapse-icon-expanded { display: none; }
html[data-sidebar="collapsed"] .collapse-icon-collapsed { display: inline; }

/* ---- Sidebar search ("/" focuses; filters nav links) ---- */
.sidebar-search {
  padding: 10px 14px 6px;
  flex-shrink: 0;
}
.sidebar-search input {
  width: 100%;
  background: rgba(255,255,255,0.08);
  border: 1px solid rgba(255,255,255,0.1);
  color: var(--on-fill);
  font-size: 13px;
  padding: 6px 10px;
  border-radius: 6px;
  outline: none;
  transition: border-color 0.15s, background 0.15s, box-shadow 0.15s;
  font-family: inherit;
}
.sidebar-search input::placeholder { color: rgba(255,255,255,0.4); }
.sidebar-search input:focus {
  border-color: var(--btn-primary);
  background: rgba(255,255,255,0.12);
  box-shadow: 0 0 0 2px var(--app-focus-ring);
}

/* ---- Nav ---- */
.sidebar-nav {
  flex: 1;
  padding: 8px 0;
  overflow-y: auto;
  scrollbar-color: rgba(255,255,255,0.15) transparent;
}
.sidebar-nav::-webkit-scrollbar-track { background: transparent; }
.sidebar-nav::-webkit-scrollbar-thumb { background: rgba(255,255,255,0.15); border-radius: 4px; }
.sidebar-nav::-webkit-scrollbar-thumb:hover { background: rgba(255,255,255,0.25); }

.nav-group { margin-bottom: 4px; }

.nav-group-title {
  padding: 14px 18px 6px;
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  /* 0.5, NOT the old app's 0.4. At 0.4 these section headings land on #797471 over the sidebar's
     #1f1812 — 3.79:1, under the 4.5:1 that 11px text has to meet, and measurably unreadable for
     anyone with reduced contrast sensitivity. 0.5 measures 5.2:1 and is the smallest step that
     clears it. This is the one value in the carried-over nav that fails an accessibility scan, and
     it failed it on every page in the app, not on one. */
  color: rgba(255,255,255,0.5);
}

.nav-link {
  display: block;
  padding: 9px 18px 9px 24px;
  color: var(--nav-text);
  text-decoration: none;
  font-weight: 500;
  font-size: 13.5px;
  border-left: 3px solid transparent;
  transition: background 0.15s, color 0.15s, border-color 0.15s, padding-left 0.15s;
}
.nav-link:hover {
  background: rgba(255,255,255,0.06);
  color: var(--nav-text-hover);
  padding-left: 28px;              /* the hover slide */
}
.nav-link.active {
  background: rgba(255,255,255,0.1);
  color: var(--on-fill);
  border-left-color: var(--btn-primary);  /* the active ember bar */
  font-weight: 700;
}

/* ---- Collapsed sidebar (icon-strip mode, desktop only) ----
   Links shrink to their 2-letter mnemonic from data-letter. */
html[data-sidebar="collapsed"] .sidebar-brand,
html[data-sidebar="collapsed"] .sidebar-search,
html[data-sidebar="collapsed"] .nav-group-title,
html[data-sidebar="collapsed"] .desktop-theme-toggle {
  display: none;
}
html[data-sidebar="collapsed"] .sidebar-header {
  padding: 12px 6px;
  justify-content: center;
}
html[data-sidebar="collapsed"] .sidebar-header-actions { gap: 0; }
html[data-sidebar="collapsed"] .nav-link {
  font-size: 0;
  text-align: center;
  padding: 10px 0;
  border-left: 3px solid transparent;
}
html[data-sidebar="collapsed"] .nav-link::before {
  content: attr(data-letter);
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.5px;
  display: inline-block;
}
html[data-sidebar="collapsed"] .nav-link:hover { padding-left: 0; }
html[data-sidebar="collapsed"] .nav-link.active { background: rgba(255,255,255,0.12); }

/* ---- Mobile drawer breakpoint ---- */
@media (max-width: 768px) {
  /* Deviation-consistent: offset + topbar clearance only under the shell */
  body.with-sidebar { margin-left: 0; padding-top: 52px; }

  .sidebar { top: 52px; height: calc(100vh - 52px); width: 240px; transform: translateX(-100%); }
  .sidebar.open { transform: translateX(0); }

  .sidebar-header { display: none; }
  .topbar { display: flex; }
  .desktop-theme-toggle,
  .desktop-collapse-toggle { display: none; }
  .sidebar-overlay.open { top: 52px; }

  /* Mobile ignores collapse state — hamburger controls visibility */
  html[data-sidebar="collapsed"] .sidebar-brand,
  html[data-sidebar="collapsed"] .sidebar-search,
  html[data-sidebar="collapsed"] .nav-group-title { display: block; }
  html[data-sidebar="collapsed"] .nav-link { font-size: 13.5px; text-align: left; padding: 9px 18px 9px 24px; }
  html[data-sidebar="collapsed"] .nav-link::before { content: none; }
}


/* ============================================================================
   4. COMPONENTS
   ----------------------------------------------------------------------------
   Container · cards · buttons · forms · tables · badges · alerts · toast ·
   spinner · modal · user cards + permission grid · footer · login · hero.
   ========================================================================== */

/* ---- Page container ---- */
.container {
  margin: 20px;
  padding: 22px 26px;
  background: var(--bg-secondary);
  border-radius: 14px;
  box-shadow: 0 1px 3px var(--shadow), 0 8px 24px var(--shadow-lg);
  transition: background 0.3s, box-shadow 0.3s;
}

/* ---- Cards ---- */
.card {
  flex: 1;
  min-width: 240px;
  padding: 16px 20px;
  border: 1px solid var(--border-color);
  border-radius: 12px;
  background: var(--bg-secondary);
  transition: background 0.3s, border-color 0.3s, transform 0.2s, box-shadow 0.2s;
}
.card:hover {
  transform: translateY(-2px);
  box-shadow: 0 4px 12px var(--shadow-lg);
  border-color: var(--border-light);
}

/* ---- Buttons ---- */
a.button, button.button {
  display: inline-block;
  padding: 9px 16px;
  background: var(--btn-fill);
  color: var(--on-btn);
  border-radius: 8px;
  text-decoration: none;
  font-size: 14px;
  font-weight: 600;
  border: none;
  cursor: pointer;
  box-shadow: 0 1px 3px rgba(0,0,0,0.12), 0 1px 2px rgba(0,0,0,0.08);
  transition: background 0.2s, transform 0.15s, box-shadow 0.15s;
}
a.button:hover, button.button:hover {
  background: var(--btn-fill-hover);
  transform: translateY(-1px);
  box-shadow: 0 4px 8px rgba(0,0,0,0.15), 0 2px 4px rgba(0,0,0,0.1);
}
a.button:active, button.button:active {
  transform: translateY(0);
  box-shadow: 0 1px 2px rgba(0,0,0,0.1);
}

a.button.secondary, button.button.secondary { background: var(--btn-secondary); color: var(--on-secondary); }
a.button.secondary:hover, button.button.secondary:hover { background: var(--btn-secondary-hover); }

a.button.danger, button.button.danger { background: var(--btn-danger); color: var(--on-fill); }
a.button.danger:hover, button.button.danger:hover { background: var(--btn-danger-hover); }

/* Small action buttons + their row (user cards) — from the V2 shim */
.button.btn-sm { padding: 4px 10px; font-size: 0.85em; }
.btn-group { display: flex; gap: 8px; flex-wrap: wrap; }

/* ---- Forms ----
   `.field` USED TO BE A BARE HOOK, unstyled here and in the old app, and that is why labels and
   inputs did not line up: with no rule, both are inline-level and simply flow as text, so where a
   label lands depends on how much room the browser happens to have. The same form read "Name
   [box]   HSN Code [box]" at one zoom level and put HSN Code on its own line at another.

   Five pages had each written their own near-identical fix (customers, factory_config,
   item_history, daily_performance, tally). The pages that had not — item types, sales, orders,
   tasks, machine item, machine settings — got the raw flow. So the rule lives HERE, once, and the
   copies are gone: a label above its control, the control filling the column.

   `min-width: 0` matters more than it looks: a grid or flex item will otherwise refuse to shrink
   below its content, which is how a long select pushes a form wider than its card. */
.field { margin-bottom: 12px; min-width: 0; }
.form-grid .field { margin-bottom: 0; }
.field > label {
  display: block;
  margin-bottom: 5px;
  font-size: 0.85em;
}
.field > input:not([type="checkbox"]):not([type="radio"]),
.field > select,
.field > textarea {
  width: 100%;
  min-width: 0;
  box-sizing: border-box;
}
/* A checkbox and its words belong on one line, not stacked. */
.field > label.check-label { display: flex; align-items: center; gap: 6px; font-size: 1em; }

/* The filter bar is deliberately a ROW of small controls, so its labels stay beside nothing and
   above everything — small, tight, and never as tall as a form label. */
.filter-bar .field > label { font-size: 0.78em; margin-bottom: 3px; }

label {
  font-weight: 700;
  color: var(--text-primary);
}

input[type="text"], input[type="email"], input[type="tel"], input[type="password"], input[type="search"], input[type="url"], input[type="date"], input[type="datetime-local"], input[type="time"], input[type="month"], input[type="week"], input[type="number"], select, textarea {
  padding: 9px 12px;
  border: 1px solid var(--border-light);
  border-radius: 8px;
  font-size: 14px;
  min-width: 180px;
  background: var(--bg-secondary);
  color: var(--text-secondary);
  transition: background 0.2s, border-color 0.2s, color 0.2s, box-shadow 0.2s;
}

input[type="text"]:hover, input[type="email"]:hover, input[type="tel"]:hover, input[type="password"]:hover, input[type="search"]:hover, input[type="url"]:hover, input[type="date"]:hover, input[type="datetime-local"]:hover, input[type="time"]:hover, input[type="month"]:hover, input[type="week"]:hover, input[type="number"]:hover, select:hover, textarea:hover {
  border-color: var(--text-muted);
}

/* The ember focus ring */
input[type="text"]:focus, input[type="email"]:focus, input[type="tel"]:focus, input[type="password"]:focus, input[type="search"]:focus, input[type="url"]:focus, input[type="date"]:focus, input[type="datetime-local"]:focus, input[type="time"]:focus, input[type="month"]:focus, input[type="week"]:focus, input[type="number"]:focus, select:focus, textarea:focus {
  outline: none;
  border-color: var(--btn-primary);
  box-shadow: 0 0 0 3px var(--app-focus-ring);
}

input[type="checkbox"] {
  transform: scale(1.15);
  accent-color: var(--btn-primary);
}

/* Help text under fields */
.help {
  font-size: 12px;
  color: var(--text-muted);
  margin-top: 6px;
}

/* Field grid (create/edit user modal) */
.form-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 12px; }

/* ---- Tables — sticky uppercase headers, zebra rows, ember hover wash ---- */
table {
  width: 100%;
  border-collapse: collapse;
  margin-top: 12px;
}

th, td {
  padding: 11px 14px;
  border-bottom: 1px solid var(--border-color);
  text-align: left;
  vertical-align: top;
}

th {
  background: var(--bg-tertiary);
  font-weight: 700;
  font-size: 0.8em;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--text-muted);
  border-bottom: 2px solid var(--border-color);
  position: sticky;
  top: 0;
  z-index: 2;
}

tr:nth-child(even) { background: color-mix(in srgb, var(--bg-tertiary) 40%, transparent); }
tr:hover { background: color-mix(in srgb, var(--btn-primary) 6%, var(--bg-secondary)); }

/* Responsive table — stacked card layout for tablets & phones.
   Tables opt out with class="no-mobile-stack" (horizontal scroll instead);
   cells label themselves via data-label. This is the shared mechanism that
   keeps every table usable on mobile. */
@media (max-width: 768px) {
  table:not(.no-mobile-stack),
  table:not(.no-mobile-stack) thead,
  table:not(.no-mobile-stack) tbody,
  table:not(.no-mobile-stack) th,
  table:not(.no-mobile-stack) td,
  table:not(.no-mobile-stack) tr {
    display: block;
  }

  table:not(.no-mobile-stack) thead tr {
    position: absolute;
    top: -9999px;
    left: -9999px;
  }

  /* The caption is the one part of a table that does NOT get display:block above, and left alone
     it keeps display:table-caption — sizing itself against a table box that no longer exists.
     The sentence then comes down the page as a 40px-wide column of single words, one per line,
     which is unreadable and pushes every card down with it. As a block it is the width of the
     card again. */
  table:not(.no-mobile-stack) caption {
    display: block;
  }

  table:not(.no-mobile-stack) tr {
    margin-bottom: 12px;
    border: 1px solid var(--border-color);
    border-radius: 10px;
    padding: 14px 16px;
    background: var(--bg-secondary);
    box-shadow: 0 1px 2px rgba(0,0,0,0.04);
  }

  table:not(.no-mobile-stack) td {
    border: none;
    position: relative;
    padding: 6px 0 6px 45%;
    text-align: right;
    word-break: break-word;
    font-variant-numeric: tabular-nums;
    min-height: 0;
  }

  table:not(.no-mobile-stack) td::before {
    content: attr(data-label);
    position: absolute;
    left: 0;
    width: 42%;
    padding-right: 10px;
    white-space: nowrap;
    text-align: left;
    font-weight: 600;
    font-size: 13px;
    color: var(--text-muted);
    text-transform: uppercase;
    letter-spacing: 0.02em;
    /* The label is the column's name, not the cell's data: it never inherits a monospace GSTIN or an
       italic "not a Blitz material" from the value it sits beside. */
    font-family: "Inter", system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
    font-style: normal;
  }

  /* Empty data-label = no prefix slot, value gets full width */
  table:not(.no-mobile-stack) td[data-label=""] {
    padding-left: 0;
    text-align: left;
  }

  /* Action cells: horizontal pill of compact buttons */
  table:not(.no-mobile-stack) td[data-label="Actions"] {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    padding-left: 0;
    margin-top: 4px;
  }
  table:not(.no-mobile-stack) td[data-label="Actions"]::before { display: none; }
  table:not(.no-mobile-stack) td[data-label="Actions"] .button {
    width: auto;
    flex: 1 1 auto;
    padding: 7px 12px;
    text-align: center;
  }
}

/* ---- Badges — only the pairs the current pages use ---- */
.badge {
  padding: 4px 12px;
  border-radius: 999px;
  font-size: 12px;
  font-weight: 700;
  display: inline-block;
  letter-spacing: 0.02em;
}

.badge-on  { background: var(--chip-green-bg); color: var(--chip-green-fg); border: 1px solid var(--chip-green-line); }
.badge-off { background: var(--chip-red-bg); color: var(--chip-red-fg); border: 1px solid var(--chip-red-line); }

/* Admin badge (accent background, white text) — from the V2 shim.
   The same pair as a filled button, so it takes the same readable fill and words. */
.badge-admin { background: var(--btn-fill); color: var(--on-btn); }

/* ---- Alerts ---- */
.alert {
  padding: 12px 16px;
  border-radius: 8px;
  margin-bottom: 16px;
  font-weight: 500;
}
.alert-success { background: var(--chip-green-bg); color: var(--chip-green-fg); border: 1px solid var(--chip-green-line); }
.alert-error   { background: var(--chip-red-bg); color: var(--chip-red-fg); border: 1px solid var(--chip-red-line); }

/* Warning bar (also used standalone as a stock-alert bar) — its own padding/
   font-size intentionally override .alert's when combined, as in the old file */
/* S10.26 part 2 [USER, 2026-10-22: no brown background]: the warning bar is the kit's warning note — the page's own
   background, a faint amber wash and an amber edge — not a solid amber-brown block. */
.alert-warning {
  background: var(--warn-tint); color: var(--text-primary); border: 1px solid var(--border-color);
  border-left: 4px solid var(--warn-line); border-radius: 8px; padding: 10px 16px; margin-bottom: 16px; font-size: 14px;
}

/* ---- Toast (fixed bottom-right, JS toggles .show) ---- */
.org-toast { position: fixed; bottom: 24px; right: 24px; padding: 12px 20px; border-radius: 8px; font-size: 0.85rem; font-weight: 600; color: var(--on-fill); z-index: 1000; opacity: 0; transform: translateY(10px); transition: opacity 0.3s, transform 0.3s; pointer-events: none; }
.org-toast.show { opacity: 1; transform: translateY(0); }
.org-toast.success { background: var(--fill-good); }
.org-toast.error { background: var(--fill-bad); }

/* ---- Loading: one quiet ring (S10.26 — the three dots' markup stays in the pages, hidden here) ---- */
#loading-indicator { display: none; text-align: center; padding: 60px 20px; color: var(--text-muted); }
#loading-indicator.active { display: flex; flex-direction: column; align-items: center; justify-content: center; }
#loading-indicator .spinner { display: flex; gap: 8px; margin-bottom: 20px; }
#loading-indicator .spinner::before {
  content: ""; width: 28px; height: 28px; border-radius: 50%; box-sizing: border-box;
  border: 3px solid var(--border-light); border-top-color: var(--btn-primary);
  animation: ringTurn 0.9s linear infinite;
}
#loading-indicator .spinner-dot { display: none; }
#loading-indicator .loading-msg { font-size: 0.95em; color: var(--text-muted); font-weight: 500; }

@keyframes ringTurn { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) { #loading-indicator .spinner::before { animation: none; } }

/* ---- Modal (JS toggles .active) ---- */
.assign-modal { display: none; position: fixed; top: 0; left: 0; right: 0; bottom: 0; background: rgba(0,0,0,0.5); z-index: 1000; align-items: center; justify-content: center; }
.assign-modal.active { display: flex; }
/* WHAT WENT WRONG, AND THE SECOND THING THAT WENT WRONG FIXING IT.
   The dialog scrolls as one block, and a long list inside it pushed the buttons below the fold:
   measured on the categories modal against real data, 277 rows meant 15,000px of content in a
   648px box, with "Close" 14,352px down. The first fix made the box a flex column and DROPPED
   `overflow-y: auto` — which stopped the pushing by CLIPPING everything past 90vh instead, so the
   editor at the bottom became unreachable altogether. Worse, and caught only because a spec
   clicked a button that was suddenly not actionable.
   So: the box still scrolls exactly as it always did, the ✕ is STICKY inside that scroll so it
   never leaves, and any list within is capped so it scrolls itself rather than the dialog. */
.assign-modal-content {
  background: var(--bg-primary);
  border-radius: 8px;
  /* The right padding is wider on purpose: it is the gutter the sticky ✕ rides in. Because the ✕
     is sticky it travels down the whole scroll, so if it sat over the content column it would sit
     on top of a different control at every scroll position — measured on the assembled-products
     form, where scrolling to "Add line" parked it on the Customer select.
     AND THE GUTTER IS GIVEN BACK IN max-width. box-sizing is border-box globally (line ~127), so
     without that the extra 28px would come straight out of the content column and every modal in
     the app would quietly narrow. The lane is reserved OUTSIDE the content, not carved out of it. */
  padding: 24px 52px 24px 24px;
  max-width: 528px;   /* 500 + the 28px gutter */
  width: 90%;
  max-height: 90vh;
  overflow-y: auto;
  position: relative;
}

/* A list inside a modal is capped and scrolls in place, so it can never be the thing that pushes
   the controls away. */
.assign-modal-content .table-scroll { max-height: 38vh; overflow-y: auto; overflow-x: auto; }
/* A dialog sits outside the page's kit: its small print and its "nothing here" line, drawn once for every dialog. */
.assign-modal .hint { margin: 6px 0 0; font-size: .85em; line-height: 1.45; color: var(--text-secondary); }
.assign-modal .empty-state { padding: 20px; text-align: center; font-size: .9em; color: var(--text-muted); }

/* The always-there close control, injected by ui.js into every modal so a ninth cannot be built
   without one. Sticky rather than absolute: absolute scrolls away with the content, sticky stays
   at the top of the scrolling box wherever the reader is in it. */
.modal-x {
  position: sticky;
  top: -8px;
  float: right;
  margin: -12px -42px 0 0;
  z-index: 2;
  border: 0;
  background: var(--bg-primary);
  color: var(--text-muted);
  font-size: 1.5rem;
  line-height: 1;
  padding: 0.1rem 0.4rem;
  cursor: pointer;
  border-radius: 6px;
}
.modal-x:hover { background: var(--bg-tertiary); color: var(--text-primary); }
.modal-x:focus-visible { outline: 2px solid var(--btn-primary); outline-offset: 2px; }

.assign-modal-content.modal-wide { max-width: 928px; }   /* 900 + the 28px gutter */

/* ---- User cards + permission grid (user_management) — from the V2 shim ----
   .card supplies bg / border / radius / padding; .cb-view / .cb-edit are
   unstyled JS hooks on the grid checkboxes. */
.user-card { margin-bottom: 12px; }
.user-card-header { display: flex; justify-content: space-between; align-items: center; flex-wrap: wrap; gap: 8px; }
.user-card-header h3 { margin: 0; font-size: 1.1em; }
.user-badges { display: flex; gap: 6px; flex-wrap: wrap; }

/* Select-all row above the permission grid */
.select-all-row { display: flex; gap: 16px; align-items: center; padding: 8px 10px; background: var(--bg-tertiary); border-radius: 6px; margin-bottom: 8px; }
.select-all-row label { font-weight: 600; font-size: 0.9em; cursor: pointer; display: flex; align-items: center; gap: 6px; }

/* A single-tick grid row (the Operator permission): one checkbox spanning View and Edit, labelled */
.perm-single { display: inline-flex; align-items: center; gap: 6px; cursor: pointer; }

/* Grid scroll wrapper (sticky <th> comes free from the global table rules) */
.perm-scroll { max-height: 60vh; overflow-y: auto; }

/* Section-header rows (<td colspan=3>), styled to read like .nav-group-title */
tr.perm-section-header td {
  background: var(--bg-tertiary);
  color: var(--text-muted);
  font-weight: 700;
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  border-top: 2px solid var(--border-color);
}

/* ---- Footer ---- */
.footer {
  text-align: center;
  font-size: 13px;
  color: var(--text-muted);
  margin-top: 24px;
}

/* ---- Login ----
   With the body offset scoped away (section 3), the card is truly centered.
   z-index: 1 lifts the card stack above the fixed hero background. */
.login-container { min-height: 100vh; display: flex; align-items: center; justify-content: center; padding: 1rem; position: relative; z-index: 1; }
.login-box { background: var(--bg-secondary); border: 1px solid var(--border-color); border-radius: 8px; padding: 2rem; width: 100%; max-width: 360px; box-shadow: 0 4px 6px rgba(0,0,0,0.1); }
.login-header { text-align: center; margin-bottom: 1.5rem; }
.login-header h1 { margin: 0 0 0.25rem 0; font-size: 1.5rem; color: var(--text-primary); }
.login-header p { margin: 0; color: var(--text-muted); font-size: 0.9rem; }
.login-form .field { margin-bottom: 1rem; }
.login-form label { display: block; margin-bottom: 0.25rem; font-weight: 500; color: var(--text-primary); }
.login-form input[type="text"], .login-form input[type="password"] { width: 100%; padding: 0.625rem 0.75rem; border: 1px solid var(--border-color); border-radius: 4px; background: var(--bg-primary); color: var(--text-secondary); font-size: 1rem; box-sizing: border-box; }
.login-form input:focus { outline: none; border-color: var(--btn-primary); box-shadow: 0 0 0 2px var(--app-focus-ring); }
.login-form .button { width: 100%; padding: 0.75rem; font-size: 1rem; }
.login-error { background: var(--chip-red-bg); border: 1px solid var(--chip-red-line); color: var(--chip-red-fg); padding: 0.75rem; border-radius: 4px; margin-bottom: 1rem; font-size: 0.9rem; }
.login-footer { text-align: center; margin-top: 1.5rem; font-size: 0.8rem; color: var(--text-muted); }

/* ---- Three.js scene host (login full-bleed background) ----
   The CSS poster gradient is the guaranteed-good fallback; the WebGL canvas
   (when hero-scene.js mounts it) fades in on top and the page is complete
   without it. */
#hero-scene {
  position: relative;
  overflow: hidden;
  background:
    radial-gradient(120% 150% at 80% 15%, rgba(124,196,255,0.22), transparent 55%),
    radial-gradient(80% 120% at 15% 90%, rgba(74,222,128,0.10), transparent 60%),
    linear-gradient(180deg, #0A0C12 0%, #0F1420 58%, #0B0E16 100%);
}
#hero-scene canvas {
  position: absolute; inset: 0;
  width: 100% !important; height: 100% !important;
  display: block; opacity: 0; transition: opacity 0.9s ease;
}
#hero-scene.three-on canvas { opacity: 1; }

/* Login full-bleed background + lifted card so it reads on the dark scene.
   The old app wrote this as bare .hero-login-bg, which loses the position
   battle to #hero-scene (id beats class) — so its login backdrop never
   actually rendered. Fixed here by qualifying the selector; the S10
   dashboard hero-band (#hero-scene without this class) is unaffected. */
#hero-scene.hero-login-bg { position: fixed; inset: 0; z-index: 0; }
.hero-login-bg ~ .login-container .login-box { box-shadow: 0 24px 60px rgba(0,0,0,0.45); }

@media (prefers-reduced-motion: reduce) { #hero-scene canvas { transition: none; } }

/* ---- Components · responsive (tablet ≤ 900px) ---- */
@media (max-width: 900px) {
  .container { margin: 12px; padding: 16px; }
  .card { min-width: 200px; }
  table { font-size: 14px; }
  th, td { padding: 8px 10px; }
}

/* ---- Components · responsive (phone ≤ 600px) ---- */
@media (max-width: 600px) {
  .container { margin: 8px; margin-top: 8px; padding: 12px; border-radius: 8px; }
  .card { min-width: 100%; }
  input[type="text"], input[type="email"], input[type="tel"], input[type="password"], input[type="search"], input[type="url"], input[type="date"], input[type="datetime-local"], input[type="time"], input[type="month"], input[type="week"], input[type="number"], select {
    min-width: 100%;
    width: 100%;
  }
  a.button, button.button { width: 100%; text-align: center; padding: 10px 12px; }
}


/* ---- Components · S2 shared kit (consumed via assets/ui.js) ----
   Generic, page-agnostic pieces only; a page's own layout rules live in
   assets/pages/<page>.css. Sort direction is carried by aria-sort. */
.toolbar { display: flex; justify-content: space-between; align-items: center; gap: 12px; flex-wrap: wrap; margin-bottom: 16px; }
.toolbar h2 { margin: 0; }
.toolbar-actions { display: flex; gap: 8px; flex-wrap: wrap; }

.filter-bar { display: flex; gap: 10px; align-items: flex-end; flex-wrap: wrap; padding: 12px; background: var(--bg-tertiary); border-radius: 8px; margin-bottom: 16px; }
.filter-bar .field { margin: 0; min-width: 140px; flex: 0 1 auto; }
.filter-bar .field label { font-size: 0.78em; }
.filter-bar .filter-actions { display: flex; gap: 8px; }

/* TABS — one definition, shared.
   These lived in sales_reports.css, so the second page that wanted tabs (assembly production) would
   have copied them and the app would have had two. That is the hodgepodge plan §11 names by
   example: table-sort JS pasted four times, fmtHMS nine copies. Port the face, not the mess. */
/* WRAP, because a tab strip that does not is a page that scrolls sideways. Six status tabs at
   phone width pushed the last two past the viewport and took the WHOLE DOCUMENT with them — every
   section on the page then sat in a 390px column inside a 598px canvas, and the horizontal scroll
   was the app's, not the strip's. Nothing asserted it and nothing could: every tab was present,
   visible and clickable, and the suite was entirely green. It was found by looking at the page at
   the width a person holds. Wrapping costs a second row on a narrow screen and nothing at all on a
   wide one, where `flex-wrap` never fires. */
.tabs {
  display: flex; flex-wrap: wrap; gap: 4px;
  border-bottom: 1px solid var(--border-color); margin-bottom: 16px;
}
.tab { background: none; border: none; border-bottom: 2px solid transparent; padding: 10px 16px;
       font: inherit; color: var(--text-muted); cursor: pointer; }
.tab.active { color: var(--text-primary); border-bottom-color: var(--btn-primary); font-weight: 600; }
.tab:focus-visible { outline: 2px solid var(--btn-primary); outline-offset: -2px; }

.table-scroll { overflow-x: auto; }

/* THE ARROW IS A PICTURE, AND THE `/ ""` SAYS SO. Generated content counts towards an element's
   ACCESSIBLE NAME, so without the empty alternative text every sortable column announced itself as
   "Purchase frequency ↕" — the arrow read out loud after every header, and the header no longer
   findable by its own name. `content: <string> / <alt>` is the CSS Content L3 alternative-text
   form: the glyph is still drawn, and it is drawn for the eye only. ui.js:118-161 owns what the
   arrow MEANS; this owns how it is seen. */
th[data-sort] { cursor: pointer; user-select: none; white-space: nowrap; }
th[data-sort]::after { content: "↕" / ""; margin-left: 6px; opacity: 0.35; font-size: 0.85em; }
th[data-sort][aria-sort="ascending"]::after { content: "↑" / ""; opacity: 1; }
th[data-sort][aria-sort="descending"]::after { content: "↓" / ""; opacity: 1; }
th[data-sort]:hover { color: var(--text-primary); }
/* ui.js makes every sortable header focusable, so it needs the ring every other operable thing in
   this app has — an element that takes focus and shows nothing for it is worse than one that
   cannot be reached, because a keyboard user cannot tell where they are. */
th[data-sort]:focus-visible { outline: 2px solid var(--btn-primary); outline-offset: -2px; }

/* Incomplete-data flag (blank recipe values etc.) — never a guess, always a flag */
.badge-warn { background: var(--chip-amber-bg); color: var(--chip-amber-fg); border: 1px solid var(--chip-amber-line); }

/* ============================================================================
   4b. THE PAGE KIT (S10.26 "one look for the whole app")
   ----------------------------------------------------------------------------
   [USER, 2026-10-22: "really hard to read any of it … like somebody spilled text on the page and there's no clear
   sections … decide ONE way, app-wide — how cards appear, how tables are presented, headings and the text between
   them, where things sit, and what feedback the user gets."]
   A page joins by putting `page-kit` on its <main class="container"> (and `is-report` for the big report type);
   every rule below is scoped under it, so a page that has not moved yet cannot pick one up by a shared class name
   (dashboard.css has its own .stat-cards, gate_intake.css its own .section-title). Pieces, top to bottom:
     type scale · the page (no box of its own) · .page-head · .section (+ -head/-title/-count/-actions/-body,
     .is-fold, .is-warn) · .sub-head / .sub-card · .stat-cards / .stat-card · .chart-box · .kv-grid · .note ·
     .empty-state · .hint · tables · the opened panel (BlitzUI.openPanel) · the loading skeleton · a busy button.
   ========================================================================== */

/* ---- type scale: one ladder — page title > section title > card title ≥ body > label/note ---- */
.page-kit {
  --fs-title: 1.75rem; --fs-section: 1.3rem; --fs-card: 1.08rem; --fs-body: 1rem; --fs-label: .9rem; --fs-note: .9rem;
  --kit-indent: 28px;
}
/* The report density [USER, 2026-10-21: "at least 3 times bigger"]: body 20 px, labels 17, cells ≥ 19, figures 36 —
   the sizes S10.25 set per page, now set once. The older tokens are raised too, so chart labels follow. */
.page-kit.is-report {
  font-size: 20px;
  --fs-title: 36px; --fs-section: 27px; --fs-card: 22px; --fs-body: 20px; --fs-label: 17px; --fs-note: 17px;
  --text-sm: 17px; --text-base: 20px; --text-lg: 23px; --stat-value: 36px;
}
/* Inside a table cell nothing is under 19 px — a product's Details panel, its buttons, captions and "i" included (S10.25
   T1440): the label and note sizes step up there. */
.page-kit.is-report td, .page-kit.is-report tbody th { --text-sm: 19px; --fs-label: 19px; --fs-note: 19px; }
/* Table text at the report size wherever a table is (blitz.css drops tables to 14 px under 900 px; the kit's own th is
   label-size): body and foot cells, and a row's own heading (a product's name), are body-size — S10.25's floor of
   19 px in a table cell [USER, 2026-10-21] (Codex impl R1). Column headings stay label-size (17 px). */
.page-kit.is-report :is(tbody, tfoot) :is(td, th) { font-size: var(--fs-body); }
.page-kit.is-report :is(label, .button, .btn-sm, select, input, .filter-bar .field > label, caption, .tab,
  .bstack-legend, .bstack-label, .bstack-total, .bstack-note, .bchart-label, .bchart-sub, .bchart-empty, .chart-tip,
  .pie-label, .info-tip-text, .info-tip-btn) { font-size: var(--fs-label); }
.page-kit.is-report .info-tip-btn { width: 1.2em; height: 1.2em; line-height: 1; }
/* A pie's legend at the report size: a part's figure goes under its name, not beside it — beside it, in the legend's
   column next to a 220 px pie, the figure ran over the name ("Material₹1,30,445.37", Profitability, found by
   tests/visual/clipcheck.js). The phone rule in charts.css already does this under 520 px. */
.page-kit.is-report .pie-label { grid-template-columns: 12px minmax(0, 1fr); row-gap: 2px; }
.page-kit.is-report .pie-value { grid-column: 2; }
/* A stacked table cell's column name on a phone (blitz.css draws it at 13 px): the report floor is 16 px. */
.page-kit.is-report td::before { font-size: var(--fs-label); }
@media (max-width: 768px) { .page-kit.is-report td::before { font-size: 16px; white-space: normal; } }
/* ...and at that size a long column name wraps to two lines: drawn absolute it ran over the next cell ("Purchase
   frequency" over "Peak quarter"), so it floats in the same place (42% of the cell, inside the 45% padding) and the
   cell grows to hold it. */
@media (max-width: 768px) {
  .page-kit.is-report table:not(.no-mobile-stack) td:not([data-label=""])::after { content: ""; display: table; clear: both; }
  .page-kit.is-report table:not(.no-mobile-stack) td:not([data-label=""])::before {
    position: static; float: left; box-sizing: border-box; width: 76.36%; margin-left: -81.82%;   /* 42/55, 45/55 */
  }
}

/* ---- the page head on the same row as the page's buttons (a list page's toolbar) ---- */
.page-kit .toolbar > .page-head { margin: 0; }
.page-kit > .toolbar { margin-bottom: 24px; }

/* ---- the page: no box of its own — the sections are the boxes ---- */
.container.page-kit { background: transparent; box-shadow: none; padding: 4px 6px 24px; }
.page-kit .page-head { margin: 4px 0 24px; }
.page-kit .page-title { margin: 0 0 6px; padding: 0; border: 0; font-size: var(--fs-title); font-weight: 700;
                        letter-spacing: -0.02em; line-height: 1.15; }
.page-kit .page-sub { margin: 0; max-width: 75ch; color: var(--text-secondary); font-size: var(--fs-body); line-height: 1.5; }

/* ---- a section: a bordered box; its title has the accent bar; its body sits in from the title ---- */
.page-kit .section {
  margin: 0 0 24px; padding: 20px 24px 24px; min-width: 0;
  background: var(--bg-secondary); border: 1px solid var(--border-color); border-radius: 14px;
  box-shadow: 0 1px 2px var(--shadow);
}
.page-kit .section-head {
  display: flex; align-items: center; flex-wrap: wrap; gap: 8px 14px;
  margin: 0 0 18px; padding: 0 0 14px; border-bottom: 1px solid var(--border-color);
}
.page-kit .section-title {
  display: flex; align-items: center; gap: 10px; margin: 0; padding: 0; border: 0;
  font-size: var(--fs-section); font-weight: 700; line-height: 1.2; color: var(--text-primary);
}
.page-kit .section-title::before {
  content: ""; flex: none; width: 4px; height: 1.1em; border-radius: 2px; background: var(--btn-primary);
}
.page-kit .section-count { color: var(--text-secondary); font-size: var(--fs-note); font-weight: 600; }
.page-kit .section-count:empty { display: none; }
.page-kit .section-actions { margin-left: auto; display: flex; gap: 8px; flex-wrap: wrap; }
/* A section whose head is a tab strip (its views are its bodies): the strip is the rule under the head. */
.page-kit .section-head:has(> .tabs) { padding-bottom: 0; }
.page-kit .section-head > .tabs { flex: 1 1 auto; margin: 0; border-bottom: 0; }
.page-kit .section-head:has(> .tabs) ~ .section-body { padding-left: 0; }
.page-kit .section-body { padding-left: var(--kit-indent); min-width: 0; }
.page-kit .section-body > :first-child { margin-top: 0; }
.page-kit .section-body > :last-child { margin-bottom: 0; }
/* A section with no title (the dates, the cards, the charts) has no indent to keep. */
.page-kit .section > .section-body:first-child { padding-left: 0; }
/* A section that folds: the head is the <summary>, with a turning chevron at its end. */
.page-kit details.section-fold > summary.section-head { cursor: pointer; list-style: none; }
.page-kit details.section-fold > summary.section-head::-webkit-details-marker { display: none; }
.page-kit details.section-fold > summary.section-head::after {
  content: "▾" / ""; margin-left: auto; color: var(--text-muted); transition: transform .2s; transform: rotate(-90deg);
}
.page-kit details.section-fold[open] > summary.section-head::after { transform: none; }
.page-kit details.section-fold:not([open]) > summary.section-head { margin: 0; padding: 0; border-bottom: 0; }
.page-kit details.section-fold > summary.section-head:focus-visible { outline: 2px solid var(--btn-primary); outline-offset: 4px; border-radius: 4px; }
.page-kit details.section-fold > summary.section-head::after { order: 2; }
.page-kit .section-gist { order: 3; flex-basis: 100%; padding-left: 14px; color: var(--text-secondary); font-size: var(--fs-note); }
.page-kit .section-gist:empty { display: none; }
/* "Needs attention": the same section, an amber edge and an amber bar. */
.page-kit .section.is-warn { border-left: 4px solid var(--warn-line); }
.page-kit .section.is-warn .section-title::before { background: var(--warn-line); }

/* ---- inside a section: a sub-heading, and a titled card ---- */
.page-kit .sub-head { margin: 20px 0 10px; font-size: var(--fs-card); font-weight: 700; color: var(--text-primary); }
.page-kit .sub-card {
  min-width: 0; padding: 14px 16px; background: var(--bg-secondary);
  border: 1px solid var(--border-color); border-radius: 12px;
}
.page-kit .sub-card-head {
  display: flex; align-items: baseline; justify-content: space-between; flex-wrap: wrap; gap: 6px 12px;
  margin: 0 0 12px; padding: 0 0 10px; border-bottom: 1px solid var(--border-color);
}
.page-kit .sub-card-head h3 { margin: 0; font-size: var(--fs-card); font-weight: 700; }
.page-kit .sub-card-total { font-family: var(--font-mono, "Geist Mono", ui-monospace, monospace); font-weight: 600;
                            font-size: var(--fs-body); color: var(--text-primary); white-space: nowrap; }

/* ---- the summary cards ---- */
.page-kit .stat-cards { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 14px; margin: 0 0 24px; }
@media (max-width: 1100px) { .page-kit .stat-cards { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
/* exactly four cards: two by two, not three and one left over */
@media (min-width: 1101px) { .page-kit .stat-cards:has(> :nth-child(4):last-child) { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 560px) { .page-kit .stat-cards { grid-template-columns: minmax(0, 1fr); } }
.page-kit .stat-card {
  min-width: 0; padding: 14px 16px; background: var(--bg-tertiary);
  border: 1px solid var(--border-color); border-radius: 12px;
}
.page-kit .stat-label { font-size: var(--fs-label); font-weight: 600; line-height: 1.35; color: var(--text-secondary); }
.page-kit .stat-value { margin-top: 6px; font-family: var(--font-mono, "Geist Mono", ui-monospace, monospace);
                        font-size: var(--stat-value, 1.6rem); font-weight: 600; line-height: 1.2; color: var(--text-primary);
                        overflow-wrap: anywhere; }
/* The sentence under a figure: where a blank says why it is blank — never decoration. */
.page-kit .stat-note { margin: 6px 0 0; font-size: var(--fs-note); line-height: 1.45; color: var(--text-secondary); }
.page-kit .stat-note:empty { display: none; }
.page-kit .stat-per-unit { margin: 4px 0 0; font-size: var(--fs-note); color: var(--text-secondary);
                           font-family: var(--font-mono, "Geist Mono", ui-monospace, monospace); }
/* The one card a page leads with (Product Cost's average): its per-unit figure at the card-figure size. */
.page-kit .stat-card.is-lead .stat-per-unit { margin-top: 6px; font-size: var(--text-lg, 1.15rem); color: var(--text-primary); }
.page-kit .stat-link { display: inline; margin: 0; padding: 0; border: 0; background: none; cursor: pointer; font: inherit;
                       color: var(--text-primary); text-decoration: underline; }
.page-kit .stat-link:focus-visible { outline: 2px solid var(--btn-primary); outline-offset: 2px; }
/* A card that opens a page (the dashboard's): the whole card is the link, so it reads as the card, not as underlined
   text; its label takes the accent on hover. */
.page-kit .stat-card a.stat-link { display: block; color: inherit; text-decoration: none; }
.page-kit .stat-card a.stat-link:is(:hover, :focus-visible) .stat-label { color: var(--btn-primary); }
/* A figure that is not there (the outreach pages' blanks) is a quiet word at body size, not a big grey figure. */
.page-kit .stat-value.blank { color: var(--text-muted); font-size: 1rem; }

/* ---- a chart in a box with its caption ---- */
.page-kit .chart-box { margin: 0; min-width: 0; padding: 14px 16px; background: var(--bg-secondary);
                       border: 1px solid var(--border-color); border-radius: 12px; }
.page-kit .chart-box figcaption { margin: 0 0 8px; font-size: var(--fs-card); font-weight: 700; color: var(--text-primary); }
.page-kit .chart-note { margin: 0 0 10px; font-size: var(--fs-note); line-height: 1.45; color: var(--text-secondary); }

/* ---- label / value pairs ---- */
.page-kit .kv-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 200px), 1fr)); gap: 12px 20px; }
.page-kit .kv-grid > * > :first-child { font-size: var(--fs-label); color: var(--text-secondary); }
.page-kit .kv-grid > * > :last-child { font-family: var(--font-mono, "Geist Mono", ui-monospace, monospace);
                                       font-size: var(--fs-body); color: var(--text-primary); }

/* ---- a note (info / warn / bad), an empty state, a hint ---- */
.page-kit .note { margin: 0 0 16px; padding: 12px 16px; border-radius: 10px; font-size: var(--fs-note); line-height: 1.5;
                  background: var(--tint); border-left: 4px solid var(--border-light); color: var(--text-primary); }
.page-kit .note.is-warn { background: var(--warn-tint); border-left-color: var(--warn-line); }
.page-kit .note.is-bad { background: var(--chip-red-bg); border-left-color: var(--chip-red-line); color: var(--chip-red-fg); }
.page-kit .note:empty { display: none; }
.page-kit .note h3 { margin: 0 0 8px; font-size: var(--fs-card); }
.page-kit .note ul { margin: 0; padding-left: 20px; }
.page-kit .note li { margin: 0 0 6px; }
.page-kit .note li:last-child { margin-bottom: 0; }
.page-kit .empty-state { margin: 16px 0 0; padding: 20px; text-align: center; color: var(--text-secondary);
                         border: 1px dashed var(--border-light); border-radius: 10px; }
.page-kit .hint { margin: 10px 0 0; font-size: var(--fs-note); line-height: 1.5; color: var(--text-secondary); max-width: 90ch; }
/* the line under one form field, and the small print inside a page's own box: smaller and quieter */
.page-kit .field .hint, .page-kit .hint.is-small { margin: 6px 0 0; font-size: .85em; line-height: 1.45; color: var(--text-muted); }
/* a note inside a table cell (Price Prediction's per-machine refusal): tighter, it shares the row */
.page-kit td .note { margin: 4px 0 0; padding: 6px 10px; }

/* ---- tables: a header band, figures right-aligned in columns, a heavy total line, a visible blank ---- */
.page-kit table { margin-top: 0; }
.page-kit th { text-transform: none; letter-spacing: 0; font-weight: 600; font-size: var(--fs-label);
               color: var(--text-secondary); background: var(--bg-tertiary); border-bottom: 1px solid var(--border-light); }
.page-kit tbody tr:hover { background: var(--tint); }
.page-kit :is(th, td).num { text-align: right; white-space: nowrap; font-variant-numeric: tabular-nums; }
.page-kit td.num { font-family: var(--font-mono, "Geist Mono", ui-monospace, monospace); }
.page-kit tfoot :is(th, td), .page-kit tr.total-row > * { font-weight: 700; border-top: 2px solid var(--border-light);
                                                         color: var(--text-primary); }
.page-kit .blank { color: var(--text-muted); }

/* ---- THE OPENED PANEL (BlitzUI.openPanel) ----
   [USER, 2026-10-22: "I should be able to distinguish that inline section from the heading and everything else …
   hard to tell when that section begins and when it ends … no feedback that something is opening or has opened."]
   The row that opened it is marked (raised background + the accent edge); the panel hangs off that row with the same
   edge, a head bar (title + Close), a body, and a foot bar that says where it ends (+ a second Close). */
.page-kit tr.is-opened > * { background: var(--bg-raised); }
.page-kit tr.is-opened > :first-child { box-shadow: inset 4px 0 0 var(--btn-primary); }
.page-kit tr.open-row, .page-kit tr.open-row:hover { background: none; }
.page-kit tr.open-row > td { padding: 0 0 18px; border-bottom: 0; background: none; }
.page-kit .open-panel {
  position: sticky; left: 0; min-width: 0;   /* held at the visible left of a wide table that scrolls */
  background: var(--bg-secondary); border: 1px solid var(--border-light); border-left: 4px solid var(--btn-primary);
  border-radius: 0 0 12px 12px; box-shadow: 0 10px 28px var(--shadow-lg);
}
.page-kit .open-panel-head {
  display: flex; align-items: center; justify-content: space-between; gap: 10px 16px; flex-wrap: wrap;
  padding: 12px 16px; background: var(--bg-raised); border-bottom: 1px solid var(--border-color);
  scroll-margin-top: 72px;   /* clear of the phone's top bar when it is brought into view */
}
.page-kit .open-panel-title { margin: 0; font-size: var(--fs-card); font-weight: 700; color: var(--text-primary); }
.page-kit .open-panel-body { padding: 16px; }
.page-kit .open-panel-lede { margin: 0 0 14px; max-width: 90ch; font-size: var(--fs-note); line-height: 1.5; color: var(--text-secondary); }
.page-kit .open-panel-foot {
  display: flex; align-items: center; justify-content: space-between; gap: 10px 16px; flex-wrap: wrap;
  padding: 10px 16px; background: var(--bg-raised); border-top: 1px solid var(--border-color);
  border-radius: 0 0 11px 8px; color: var(--text-secondary); font-size: var(--fs-note);
}
/* It announces itself: the edge glows and the body comes up, once, for about a second. */
.page-kit .open-panel.is-arriving { animation: panelArrive 1.1s ease-out; }
.page-kit .open-panel.is-arriving .open-panel-body { animation: panelBodyIn .35s ease-out; }
@keyframes panelArrive {
  0% { box-shadow: 0 0 0 4px color-mix(in srgb, var(--btn-primary) 60%, transparent), 0 10px 28px var(--shadow-lg); }
  100% { box-shadow: 0 0 0 0 transparent, 0 10px 28px var(--shadow-lg); }
}
@keyframes panelBodyIn { from { opacity: .55; transform: translateY(-4px); } to { opacity: 1; transform: none; } }
@media (max-width: 768px) {
  /* On a phone a product row is a card; its panel hangs straight off the card's bottom edge. */
  .page-kit tr.is-opened, .page-kit tr:has(+ tr.open-row) { margin-bottom: 0; border-bottom-left-radius: 0; border-bottom-right-radius: 0; }
  .page-kit tr.open-row { margin: 0 0 14px; padding: 0; border: 0; background: none; box-shadow: none; }
  .page-kit tr.open-row > td { padding: 0; }
  .page-kit .open-panel { position: static; width: auto !important; }
  .page-kit .open-panel-body { padding: 12px; }
}

/* ---- loading a report: placeholder shapes where the cards and the table will be ---- */
.page-kit #loading-indicator.active { align-items: stretch; padding: 4px 0 24px; }
.page-kit #loading-indicator .loading-msg { order: -1; margin: 0 0 12px; font-size: var(--fs-note); }
.page-kit .skeleton { display: grid; gap: 14px; width: 100%; }
.page-kit .skeleton-cards { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 14px; margin-bottom: 10px; }
@media (max-width: 560px) { .page-kit .skeleton-cards { grid-template-columns: minmax(0, 1fr); } }
.page-kit .skeleton-card { height: 104px; border-radius: 12px; }
.page-kit .skeleton-row { height: 26px; border-radius: 6px; }
.page-kit .skeleton-row:nth-child(even) { width: 92%; }
.page-kit :is(.skeleton-card, .skeleton-row) {
  background: linear-gradient(90deg, var(--bg-tertiary) 0%, var(--bg-raised) 50%, var(--bg-tertiary) 100%);
  background-size: 200% 100%; animation: skeletonShimmer 1.6s ease-in-out infinite;
}
@keyframes skeletonShimmer { from { background-position: 100% 0; } to { background-position: -100% 0; } }

/* ---- a button whose work is running: a small ring beside its words ---- */
.page-kit .button[aria-busy="true"] { cursor: progress; }
.page-kit .button[aria-busy="true"]::after {
  content: ""; display: inline-block; width: .85em; height: .85em; margin-left: 8px; vertical-align: -1px;
  border: 2px solid currentColor; border-right-color: transparent; border-radius: 50%; animation: ringTurn .9s linear infinite;
}

@media (max-width: 768px) {
  .page-kit .section { padding: 16px 14px 18px; border-radius: 12px; }
  .page-kit .section-body { padding-left: 0; }
  .page-kit.is-report { --fs-title: 30px; --fs-section: 24px; }
}
@media (prefers-reduced-motion: reduce) {
  .page-kit .open-panel.is-arriving, .page-kit .open-panel.is-arriving .open-panel-body,
  .page-kit :is(.skeleton-card, .skeleton-row), .page-kit .button[aria-busy="true"]::after,
  .page-kit details.section-fold > summary.section-head::after { animation: none; transition: none; }
}

/* ---- a row's own button (a product's name that opens it): looks like the name, says it can be pressed ---- */
.page-kit .row-opener { padding: 0; border: 0; background: none; font: inherit; color: inherit; text-align: left;
                        cursor: pointer; text-decoration: underline dotted; text-underline-offset: 3px; }
.page-kit .row-opener:hover { color: var(--btn-primary); }
.page-kit .row-opener:focus-visible { outline: 2px solid var(--btn-primary); outline-offset: 2px; }


/* ============================================================================
   5. MOTION — reveal-on-enter layer driven by motion.js (window.AppMotion)
   ----------------------------------------------------------------------------
   .am-pre is applied by JS ONLY, never in markup, so no-JS / motion-off users
   are never left with invisible content. [data-am-loader] on the spinner is a
   markup hook with no rule (here or in the old app).
   ========================================================================== */

/* Entrance reveal: hidden pre-state (JS-applied) -> animated in-state.
   Uses transform + opacity only; the global [data-theme] * rule transitions
   COLOR props only, so there is no conflict. */
.am-pre {
  opacity: 0;
  transform: translateY(var(--am-shift));
  transition: none;
  will-change: opacity, transform;
}
.am-in {
  opacity: 1;
  transform: none;
  transition: opacity var(--am-dur) var(--am-ease),
              transform var(--am-dur) var(--am-ease);
}
/* Clean up the GPU hint once settled */
.am-in:not(.am-pre) { will-change: auto; }

/* Animated theme-toggle icon nudge (augments toggleTheme()) */
.theme-toggle.am-spin { animation: amThemeSpin 0.5s var(--am-ease); }
@keyframes amThemeSpin {
  0%   { transform: rotate(0deg)   scale(1); }
  50%  { transform: rotate(180deg) scale(1.18); }
  100% { transform: rotate(360deg) scale(1); }
}

/* Sidebar nav entrance (enhanceNav) */
.nav-link.am-nav-pre { opacity: 0; transform: translateX(-8px); }
.nav-link.am-nav-in {
  opacity: 1; transform: none;
  transition: opacity 0.32s var(--am-ease), transform 0.32s var(--am-ease);
}

/* Gentler + faster on phones; entirely off for reduced-motion */
@media (max-width: 768px) {
  :root { --am-shift: 8px; --am-dur: 380ms; }
}
@media (prefers-reduced-motion: reduce) {
  .am-pre, .am-in,
  .nav-link.am-nav-pre, .nav-link.am-nav-in {
    opacity: 1 !important;
    transform: none !important;
    transition: none !important;
    animation: none !important;
  }
  .theme-toggle.am-spin { animation: none !important; }
}

/* ---- "i": how a figure is worked out (BlitzUI.infoTip) ---- */
.info-tip { position: relative; display: inline-block; margin-left: 4px; vertical-align: middle; }
.info-tip-btn {
  width: 16px; height: 16px; padding: 0; border-radius: 50%; border: 1px solid var(--border-color);
  background: var(--bg-secondary); color: var(--text-secondary); font: italic 600 10px/14px Georgia, serif;
  cursor: pointer; text-transform: none; letter-spacing: 0;
}
.info-tip-btn:hover, .info-tip-btn[aria-expanded="true"] { border-color: var(--btn-primary); color: var(--text-primary); }
.info-tip-btn:focus-visible { outline: 2px solid var(--btn-primary); outline-offset: 2px; }
.info-tip-text {
  position: absolute; z-index: 30; top: 22px; left: -8px; width: max-content; max-width: min(300px, calc(100vw - 32px));
  padding: 8px 10px; border-radius: 8px; border: 1px solid var(--border-color); background: var(--bg-secondary);
  box-shadow: 0 6px 18px rgba(0,0,0,.12); color: var(--text-primary); font: 400 12.5px/1.45 Inter, system-ui, sans-serif;
  text-transform: none; letter-spacing: 0; white-space: normal; text-align: left;
}
th.num .info-tip-text { left: auto; right: -8px; }

/* ---- SHARED PAGE PATTERNS (tidied 2026-10-17) — rules several pages had each copied word for word ---- */
.total-value { font-weight: 600; }
.total-row:last-of-type .total-value { font-size: 1.1em; }
.empty-note { color: var(--text-muted); }
.static-card { margin-bottom: 20px; }
.static-card:hover { transform: none; box-shadow: none; }
.badge-neutral { background: var(--bg-tertiary); color: var(--text-secondary); border: 1px solid var(--border-color); }
.filter-bar .field.grow { flex: 1 1 260px; }
.filter-bar .field.grow input { width: 100%; box-sizing: border-box; }
.table-scroll:focus-visible { outline: 2px solid var(--btn-primary); outline-offset: 2px; }
.hint:empty { display: none; }
.stat-note:empty { display: none; }
.notice[hidden] { display: none; }
