/* hub_core/ui_shared/static/app.css
 *
 * Retracted in place (TENANCY-SPEC.md P7, §9 row P7, §6.1 SPEC-DECISION,
 * 2026-09-25) -- this file's own header used to read "frontend_flask/
 * static/app.css". `git mv`'d here so the hub, farm_app and buy_app can all
 * serve the SAME component layer from one blueprint (`hub_core/ui_shared`,
 * `ui_shared_bp`, static_url_path "/ui-static") instead of three copies --
 * see that package's own `__init__.py` and `templates/_head_assets.html`.
 *
 * Redesign P1 (docs/redesign/SPEC.md §3, §6 P1 row) -- the new
 * design-system COMPONENT layer: the rail/tabs shell base.html renders
 * around every existing page, plus the badge/banner/button/table/ledger/
 * drawer/toast/case-tile/meter component classes later phases build
 * screens out of. Loads after tokens.css (every colour here is a
 * `var(--token)`, never a bare hex).
 *
 * Retracted in place (REDESIGN PHASE P11) -- the line above used to end
 * "...and before legacy style.css, which keeps winning any tie until P11
 * folds it in (base.html's own load-order comment)." P11 folded it in:
 * `static/style.css` is deleted, its rules moved verbatim into this
 * file's own trailing "Folded from static/style.css" section (see that
 * section's own header). Every "style.css already owns/has/does..."
 * comment below this point, dating from P1-P10, describes the scoping
 * decisions that were true when style.css was a SEPARATE file being
 * planned around -- those decisions (what's scoped under `.rd`, what's
 * left bare) still hold exactly as described, because the SAME class
 * names, at the SAME cascade position (last in this file), now live in
 * the folded section instead. Read "style.css" in those older comments
 * as "the folded section below" — not re-annotated line by line, since
 * the folded section's own header already gives the complete, current
 * account of what moved and why.
 *
 * RENDER-UNCHANGED RULE (pre-flight, binding for this whole file): nothing
 * here may change how any EXISTING page looks, except the global
 * `:focus-visible` ring, which is intended. style.css already owns dozens
 * of class names this redesign also wants (`.btn`, `.card`, `.num`, `.sub`,
 * `.small`, `.muted`, `.meter`/`.meter-*`, `.k`, `.v`, `.pill`, `.stack`,
 * `.top`, `.topbar`, `.actions`, `.alert`, `.hint`, `.field`, `.x`,
 * `.wrap`) and several bare element selectors (body's own font rules,
 * h1-h3, table/th/td, a, input). Every rule below that would otherwise
 * collide with one of those is scoped under an opt-in `.rd` ancestor class
 * (added to a page's root by the phase that redesigns it, never by this
 * one -- P1 adds `.rd` nowhere) or under one of the shell's own,
 * genuinely-new ancestor classes (`.rail`, `.shell-topbar`, `.nav`,
 * `.tabs`), which never appear in legacy markup. A rule whose selector is
 * a NEW name with no legacy match at all (`.badge`, `.banner`, `table.t`,
 * `.ledger`, `.kv`, `.stat`, `.drawer`, `.toast`, `.case-tile`, `.eyebrow`,
 * `.due`, `.money`, `.scroll-x`, `.shell`, `.rail`, `.main`, `.page`) is
 * left unscoped, per the pre-flight's own list.
 *
 * The one exception needing its own note: `.meter`'s OUTER box-model rule
 * (display/width/height/margin) is unscoped even though `.meter` already
 * exists in style.css (macro_meter.html's SVG), because the value set
 * below is byte-identical to style.css's own -- whichever one wins,
 * nothing on screen changes. Its CHILDREN use the new nested short names
 * (`.meter .track`/`.opening`/`.fill`/`.gap`/`.tick`), which never match
 * legacy's flat `meter-track`/`meter-seg-fill`/`meter-gap`/`meter-tick`
 * classes (a compound class name vs. two separate ones) -- so no
 * collision there either, this system is simply dormant until a later
 * phase's markup actually nests elements under a `.meter` this way.
 */

/* ---------- Global (unscoped, render-neutral) ---------- */

body {
  /* Only these two properties, per the pre-flight rule -- font-family/
     size/line-height stay style.css's, which redeclares body's background
     and color right after this (loads later, same specificity, wins the
     tie) so every existing page keeps its exact legacy look. */
  background: var(--ground);
  color: var(--ink);
}

:focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: 2px;
}

/* REDESIGN PHASE P11 (SPEC §3.11, phase brief D) -- the base.html skip
   link's own visually-hidden-until-focused technique. New name, unscoped
   (checked against style.css and this file's own colliding-name list --
   no `.visually-hidden` anywhere in either). Hidden state matches the
   existing `.case-tile input` visually-hidden pattern (P1) exactly;
   `:focus` (not `:focus-visible`) on purpose -- a skip link should reveal
   on ANY focus, keyboard or programmatic, not just the visible-focus
   heuristic. */
.visually-hidden {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}
.visually-hidden:focus {
  position: fixed;
  top: var(--s-2);
  left: var(--s-2);
  width: auto; height: auto;
  padding: 8px 14px;
  margin: 0;
  overflow: visible;
  clip: auto;
  white-space: normal;
  background: var(--surface);
  color: var(--ink);
  border: 1px solid var(--line-strong);
  border-radius: var(--r-sm);
  box-shadow: var(--shadow);
  z-index: 100;
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    transition: none !important;
    animation: none !important;
  }
}

/* ---------- Shell: rail (desktop) / tabs (phone) ---------- */

.shell {
  display: grid;
  grid-template-columns: var(--rail-w) 1fr;
  min-height: 100%;
}

.rail {
  background: var(--surface);
  color: var(--ink);
  border-right: 1px solid var(--line);
  padding: var(--s-4) var(--s-3);
  display: flex;
  flex-direction: column;
  gap: var(--s-4);
  position: sticky;
  top: env(safe-area-inset-top, 0px);
  height: 100vh;
  font-family: var(--sans);
  font-size: 14px;
  line-height: 1.45;
}

.rail .brand,
.shell-topbar .brand {
  display: flex;
  align-items: center;
  gap: var(--s-2);
}
.rail .brand { padding: 0 var(--s-2); }

.rail .brand .mark,
.shell-topbar .brand .mark {
  width: 28px; height: 28px;
  border-radius: 8px;
  background: var(--accent);
  color: var(--accent-ink);
  display: grid;
  place-items: center;
  font-family: var(--mono);
  font-weight: 500;
  font-size: 13px;
  flex: none;
}
.shell-topbar .brand .mark { width: 26px; height: 26px; border-radius: 7px; font-size: 12px; }

.rail .brand b,
.shell-topbar .brand b {
  font-weight: 600;
  font-size: 14px;
  display: block;
}
.rail .brand span { font-size: 11px; color: var(--muted); }

.nav { display: flex; flex-direction: column; gap: 2px; }
.nav a {
  display: flex;
  align-items: center;
  gap: var(--s-2);
  padding: 8px 10px;
  border-radius: var(--r-sm);
  color: var(--ink-2);
  font-weight: 500;
  font-size: 13px;
  text-decoration: none;
}
.nav a:hover { background: var(--surface-2); text-decoration: none; }
.nav a[aria-current="page"] { background: var(--accent-soft); color: var(--accent-soft-ink); }

/* REDESIGN PHASE P11 (SPEC §3.11, phase brief D) -- retract in place:
   this used to be `.nav .nav-group` (a descendant selector, `.nav-group`
   nested inside `<nav class="nav">`). It's now `.nav`'s own SIBLING (see
   base.html's own retraction comment for why -- the Setup sub-links
   needed their own `<nav>` landmark to carry `aria-current="page"`
   without a second one inside the primary nav). `margin-top` is dropped
   here -- `.rail`'s own flex `gap: var(--s-4)` between direct children
   now supplies the same visual spacing that this margin used to add on
   top of `.nav`'s own 2px inter-item gap. */
.nav-group { padding: 0 10px; }
.nav-group .eyebrow { display: block; margin-bottom: 2px; }
/* Deliberately NOT `.sub` -- style.css's `.sub { color: var(--muted);
   margin-top: 6px; font-size: 13px; }` is an unrelated, unscoped legacy
   class (the page-header sub-title line); reusing its name here would
   collide two unrelated components. */
.nav-sub { display: flex; flex-direction: column; gap: 2px; }
.nav-sub a {
  padding: 5px 10px 5px 18px;
  border-radius: var(--r-sm);
  font-weight: 400;
  font-size: 12.5px;
  color: var(--ink-2);
  text-decoration: none;
}
.nav-sub a:hover { background: var(--surface-2); text-decoration: none; }

.rail .foot {
  margin-top: auto;
  font-size: 12px;
  color: var(--muted);
  padding: 0 var(--s-2);
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.rail .foot b { display: block; color: var(--ink-2); font-weight: 500; }
.rail .foot a { color: var(--accent); text-decoration: none; font-weight: 500; }
.rail .foot a:hover { text-decoration: underline; }

/* SECURITY-SPEC.md SEC-18 (hardening P4) -- the nav "Log out" link is a
   one-button POST form now (base.html), not a bare <a>. `.hub-logout-
   form` collapses the form to inline block flow (it would otherwise add
   its own margin/block gap inside `.rail .foot`'s flex column); `.hub-
   logout` resets every button-chrome default (border/background/padding/
   font) so the <button> reads EXACTLY like the `.rail .foot a` link it
   replaces, active/hover states included. */
.rail .foot .hub-logout-form { margin: 0; }
.rail .foot .hub-logout {
  border: none;
  background: none;
  padding: 0;
  margin: 0;
  font: inherit;
  color: var(--accent);
  text-decoration: none;
  font-weight: 500;
  cursor: pointer;
  text-align: left;
}
.rail .foot .hub-logout:hover { text-decoration: underline; }

/* `.main` keeps `color-scheme: light` regardless of the OS/theme's own
   scheme: `color-scheme: dark` on :root (tokens.css, OS dark mode) is
   inherited by every descendant, including .main's own legacy content.
   Retracted in place (hardening P12, UX-01/UX-07) -- this comment used to
   say the legacy input/select were "already forced to a white
   `background` with no explicit `color`" and would "otherwise pick up
   the UA's dark form-control colours and go unreadable" without this
   rule. That premise is gone: input/select/textarea (below, "Forms") now
   carry an explicit `background: var(--surface)`/`color: var(--ink)`
   that already tracks the theme correctly on their own, with no need for
   a forced light `color-scheme` to make them legible. The rule ITSELF
   still stays -- test_rd1_shell.py::
   test_app_css_main_forces_light_color_scheme pins it -- because it
   still matters for what P12 did NOT retoken: unclassed `<button>`s and
   other bare UA form widgets inside `.main` carry no rule of their own
   at all, and would still pick up the UA's native dark-mode chrome
   (which this codebase has never styled) without it. Deliberately NOT on
   `.rail`/`.tabs`, which must keep the dark scheme. */
.main { min-width: 0; padding: var(--s-5); color-scheme: light; }
.page { max-width: 1180px; margin: 0 auto; display: flex; flex-direction: column; gap: var(--s-4); }

/* The phone top bar carries BOTH `topbar` (legacy, header.html's own
   title+actions row elsewhere on the page -- untouched, still styled by
   style.css) and `shell-topbar` (new). Every rule below that needs to win
   regardless of style.css's own unscoped `.topbar { display:flex; ... }`
   requires BOTH classes (`.topbar.shell-topbar`, specificity 0,2,0), which
   beats a bare `.topbar` (0,1,0) on any shared property no matter which
   stylesheet loads last -- source order alone would NOT be enough here,
   since header.html's own `.topbar` element must keep behaving exactly as
   before and this new element must not inherit its flex layout. */
.topbar.shell-topbar { display: none; }

@media (max-width: 820px) {
  .shell { grid-template-columns: 1fr; }
  .rail { display: none; }

  .topbar.shell-topbar {
    display: block;
    position: sticky;
    top: env(safe-area-inset-top, 0px);
    z-index: 20;
    background: var(--surface);
    color: var(--ink);
    border-bottom: 1px solid var(--line);
    margin-bottom: 0;
  }
  .shell-topbar .brand { padding: 10px 16px 6px; }
  .shell-topbar .tabs {
    display: flex;
    gap: 2px;
    overflow-x: auto;
    padding: 0 12px 8px;
    scrollbar-width: none;
    list-style: none;
    margin: 0;
  }
  .shell-topbar .tabs a {
    flex: none;
    padding: 6px 10px;
    border-radius: 999px;
    font-size: 13px;
    font-weight: 500;
    color: var(--ink-2);
    text-decoration: none;
  }
  .shell-topbar .tabs a[aria-current="page"] { background: var(--accent-soft); color: var(--accent-soft-ink); }

  .main { padding: var(--s-4) 16px var(--s-6); }
}

/* ---------- Type classes (SPEC §3.2) ---------- */

.eyebrow { font-size: 11px; text-transform: uppercase; letter-spacing: .08em; color: var(--muted); font-weight: 500; }
.money { font-family: var(--mono); font-variant-numeric: tabular-nums; }
.due { font-family: var(--mono); font-size: 24px; font-weight: 500; font-variant-numeric: tabular-nums; }

/* `.num` (34 existing legacy uses) and `.small` (shares its name with
   every `.btn.small` in the app) both collide -- scoped under `.rd` (a
   later phase's redesigned page) or the shell, per the file header. */
.rd .num { font-family: var(--mono); font-variant-numeric: tabular-nums; }
.rail .small, .shell-topbar .small, .rd .small { font-size: 12px; }

/* Headings/body type (SPEC §3.2) -- `h1`-`h3` and body copy sizing are
   bare element selectors style.css already owns (its own h1/`.sub`/body
   rules), so these apply only inside `.rd`, never globally. */
.rd { font-family: var(--sans); font-size: 14px; line-height: 1.45; }
.rd h1 { margin: 0; font-size: 22px; font-weight: 600; letter-spacing: -0.01em; text-wrap: balance; }
.rd h2 { margin: 0; font-size: 16px; font-weight: 600; }
.rd h3 { margin: 0; font-size: 13px; font-weight: 600; text-transform: uppercase; letter-spacing: .06em; color: var(--ink-2); }
.rd a { color: var(--accent); text-decoration: none; }
.rd a:hover { text-decoration: underline; }

/* ---------- Badge (SPEC §3.5) -- new name, unscoped ---------- */

.badge {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 1px 7px;
  border-radius: 999px;
  font-size: 11px;
  font-weight: 500;
  line-height: 18px;
  border: 1px solid transparent;
  white-space: nowrap;
}
.badge svg { width: 11px; height: 11px; }
.badge.unpriced { color: var(--warn); background: var(--warn-soft); }
.badge.priced { color: var(--muted); background: var(--surface-2); }
.badge.far { color: var(--far); background: transparent; border-color: var(--far); }
.badge.settled { color: var(--settled); background: var(--settled-soft); }
.badge.voided { color: var(--void); background: var(--void-soft); text-decoration: line-through; }
.badge.late { color: var(--warn); border-color: var(--warn); background: transparent; }
.badge.excluded { color: var(--critical); background: var(--critical-soft); }

/* ---------- Banner (SPEC §3.6) -- new name, unscoped ---------- */

.banner {
  display: flex;
  gap: var(--s-3);
  align-items: flex-start;
  padding: 10px 12px;
  border-radius: var(--r-md);
  border: 1px solid;
  font-size: 13px;
}
.banner .t { font-weight: 600; }
.banner .spacer { flex: 1; }
.banner.info { background: var(--accent-soft); border-color: transparent; color: var(--accent-soft-ink); }
.banner.warn { background: var(--warn-soft); border-color: transparent; color: var(--warn); }
.banner.settled { background: var(--settled-soft); border-color: transparent; color: var(--settled); }
.banner.critical { background: var(--critical-soft); border-color: transparent; color: var(--critical); }
.banner.notice { background: var(--surface); border-color: var(--line-strong); color: var(--ink); }
/* REDESIGN PHASE P8 -- `.banner`'s own `display: flex` above is an AUTHOR
   rule, which beats the user-agent stylesheet's `[hidden] { display:
   none }` regardless of source order or specificity (author origin always
   wins over user-agent origin) -- so a `.banner`-classed element toggled
   via the bare `hidden` attribute (statement_view.html's own inline Void
   confirm, `static/statements.js`) would not actually hide without this.
   Same fix, same reason, as the pre-existing `.drawer[hidden]`/`.toast[
   hidden]` rules below (P1) -- no earlier phase needed it for `.banner`
   specifically, since every other inline banner on this codebase is
   inserted/removed from the DOM outright rather than toggled hidden. */
.banner[hidden] { display: none; }
/* Chrome pass 2026-09-25: the `hidden` attribute must win over every
   class-level `display:` rule (the bulk-set bar `#manifestBulkBar` is a
   `.row-actions` flex row and rendered even with nothing selected, because
   `.row-actions { display: flex }` beat the user-agent `[hidden]` rule).
   The three per-class fixes above stay; this is the general one the
   redesign spec's page contract called for. */
[hidden] { display: none !important; }

/* ---------- Button (SPEC §3.7) -- collides with legacy `.btn`, scoped ---------- */

.rd .btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 7px 12px;
  border: 1px solid var(--line-strong);
  border-radius: var(--r-sm);
  background: var(--surface);
  color: var(--ink);
  font-weight: 500;
  font-family: var(--sans);
  cursor: pointer;
  line-height: 1.2;
  white-space: nowrap;
  text-decoration: none;
}
.rd .btn:hover { background: var(--surface-2); text-decoration: none; }
.rd .btn.primary { background: var(--accent); border-color: var(--accent); color: var(--accent-ink); }
.rd .btn.primary:hover { filter: brightness(1.08); }
.rd .btn.quiet { border-color: transparent; background: transparent; color: var(--accent); }
.rd .btn.danger { color: var(--critical); border-color: var(--critical); }
.rd .btn.sm { padding: 4px 9px; font-size: 12px; }
.rd .btn:disabled { opacity: .45; cursor: not-allowed; }

/* ---------- Table (SPEC §3.8) -- `table.t` is a new selector: it only
   matches a <table class="t">, which no legacy template emits, so it (and
   everything scoped under it) is safe unscoped. ---------- */

table.t { width: 100%; border-collapse: collapse; }
table.t th {
  text-align: left;
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: .06em;
  color: var(--muted);
  font-weight: 500;
  padding: 6px 10px;
  border-bottom: 1px solid var(--line-strong);
  white-space: nowrap;
}
table.t td { padding: 8px 10px; border-bottom: 1px solid var(--line); vertical-align: top; }
table.t tr:last-child td { border-bottom: 0; }

/* BP-03 (hardening P22): the day panel's "Day charge by farm" table lives
   in a ~306px column; the global nowrap th + 10px padding made it 393px so
   the Charge column sat behind a scrollbar. Scoped tightening: headers may
   wrap, padding 6px, the farm name breaks anywhere; money cells keep
   nowrap + tabular-nums (from `.money`). The `.scroll-x` wrapper stays only
   as a safety net for pathological 7-figure amounts. */
.day-panel table.t.day-charge th { white-space: normal; padding: 4px 6px; }
.day-panel table.t.day-charge td { padding: 6px; }
.day-panel table.t.day-charge td:first-child { overflow-wrap: anywhere; }
.day-panel table.t.day-charge td.money { font-size: 12px; }
table.t .r, table.t th.r { text-align: right; }
table.t .money { text-align: right; white-space: nowrap; }
table.t tr.total td { font-weight: 600; border-top: 2px solid var(--line-strong); }
table.t tr.clickable { cursor: pointer; }
table.t tr.clickable:hover td { background: var(--surface-2); }
table.t tr.faint td { opacity: .55; }

.scroll-x { overflow-x: auto; }

/* ---------- Ledger / key-value / stat (SPEC §3.8) -- new names ---------- */

.ledger { display: grid; grid-template-columns: 1fr max-content; font-size: 13px; }
.ledger > div { padding: 6px 0; border-bottom: 1px solid var(--line); }
.ledger .lab { display: flex; align-items: center; gap: 8px; }
.ledger .amt { text-align: right; font-family: var(--mono); font-variant-numeric: tabular-nums; padding-left: 16px; }
.ledger .sub { padding-left: 18px; color: var(--muted); font-size: 12px; border-bottom: 0; padding-top: 2px; padding-bottom: 2px; }
.ledger .sub.amt { color: var(--muted); }
.ledger .total { font-weight: 600; border-top: 2px solid var(--line-strong); border-bottom: 0; padding-top: 8px; font-size: 14px; }
.ledger .band { grid-column: 1 / -1; padding: 8px 0 2px; border-bottom: 0; }
.ledger .band .eyebrow { display: block; }
/* Chrome pass 2026-09-24: `display: contents` on <details> does not make
   its children grid items in Chrome (they are slotted), so every amount
   stacked under its label. A disclosure is one full-width grid row now:
   the head amount sits inside the <summary>, sub pairs in `.subrow`. */
.ledger details { grid-column: 1 / -1; padding: 6px 0; border-bottom: 1px solid var(--line); }
.ledger summary { list-style: none; cursor: pointer; display: flex; align-items: center; gap: 8px; }
.ledger summary .amt { margin-left: auto; padding-left: 16px; }
.ledger .subrow { display: grid; grid-template-columns: 1fr max-content; }
.ledger summary::-webkit-details-marker { display: none; }
.ledger summary::before {
  content: "";
  width: 0; height: 0;
  border: 4px solid transparent;
  border-left: 5px solid var(--muted);
  margin-left: 2px;
  transition: transform .12s;
}
.ledger details[open] summary::before { transform: rotate(90deg); }
.ledger details:not([open]) .subrow { display: none; }

.kv { display: grid; grid-template-columns: max-content 1fr; gap: 4px 14px; font-size: 13px; }
.kv dt { color: var(--muted); }
.kv dd { margin: 0; }

.stat { background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-md); padding: 10px 12px; }
.stat .v { font-family: var(--mono); font-size: 18px; font-weight: 500; font-variant-numeric: tabular-nums; margin-top: 2px; }
.stat .v.small { font-size: 14px; }

/* ---------- Meters (SPEC §3, mockup lines ~215-227) -- see file header for
   why the outer `.meter` box model is safe unscoped, and why the nested
   short names never touch legacy's flat meter-* classes. ---------- */

.meter { display: block; width: 100%; height: 10px; margin: 6px 0 4px; }
.meter .track { fill: var(--meter-track); stroke: var(--line); stroke-width: 1; vector-effect: non-scaling-stroke; }
.meter .opening { fill: var(--meter-opening); }
.meter .fill { fill: var(--meter-fill); }
.meter .gap { stroke: var(--surface); stroke-width: 2; }
.meter .tick { stroke: var(--ink); stroke-width: 2; }
.meter-row { display: grid; grid-template-columns: 1fr; gap: 2px; padding: 10px 0; border-bottom: 1px solid var(--line); }
.meter-row:last-child { border-bottom: 0; }
.meter-row .figs { display: flex; gap: var(--s-4); flex-wrap: wrap; font-size: 12px; color: var(--muted); }
.meter-row .figs b { color: var(--ink); font-weight: 500; font-family: var(--mono); font-variant-numeric: tabular-nums; }
.legend { display: flex; gap: 14px; font-size: 11px; color: var(--muted); flex-wrap: wrap; }
.legend i { display: inline-block; width: 10px; height: 10px; border-radius: 2px; margin-right: 4px; vertical-align: -1px; }

/* REDESIGN PHASE P9 -- `.mt-8` (8px, `--s-2`'s own value). style.css's
   own margin-utility set (`.mt-4`/`.mb-4/6/10/12/16`/`.mt-16`) never
   actually defined an `.mt-8` -- statements.html (P8) already reaches
   for it three times (its own `.seg mt-8`/`.row-actions mt-8`/`.note
   mt-8`), so it has rendered with no top margin at all since that phase;
   this page's own filter card reaches for the identical class on its own
   `.seg`/banner. One small, additive utility closes the gap for both
   pages at once -- new name, no collision. */
.mt-8 { margin-top: 8px; }

/* ---------- Recovery (P9) -- SPEC §4.8/§5.7. Gap colour comes ONLY from
   the Python `gap_sign` (+1/0/-1; recovery_report.html never compares the
   raw `gap` float itself) -- these two classes are its only consumer.
   Checked against style.css's own `.gap-6/8/10/12` spacing utilities
   (bare numeric suffixes only) -- no name collision. ---------- */
.figs .gap-credit { color: var(--credit); }
.figs .gap-critical { color: var(--critical); }

/* Optional, dark-mode-friendly meter fill -- macro_meter.html itself
   stays byte-unchanged (still `meter-track`/`meter-seg-<kind>`/
   `meter-gap`/`meter-tick`, styled by style.css's own legacy rules,
   `--legacy-meter-track`/`--brand`/`--meter-light`/`--border`/`--text`).
   style.css carries no dark-mode block at all, so those legacy tokens
   stay their LIGHT values under `prefers-color-scheme: dark` -- fine on
   style.css's own unconverted pages, but the recovery page has been
   `.rd page` (dark-aware, tokens.css) since P1. Scoped to `.rd
   .meter-row` (this page's own container) so no legacy page's meter
   changes colour. */
.rd .meter-row .meter-track { fill: var(--meter-track); stroke: var(--line); }
.rd .meter-row .meter-seg-fill { fill: var(--meter-fill); }
.rd .meter-row .meter-seg-opening { fill: var(--meter-opening); }
.rd .meter-row .meter-gap { stroke: var(--surface); }
.rd .meter-row .meter-tick { stroke: var(--ink); }

/* ---------- Drawer (SPEC §3.10) -- new name, unscoped ---------- */

.drawer {
  position: fixed;
  inset: 0 0 0 auto;
  width: var(--drawer-w);
  max-width: 100%;
  height: 100%;
  background: var(--surface);
  border-left: 1px solid var(--line);
  box-shadow: var(--shadow);
  z-index: 60;
  overflow-y: auto;
  padding: var(--s-4);
}
.drawer[hidden] { display: none; }

/* REDESIGN PHASE P9 -- `.drawer-head` factors out the "h2 + Close button"
   row every drawer opens with (route_view.html's own five P5/P7 drawers
   inline this same layout via `style="justify-content:space-between;..."`
   -- pre-existing debt flagged in that phase's own residuals, not
   repeated here per this phase's own "no inline style on new markup"
   rule). New name, unscoped -- no collision. */
.drawer-head { display: flex; justify-content: space-between; align-items: center; gap: var(--s-3); margin-bottom: var(--s-3); }

@media (max-width: 700px) {
  .drawer { width: 100%; }
}

/* ---------- Toast (SPEC §3.11) -- new name, unscoped ---------- */

.toast {
  position: fixed;
  bottom: calc(16px + env(safe-area-inset-bottom, 0px));
  left: 50%;
  transform: translateX(-50%);
  background: var(--ink);
  color: var(--ground);
  padding: 8px 14px;
  border-radius: 999px;
  font-size: 13px;
  box-shadow: var(--shadow);
  opacity: 0;
  pointer-events: none;
  transition: opacity .15s;
  z-index: 50;
}
.toast.on { opacity: 1; }
.toast[hidden] { display: none; }

/* ---------- Pictorial case tile (SPEC §3.9) -- visuals only, no JS this
   phase; new name, unscoped. A `:has()` selector marks the tile whose
   radio is checked/focused -- no click handler needed for the look. ---- */

.case-tile {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  width: 84px;
  padding: var(--s-2);
  border: 1px solid var(--line-strong);
  border-radius: var(--r-md);
  cursor: pointer;
  text-align: center;
  background: var(--surface);
}
.case-tile input {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}
.case-tile:has(input:checked) {
  border-color: var(--accent);
  background: var(--accent-soft);
  color: var(--accent-soft-ink);
}
.case-tile:has(input:focus-visible) {
  outline: 2px solid var(--focus);
  outline-offset: 2px;
}
.case-tile svg { width: 100%; height: 40px; }
.case-tile .name { font-weight: 500; font-size: 12px; }
.case-tile .cu-ft { font-size: 11px; color: var(--muted); }

/* ---------- Calendar (SPEC §3, §4.1, §4.2; REDESIGN PHASE P3) -- ported
   from docs/mockups/app-redesign.html lines ~193, 230-290, not invented.
   `.note`/`.stat-band`/`.cal-*`/`.day`/`.rcard`/`.dots`/`.dot`/`.day-panel`/
   `.ritem`/`.dow`/`.dn`/`.lock`/`.seg` are new names with no legacy match
   (same "left unscoped" precedent as .badge/.banner/table.t above). The
   mock's own bare `.top`/`.foot`/`.cards`/`.actions`/`.row`/`.stack`
   selectors DO collide with style.css (`.top`, line 67) or with this
   file's own colliding-name list (`.actions`, `.stack`) -- every one of
   those is nested under `.cal`/`.day-panel`/`.cal-head` below, never left
   bare, per this file's own scoping convention. */

.note { font-size: 12px; color: var(--muted); }

.stat-band { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: var(--s-3); }

.cal-head { display: flex; align-items: center; justify-content: space-between; gap: var(--s-3); flex-wrap: wrap; }
.cal-head .title { display: flex; align-items: center; gap: var(--s-2); }
.cal-head h1 { min-width: 190px; }
.cal-head .actions { display: flex; gap: var(--s-3); flex-wrap: wrap; align-items: center; }
.cal-head .voided-toggle { font-size: 13px; color: var(--ink-2); text-decoration: none; }
.cal-head .voided-toggle:hover { text-decoration: underline; }

.seg { display: inline-flex; border: 1px solid var(--line-strong); border-radius: var(--r-sm); overflow: hidden; }
.seg a { display: inline-block; border: 0; background: var(--surface); padding: 6px 12px; font-weight: 500; color: var(--ink-2); text-decoration: none; }
.seg a:hover { text-decoration: none; background: var(--surface-2); }
.seg a + a { border-left: 1px solid var(--line-strong); }
.seg a[aria-current="true"] { background: var(--accent-soft); color: var(--accent-soft-ink); }

.cal-layout { display: grid; grid-template-columns: 1fr; gap: var(--s-4); }
/* minmax(0, 1fr), not bare 1fr: a bare 1fr track is minmax(auto, 1fr), so a
   route card's nowrap name sets the track's MIN width and the seven day
   columns overflowed the card (Sat clipped, page scrolled sideways at
   1920 px -- Chrome pass 2026-09-24). Same fix on .dow and .grid below. */
.cal-layout.with-panel { grid-template-columns: minmax(0, 1fr) 340px; }
@media (max-width: 1000px) { .cal-layout.with-panel { grid-template-columns: 1fr; } }

.cal { background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-lg); overflow: hidden; }
.cal .dow { display: grid; grid-template-columns: repeat(7, minmax(0, 1fr)); border-bottom: 1px solid var(--line-strong); }
.cal .dow div { padding: 6px 8px; font-size: 11px; text-transform: uppercase; letter-spacing: .06em; color: var(--muted); font-weight: 500; }
.cal .grid { display: grid; grid-template-columns: repeat(7, minmax(0, 1fr)); }
.cal .grid.week { grid-auto-rows: minmax(220px, auto); }

.day { min-width: 0; text-align: left; background: var(--surface); border: 0; border-right: 1px solid var(--line); border-bottom: 1px solid var(--line); padding: 6px 7px 8px; min-height: 108px; display: flex; flex-direction: column; gap: 4px; position: relative; }
.day:nth-child(7n) { border-right: 0; }
.day.other { color: var(--muted); background: color-mix(in srgb, var(--surface-2) 50%, var(--surface)); }
.day.today .dn { background: var(--accent); color: var(--accent-ink); }
.day.selected { box-shadow: inset 0 0 0 2px var(--accent); }
.day.settled { background: color-mix(in srgb, var(--settled-soft) 45%, var(--surface)); }
.day .day-btn { display: flex; flex-direction: column; gap: 4px; flex: 1; min-width: 0; text-align: left; color: inherit; text-decoration: none; }
.day .day-btn:hover { text-decoration: none; }
.day .top { display: flex; align-items: center; justify-content: space-between; gap: 4px; }
.day .dn { font-family: var(--mono); font-size: 12px; font-weight: 500; width: 22px; height: 22px; border-radius: 50%; display: grid; place-items: center; }
.day .lock { color: var(--settled); display: inline-flex; align-items: center; gap: 3px; font-size: 11px; font-weight: 500; }
.day .cards { display: flex; flex-direction: column; gap: 3px; }
.rcard { font-size: 11.5px; line-height: 1.25; padding: 3px 6px; border-radius: 5px; background: var(--surface-2); border-left: 3px solid var(--accent); display: flex; justify-content: space-between; gap: 6px; align-items: baseline; }
.rcard.unpriced { border-left-color: var(--warn); background: var(--warn-soft); }
.rcard.voided { border-left-color: var(--void); opacity: .55; text-decoration: line-through; }
.rcard .nm { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }
.rcard .amt { font-family: var(--mono); font-variant-numeric: tabular-nums; flex: none; }
.rcard .amt.warn { color: var(--warn); font-family: var(--sans); font-size: 10.5px; }
.cal .grid.week .rcard { font-size: 12.5px; padding: 6px 8px; flex-direction: column; gap: 2px; }
.cal .grid.week .rcard .meta { display: block; color: var(--muted); font-size: 11px; white-space: normal; }
.day .foot { margin-top: auto; font-size: 11px; color: var(--muted); display: flex; flex-direction: column; gap: 1px; }
.day .foot b { font-family: var(--mono); font-weight: 500; color: var(--ink); font-variant-numeric: tabular-nums; }
.day .foot.warn, .day .foot.warn b { color: var(--warn); }

@media (max-width: 700px) {
  .cal .grid.month .day { min-height: 72px; padding: 5px; }
  .cal .grid.month .rcard { display: none; }
  .cal .grid.month .dots { display: flex; gap: 3px; flex-wrap: wrap; }
  .cal .grid.month .foot b { font-size: 11px; }
  .cal .grid.week, .cal .dow.week { display: flex; flex-direction: column; }
  .cal .grid.week .day { border-right: 0; min-height: 0; }
  .cal .dow.week { display: none; }
}
.dots { display: none; }
.dot { width: 7px; height: 7px; border-radius: 50%; background: var(--accent); display: inline-block; }
.dot.unpriced { background: var(--warn); }
.dot.voided { background: var(--void); }

.day-panel { background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-lg); padding: var(--s-4); position: sticky; top: calc(env(safe-area-inset-top, 0px) + 16px); align-self: start; display: flex; flex-direction: column; gap: var(--s-3); }
.day-panel .ph { display: flex; justify-content: space-between; align-items: flex-start; gap: var(--s-2); }
.day-panel .rlist { display: flex; flex-direction: column; gap: 6px; }
.day-panel .ritem { display: grid; grid-template-columns: 1fr max-content; gap: 2px 10px; padding: 8px 10px; border: 1px solid var(--line); border-radius: var(--r-md); background: var(--surface); text-align: left; font: inherit; color: inherit; text-decoration: none; }
.day-panel .ritem:hover { background: var(--surface-2); text-decoration: none; }
.day-panel .ritem .m { color: var(--muted); font-size: 12px; grid-column: 1 / -1; }
.day-panel .ritem .amt { font-family: var(--mono); font-variant-numeric: tabular-nums; }
.day-panel .row, .day-panel .actions-row { display: flex; gap: var(--s-3); flex-wrap: wrap; align-items: center; }
@media (max-width: 700px) {
  .cal-layout.with-panel .day-panel { position: static; }
}

/* ---------- Print (SPEC §3.12) ----------
   Retracted in place (REDESIGN PHASE P11) -- this used to say "style.css
   already has its own `@media print` block (body white, `.card` hairline
   + break-inside:avoid, `.no-print` hidden, meter-* print-color-adjust)
   that this doesn't need to repeat -- it predates the shell and so never
   accounted for it." That was true only while style.css was a separate
   file; P11 deleted it and merged its print block into this rule. Retracted
   AGAIN in place (REDESIGN PHASE P11 fix, iteration 1, gate-confirmed
   must-fix) -- this used to go on to say the merged rule sits "here" i.e.
   immediately below; it no longer does. Placing it here, BEFORE the folded
   legacy section, let that section's own same-specificity `body`/`.card`
   screen rules (later in source order) win the cascade in print too -- an
   off-white printed background and a card drop shadow on every printed
   page. The merged `@media print` rule now sits at the END of this file
   (after the folded section's own last rule) so it wins those ties again,
   the same way style.css's own trailing `@media print` used to when it
   loaded after this file. What the rule adds (P1, still true) is: hide the
   rail/phone topbar/toast/drawer (none of which style.css ever heard of),
   remove the now-empty rail grid column so nothing prints a blank left
   gutter, zero .main's screen padding, print-color-adjust for the NEW
   nested meter classes, and a `.print-head` running-header class (used by
   statement_view.html since Phase 8); folded into it is style.css's own
   print content (body white, `.card` hairline + break-inside:avoid,
   `.audit-block` page-break-inside, no printed link URLs, legacy meter
   print-color-adjust). See "Folded from static/style.css" below for the
   fold, and the end of this file for the rule itself. */

.print-head { display: none; }

/* ---------- Route page (SPEC §4.4, §4.5a; REDESIGN PHASE P5) -- new
   names, unscoped (no legacy match -- checked against style.css above).
   `.split`/`.split.wide` mirrors the mockup's own two-column route-page
   layout; `.card-head` is the "h3 + a trailing action/segmented control"
   row every region card uses (ledger, farm table, far-stop card, trip,
   resources, manifest). Far-stop form rows reuse existing, unscoped
   legacy classes (`.grid2`, `.far-alloc-unsaved`, `table.t`) -- nothing
   new needed there. ---------- */

.split { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--s-4); align-items: start; }
/* REDESIGN PHASE P11 (SPEC §3.13, phase brief E) -- P5's own residual:
   "`.split.wide` grid min-width overflow." A bare `1.6fr 1fr` track's
   implicit min size is `auto` (its content's own min-content width), so
   a wide `.ledger`/`table.t` inside either column could force the WHOLE
   grid past its container rather than wrap/scroll internally -- exactly
   the "nothing scrolls sideways" rule SPEC §3.13 requires. `minmax(0,
   ...)` lets each track shrink to zero before its content does (every
   card's own `.scroll-x`/`table.t` wrapping still handles the content
   itself); the fr proportions (1.6 : 1) are unchanged. */
.split.wide { grid-template-columns: minmax(0, 1.6fr) minmax(0, 1fr); }
@media (max-width: 900px) {
  .split.wide { grid-template-columns: minmax(0, 1fr); }
}

/* REDESIGN PHASE P10 (SPEC §4.9, phase brief B) -- `.setup-split` puts a
   drawer's form next to its own History table at >= 1000px (Trucks, Hub
   settings) and stacks them below that -- a new, unscoped name (checked
   against style.css's own colliding-name list above; no `.setup-split`
   there). Deliberately its own breakpoint (1000px, SPEC §3.13's own list),
   not `.split.wide`'s 900px -- a drawer's own width is narrower than the
   route page's two-column body this class was built for. */
.setup-split { display: grid; grid-template-columns: 1fr; gap: var(--s-4); align-items: start; }
@media (min-width: 1000px) {
  .setup-split { grid-template-columns: 1fr 380px; }
}

.card-head { display: flex; justify-content: space-between; align-items: center; gap: var(--s-3); margin-bottom: var(--s-3); flex-wrap: wrap; }
.card-head h3 { margin: 0; }

/* REDESIGN PHASE P9 (SPEC §4.10) -- a `.card` with less padding, for a
   page (hub stock) whose cards each hold nothing but a `.card-head` and
   a flush `.scroll-x` table -- style.css's own `.card` (16px) leaves a
   visible gap above/beside the table `.scroll-x` doesn't need. Scoped to
   `.rd .card.tight` (compound, specificity 0,0,2,0) so it always beats
   the legacy `.card` rule (0,0,1,0) regardless of stylesheet order,
   same precedent as `.card.internal-card` above -- and never touches a
   legacy (non-`.rd`) page's own `.card`. */
.rd .card.tight { padding: var(--s-3); }

/* ---------- Manifest grid (SPEC §4.5, §3.9, §3.13; REDESIGN PHASE P7) --
   every selector below is scoped under `[data-region="manifest"]` or
   `.grid`/`.case-tile-grid`/`.hub-row`/`.quick-create-inline`/`.field-
   error` (new names, checked against style.css and this file's own
   colliding-name list above -- none collide). `.case-tile` itself is P1's
   own unscoped rule; only the size-rank scaling and the manual-cu-ft input
   nested inside it are added here. ---------- */

[data-region="manifest"] table.t.grid { table-layout: auto; }
[data-region="manifest"] table.t.grid th, [data-region="manifest"] table.t.grid td { vertical-align: middle; }
[data-region="manifest"] .cell-handle { width: 28px; }
[data-region="manifest"] .cell-remove { width: 32px; }
[data-region="manifest"] .cell-qty { width: 80px; }
[data-region="manifest"] .cell-case { min-width: 160px; }
[data-region="manifest"] .cell-product, [data-region="manifest"] .cell-farm, [data-region="manifest"] .cell-dest { min-width: 150px; position: relative; }
/* hardening P23, BP-18(b) -- the product cell grows so "Heirloom Tomatoes
   (T..." is not truncated when the column has room; the narrow-viewport
   `min-width: 0` override further down still wins at its breakpoint. */
[data-region="manifest"] .cell-product { min-width: 220px; }

[data-region="manifest"] .grid-input {
  width: 100%;
  border: 1px solid transparent;
  border-radius: var(--r-sm);
  padding: 4px 6px;
  font: inherit;
  background: transparent;
  color: inherit;
}
[data-region="manifest"] .grid-input:hover { border-color: var(--line); }
[data-region="manifest"] .grid-input:focus { border-color: var(--focus); background: var(--surface); outline: none; }
[data-region="manifest"] .grid-input.num { text-align: right; font-family: var(--mono); font-variant-numeric: tabular-nums; min-width: 4.5em; }
/* Chrome pass 2026-09-24: the Cases column collapsed to ~37px under the
   nine-column table, clipping "10.0" to "10." -- give the qty cell a floor. */
[data-region="manifest"] td.cell-qty { min-width: 5.5em; }
[data-region="manifest"] .grid-locked-value { display: inline-block; padding: 4px 6px; color: var(--ink-2); }
/* TENANCY-SPEC.md P6 (§9 row P6, §5.2) -- a declaration-backed destination
   cell (server-locked, or a fresh hub-stock draw seeded from a declared
   drop) -- same greyed treatment as `.from-hub`'s product/farm cells,
   existing tokens only. */
[data-region="manifest"] .cell-dest.locked { background: var(--surface-2); border-radius: var(--r-sm); }
[data-region="manifest"] .cell-dest.locked .grid-input[readonly] { color: var(--ink-2); cursor: default; }
[data-region="manifest"] tr.from-hub .cell-product, [data-region="manifest"] tr.from-hub .cell-farm { background: var(--surface-2); border-radius: var(--r-sm); }
[data-region="manifest"] tr.changed td { background: var(--accent-soft); }
[data-region="manifest"] tr.new td { background: var(--settled-soft); }
[data-region="manifest"] tr.err-product .cell-product,
[data-region="manifest"] tr.err-quantity .cell-qty,
[data-region="manifest"] tr.err-case .cell-case { outline: 1px solid var(--critical); outline-offset: -1px; border-radius: var(--r-sm); }

[data-region="manifest"] .cell-note { margin-top: 2px; }
[data-region="manifest"] .cell-note.note-red { color: var(--critical); }
[data-region="manifest"] .cell-note.note-amber { color: var(--warn); }
[data-region="manifest"] .cell-note.note-grey { color: var(--muted); }
[data-region="manifest"] .cell-note.note-mark { color: var(--muted); font-style: italic; }

/* Combobox list (static/combobox.js attaches role/aria only -- this file
   owns every pixel of it). Positioned relative to its own cell. */
[data-region="manifest"] .cbx-list {
  position: absolute;
  z-index: 30;
  top: 100%;
  left: 0;
  min-width: 220px;
  max-height: 240px;
  overflow-y: auto;
  margin: 2px 0 0;
  padding: 4px;
  list-style: none;
  background: var(--surface);
  border: 1px solid var(--line-strong);
  border-radius: var(--r-md);
  box-shadow: var(--shadow);
}
[data-region="manifest"] .cbx-option { padding: 6px 8px; border-radius: var(--r-sm); cursor: pointer; }
[data-region="manifest"] .cbx-option:hover, [data-region="manifest"] .cbx-option.active { background: var(--accent-soft); color: var(--accent-soft-ink); }
[data-region="manifest"] .cbx-option.cbx-create { border-top: 1px solid var(--line); margin-top: 4px; padding-top: 8px; font-weight: 500; }

/* Case-size cell: a button trigger + popover fieldset of §3.9 tiles. */
[data-region="manifest"] .case-cell { position: relative; }
[data-region="manifest"] .grid-case-btn {
  width: 100%;
  text-align: left;
  border: 1px solid transparent;
  border-radius: var(--r-sm);
  padding: 4px 6px;
  background: transparent;
  color: inherit;
  font: inherit;
  cursor: pointer;
}
[data-region="manifest"] .grid-case-btn:hover { border-color: var(--line); }
[data-region="manifest"] .grid-case-btn:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }
[data-region="manifest"] .case-popover {
  position: absolute;
  z-index: 30;
  top: 100%;
  left: 0;
  margin-top: 2px;
  /* hardening P23, BP-18(d) -- an explicit width so the tiles flow into
     rows instead of one 160px column. */
  width: max-content;
  max-width: min(340px, 90vw);
  padding: var(--s-3);
  background: var(--surface);
  border: 1px solid var(--line-strong);
  border-radius: var(--r-md);
  box-shadow: var(--shadow);
}
.case-tile-grid { display: flex; flex-wrap: wrap; gap: var(--s-2); border: 0; margin: 0; padding: 0; max-width: 320px; }
.case-tile-grid legend { padding: 0 0 4px; }
/* §3.9 size-rank scaling -- CSS only, no JS size arithmetic. Quintile 1
   (smallest) through 5 (largest); a single-value tenant ranks 3 (P1's own
   `.case-tile` box model is unchanged, only the glyph grows/shrinks). */
.case-tile[data-rank="1"] .crate-glyph { width: 22px; height: 22px; }
.case-tile[data-rank="2"] .crate-glyph { width: 28px; height: 28px; }
.case-tile[data-rank="3"] .crate-glyph { width: 34px; height: 34px; }
.case-tile[data-rank="4"] .crate-glyph { width: 40px; height: 40px; }
.case-tile[data-rank="5"] .crate-glyph { width: 46px; height: 46px; }
.case-tile .cu-ft input.case-manual-input {
  width: 64px;
  margin-top: 2px;
  padding: 2px 4px;
  border: 1px solid var(--line-strong);
  border-radius: var(--r-sm);
  font: inherit;
  text-align: center;
}

/* Desktop layout (§4.5 "the hub aside ... sits beside the grid on
   desktop"): `.grid-body` is the [data-region=manifest] .grid-main +
   #manifestHubPanel pair's own container -- two columns, the aside a
   fixed 280px, the grid column `minmax(0, ...)` so the table can shrink
   below its content width instead of forcing the row sideways (the
   phone breakpoint below relies on that same minmax, not a horizontal
   scroll fallback). Below 900px (§4.5 "folds above the grid below
   900px") it becomes a single column with the aside reordered first. */
/* Retracted in place (Chrome pass 2026-09-24): the two-column rule above
   was keyed to the VIEWPORT (900px) but the manifest card never gets
   wider than ~716px at ANY viewport -- `.page` is capped at 1180px and
   the route page splits that with the trip/resources column -- so the
   280px aside always overlapped the nine-column grid (cells hidden under
   the hub panel on a 1920px screen). The fold is keyed to the card's own
   width now (container query): two columns only when the card itself is
   >= 1000px, otherwise one column with the hub panel BELOW the grid (entry
   first; §4.5 said "above" for the phone fold, and the aside is still
   reachable one scroll down). `.grid-main` deliberately has NO overflow
   rule: an overflow-x:auto there also clips vertically, trapping the
   case-size popover inside a scroll box (seen in the same pass). */
/* Retracted in place (BP-02, hardening P22): two sentences above are stale.
   "the manifest card never gets wider than ~716px" -- the manifest moved
   full-width, so the card is now the whole 1180px `.page` cap; and "two
   columns only when the card itself is >= 1000px" -- the nine-column table
   needs ~1035px, plus the 280px aside and the gap (~1330px), so at 1000px
   the grid ran ~180px under the panel (the x remove buttons were
   unclickable). The threshold is now 1340px. Consequence: with the card
   capped at 1180px the route page ALWAYS stacks the hub panel BELOW the
   grid; the two-column rule only engages if the card is ever allowed past
   1340px. The still-valid why stays: key on the card's own width, never
   the viewport, and no overflow on `.grid-main`. */
[data-region="manifest"] { container-type: inline-size; }
[data-region="manifest"] .grid-body {
  display: flex;
  flex-direction: column;
  gap: var(--s-3);
  align-items: stretch;
}
[data-region="manifest"] .grid-main { min-width: 0; }
@container (min-width: 1340px) {
  [data-region="manifest"] .grid-body {
    display: grid;
    grid-template-columns: minmax(0, 1fr) 280px;
    align-items: start;
  }
}

/* Bulk-set bar, stops strip, hub panel, save bar. */
[data-region="manifest"] #manifestBulkBar { align-items: center; margin-top: var(--s-2); }
[data-region="manifest"] #manifestStopsStrip { margin-top: var(--s-3); align-items: center; }
/* hardening P23, BP-19(7) -- moved out of an inline style= on the Expected section. */
#expectedTodayRows { margin-top: var(--s-3); }
[data-region="manifest"] .hub-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-3);
  padding: var(--s-2) 0;
  border-bottom: 1px solid var(--line);
}
[data-region="manifest"] .hub-row:last-child { border-bottom: 0; }
[data-region="manifest"] .hub-row-head { font-weight: 500; }
#manifestHubPanel { margin-top: var(--s-3); }
#manifestSaveBar {
  position: sticky;
  bottom: 0;
  z-index: 10;
  align-items: center;
  margin-top: var(--s-3);
  padding: var(--s-3);
  background: var(--surface);
  border-top: 1px solid var(--line);
}

.quick-create-inline {
  margin-top: 6px;
  padding: var(--s-2);
  border: 1px solid var(--line-strong);
  border-radius: var(--r-md);
  background: var(--surface-2);
}
.field-error { color: var(--critical); margin: 0; }

/* Phone (§3.13, §4.5 "Phone (< 720px)") -- rows stack: product/qty, then
   case/farm, then destination; the hub panel folds above the grid; the
   save bar stays sticky. Nothing scrolls sideways at 400px -- the grid
   never sits inside `.scroll-x` (a combobox/popover dropdown would be
   clipped by that ancestor's own overflow), so this is a pure reflow, not
   a horizontal scroll fallback. */
@media (max-width: 720px) {
  [data-region="manifest"] table.t.grid thead { display: none; }
  [data-region="manifest"] table.t.grid, [data-region="manifest"] table.t.grid tbody { display: block; width: 100%; }
  [data-region="manifest"] table.t.grid tr {
    display: grid;
    grid-template-columns: auto 1fr 1fr;
    grid-template-areas:
      "handle product qty"
      ".      case    farm"
      ".      dest    dest"
      ".      alloc   remove";
    gap: 4px 8px;
    padding: var(--s-2) 0;
    border-bottom: 1px solid var(--line);
  }
  [data-region="manifest"] table.t.grid td { border-bottom: 0; padding: 2px 0; }
  [data-region="manifest"] .cell-handle { grid-area: handle; }
  /* REDESIGN PHASE P11 (SPEC §3.13, phase brief E) -- `min-width: 0`
     added: this selector reset every OTHER sibling's desktop `min-width`
     (`.cell-case`/`.cell-farm`/`.cell-dest`, two lines down) but not this
     one's own desktop `min-width: 150px` (the shared `.cell-product,
     .cell-farm, .cell-dest { min-width: 150px; ... }` rule, above) --
     left un-reset, that 150px floor on a grid item sharing the `1fr`
     "product" track with "qty" could force the stacked row wider than a
     400px viewport, the exact overflow class this pass exists to catch. */
  [data-region="manifest"] .cell-product { grid-area: product; min-width: 0; }
  [data-region="manifest"] .cell-qty { grid-area: qty; width: auto; }
  [data-region="manifest"] .cell-case { grid-area: case; min-width: 0; }
  [data-region="manifest"] .cell-farm { grid-area: farm; min-width: 0; }
  [data-region="manifest"] .cell-dest { grid-area: dest; min-width: 0; }
  [data-region="manifest"] .cell-alloc { grid-area: alloc; text-align: left; }
  [data-region="manifest"] .cell-remove { grid-area: remove; justify-self: end; }
  [data-region="manifest"] .cell-weight[data-weight-col] { grid-column: 1 / -1; }
  /* Retract in place: this used to also carry `#manifestHubPanel {
     order: -1; }` here, but `#manifestHubPanel`'s parent (`.grid-body`)
     was never a flex/grid container at this breakpoint, so `order` had
     no effect and the aside always rendered below the grid regardless of
     viewport width. The real fold now lives on `.grid-body` itself in
     the >=720px-reaching `@media (max-width: 900px)` block above, which
     already covers every width this query also matches. */
  #manifestSaveBar { flex-wrap: wrap; }
}

/* ---------- Statements (SPEC §4.6, §4.7, §3.5-3.8, §3.12; REDESIGN
   PHASE P8) -- `.preflight`/`.preflight .hd`/`.bd`/`.dates`, `.preflight.
   ready .hd`/`.preflight.blocked .hd`, `.datechip`(.settle/.locked),
   `.stmt-paper .hdr`/`.due` are ported from docs/mockups/app-redesign.html
   lines 301-313, unchanged. `.preflight.notice .hd` is NEW -- the mock
   only ever had two pre-flight states (ready/blocked); SPEC §4.6 has six,
   and "notice" (nothing to bill / future period / regenerate) gets the
   SAME surface-plus-hairline treatment `.banner.notice` already uses,
   never a colour standing in for "nothing wrong, just FYI". `.seg button`
   is a NEW sibling to the calendar's own `.seg a` rule (P3) -- the
   statement-view toggle (Farm copy | Full breakdown) is a two-button
   form, not two links, and `.seg` itself doesn't care which child it
   styles. `.card.internal-card` (compound, two classes) is the ONE
   selector here that needs extra specificity, not just source order: the
   dashed-border cards (`statement_view.html`'s "Internal detail"/"Day
   composition") also carry the legacy `.card` class for its padding/
   radius, and style.css's own `.card` border loads AFTER this file (the
   file header's own load-order rule), so a bare `.internal-card { border:
   ... }` would lose that tie -- `.card.internal-card` (specificity 0,0,2,0)
   beats `.card` alone (0,0,1,0) regardless of load order.

   No new print rule is needed here: the existing `.no-print`/`.print-
   head` machinery (this file's own Print section, above) already gives
   this phase everything §3.12 asks for -- the normal view's page head/
   void-confirm/reissue banner are `.no-print` in the template, so only
   the paper plus the (unmarked) voided/superseded banners print; the
   audit view's internal-only cards keep the SAME `.no-print` treatment
   they always had, just moved inside the audit-only block (see
   statement_view.html's own retraction comment for why).

   Checked against style.css and this file's own colliding-name list:
   `.hd`/`.bd`/`.dates`/`.hdr`/`.due`/`.ready`/`.blocked`/`.notice` have no
   bare-selector match anywhere in either stylesheet -- every one is left
   nested under `.preflight`/`.stmt-paper` regardless, per this file's own
   scoping convention (never bare, even when nothing collides). ---------- */

.preflight { border: 1px solid var(--line-strong); border-radius: var(--r-md); overflow: hidden; }
.preflight .hd { padding: 10px 14px; display: flex; align-items: center; gap: 8px; font-weight: 600; }
.preflight.ready .hd { background: var(--settled-soft); color: var(--settled); }
.preflight.blocked .hd { background: var(--warn-soft); color: var(--warn); }
.preflight.notice .hd { background: var(--surface); color: var(--ink); border-bottom: 1px solid var(--line-strong); }
.preflight .bd { padding: 10px 14px; display: flex; flex-direction: column; gap: 6px; font-size: 13px; }
.preflight .dates { display: flex; gap: 6px; flex-wrap: wrap; }

.datechip { font-family: var(--mono); font-size: 12px; padding: 2px 8px; border-radius: 999px; background: var(--surface-2); border: 1px solid var(--line); }
.datechip.settle { background: var(--settled-soft); color: var(--settled); border-color: transparent; }
.datechip.locked { background: var(--void-soft); color: var(--void); border-color: transparent; }

.stmt-paper { max-width: 760px; }
.stmt-paper .hdr { display: flex; justify-content: space-between; gap: var(--s-4); flex-wrap: wrap; padding-bottom: var(--s-3); border-bottom: 2px solid var(--ink); margin-bottom: var(--s-3); }
.stmt-paper .hdr h1 { font-size: 20px; }
.stmt-paper .due { font-family: var(--mono); font-size: 24px; font-weight: 500; }

.seg button { display: inline-block; border: 0; background: var(--surface); padding: 6px 12px; font-weight: 500; color: var(--ink-2); font: inherit; cursor: pointer; }
.seg button:hover { background: var(--surface-2); }
.seg button + button { border-left: 1px solid var(--line-strong); }
.seg button[aria-pressed="true"] { background: var(--accent-soft); color: var(--accent-soft-ink); }

.card.internal-card { border: 2px dashed var(--line-strong); background: var(--surface-2); }

/* ==========================================================================
 * Folded from static/style.css in REDESIGN P11 (docs/redesign/SPEC.md §6
 * P11 row, phase brief A) -- this block sits after every other rule in the
 * file so it keeps winning the same ties style.css won when it loaded
 * after app.css (base.html's own former load-order comment, now itself
 * retracted -- see that file). Retracted in place (REDESIGN PHASE P11 fix,
 * iteration 1, gate-confirmed must-fix): this used to say the block "sits
 * LAST" outright, and that its own `@media print` block was merged UP into
 * this file's EXISTING `@media print` rule "(above, "Print (SPEC §3.12)")".
 * Both were true only until the fix: the merged `@media print` rule sat
 * BEFORE this folded section, so this section's own same-specificity
 * `body`/`.card` screen rules won the cascade in print too -- the page
 * printed on an off-white background with a card drop shadow, contrary to
 * SPEC §3.12. The merged `@media print` rule now sits at the END of this
 * file, AFTER this folded section (not "above" it), so this folded section
 * is no longer literally last -- the print rule is. `style.css` is
 * deleted; every rule below is its content, unchanged (values
 * byte-identical to the deleted file), reordered only insofar as its own
 * `@media print` block was merged into this file's `@media print` rule
 * (now at the end of this file) rather than duplicated here -- see that
 * rule for the merged result. A handful of selectors were
 * dropped rather than folded, each because a repo-wide grep of
 * `templates/` and `static/*.js` (including dynamic class construction --
 * checked by reading the Python/Jinja that builds each dynamic class, e.g.
 * `.meter-seg-{{ s.kind }}` in macro_meter.html, which DOES resolve to
 * `.meter-seg-fill`/`.meter-seg-opening` and so those two stayed) found no
 * live match: `.manifest-card`, `.manifest-table` (+ its `th`/`td`/
 * `td:last-child`), `.manifest-scroll` (superseded by the P7 manifest
 * grid's own `table.t.grid`); `.success-msg` (only `.error-msg`, its
 * sibling in the same style.css rule, is ever rendered -- the shared
 * box-model declarations stayed, moved onto `.error-msg` alone, below);
 * `.text-center`, `.flex-between`, `.items-center`, `.justify-end`,
 * `.gap-6`, `.gap-8`, `.gap-12`, `.grid-4` (dropped from its shared rule
 * only -- `.grid-5`/`.grid-3`/`.grid2`/`.grid3` in the same rules are live,
 * kept), `.mt-0`, `.mb-6`, `.pt-12`, `.p-12`, `.border-top`, `.text-13`,
 * `.map-container`, `.map-iframe`, `.surplus`, `.deficit`, `.ml-6` (all
 * zero-hit utility/page classes); `.stop-list`, `.stop-row` (+
 * `.stop-num`/`select`/`.btn.small`/`.btn:disabled`), `.stop-actions`,
 * `.case-size-radios`, `.case-size-option`, `.stop-listing` (+ `li`/
 * `li span`), `.leg-table` (+ `td`/`th`/`.num`/`.leg-total td`),
 * `.corrected-value`, `.overage-pos`, `.overage-neg`, `.overage-part`
 * (the whole pre-redesign multi-stop/actuals-card markup family --
 * superseded by the P5 route partials' own `.split`/`.ledger`/`.kv`
 * markup, which never reused these names); `.muted-row`, `.alert-row`
 * (+ `td`), `.margin-day-repeat` (superseded by `table.t tr.faint`/
 * badges); `.link-muted` (+ `:hover`, superseded by Setup's own drawer
 * links); `.hub-stock-picker` (+ `.hub-stock-pick-row`/`:last-child`/
 * `.hub-stock-overdraw-note`, superseded by the P7 grid's own hub panel
 * markup); and, from a SHARED rule, `.pill.amber`/`.pill.red`
 * specifically (bare `.pill` is still live in
 * `templates/hub/_form_intake.html`, but nothing anywhere builds
 * `class="pill amber"`/`class="pill red"` any more -- P9 re-skinned that
 * usage onto `.badge.late`/`.badge.excluded`). The coder's own report
 * lists this same set as the grep proof. `outline: none` on the legacy
 * `input, select` rule below was checked against this file's own
 * `:focus-visible` rule (above, "Global") -- it does NOT defeat it
 * (`:focus-visible` is a pseudo-class, specificity 0-1-0, which already
 * beats the plain element selector `input`/`select` at 0-0-1 regardless
 * of source order), so nothing needed dropping for SPEC §3.11's
 * one-visible-focus-style rule. Retracted in place (hardening P12,
 * UX-01/UX-16) -- that rule is now `input, select, textarea` (P12 added
 * `textarea` for UX-16); the same specificity argument holds unchanged
 * for `textarea:focus-visible` too, still 0-1-0 beating the plain
 * `textarea` element selector at 0-0-1.
 *
 * SPEC §4.11 exception: `.login-body`/`.login-container` and its form/
 * button/message children ARE changed, not folded unchanged -- the phase
 * brief's own instruction ("auth pages render inside a centred `.card` on
 * `--ground`, using the tokens... do this with CSS... plus at most class
 * additions on the wrapper"). Every hardcoded hex color on those specific
 * rules is replaced with the matching design token (`--ground`,
 * `--surface`, `--ink`, `--ink-2`, `--line`, `--line-strong`, `--shadow`,
 * `--r-lg`, `--r-sm`, `--accent`, `--accent-ink`, `--critical`,
 * `--critical-soft`); no field name, id, JS or copy changed anywhere. The
 * legacy `--root` custom properties below are unchanged from style.css's
 * own (already P1-renamed) names -- re-checked against tokens.css's own
 * list; none of the eleven collide (P1 already renamed the three that
 * did: `--muted`/`--shadow`/`--meter-track` -> `--legacy-muted`/
 * `--legacy-shadow`/`--legacy-meter-track`).
 * ========================================================================== */

:root {
  /* Chrome pass 2026-09-24: these five used to be fixed LIGHT hex values
     (#f6f8fc / #ffffff / #0f172a / #64748b / #e2e8f0) with no dark
     redefinition, so in dark mode the rail and buttons went dark while
     every legacy-styled body, card and table stayed white. Aliased to
     the redesign tokens, which tokens.css already redefines for both
     dark selectors.
     Retracted in place (hardening P12, UX-07) -- `--brand` used to stay a
     fixed `#1D499B` even after the rest of this list was aliased, so
     every legacy link/`.btn` text (both apps' shells reuse these classes
     wholesale) read as a near-illegible dark-on-dark blue in dark mode
     (1.87-2.06:1, measured). `--accent`'s own LIGHT value is the exact
     same `#1D499B`, so light mode is visually unchanged; dark mode now
     resolves to tokens.css's own dark `--accent` (`#8FB0F0`). */
  --brand: var(--accent);
  --danger: #b91c1c;
  --bg: var(--ground);
  --card: var(--surface);
  --text: var(--ink);
  --legacy-muted: var(--muted);
  --border: var(--line);
  --legacy-shadow: 0 10px 25px rgba(2, 6, 23, .08);
  --radius: 14px;
  --legacy-meter-track: #E3EAF6;
  --meter-light: #8FA9D9;
}

html, body {
  margin: 0;
  height: 100%;
}

body {
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  background: var(--bg);
  color: var(--text);
}

/* Layout */
.wrap {
  max-width: 1280px;
  margin: 28px auto;
  padding: 0 18px;
}

.topbar {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 16px;
  margin-bottom: 16px;
}

.top {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  margin-bottom: 16px;
  gap: 12px;
}

.grid2 {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 12px;
}

.grid3 {
  display: grid;
  grid-template-columns: 1fr 1fr 1fr;
  gap: 12px;
}

.stack {
  display: flex;
  flex-direction: column;
  gap: 6px;
}

/* Typography */
h1 {
  margin: 0;
  font-size: 28px;
  letter-spacing: -.3px;
}

.sub {
  color: var(--legacy-muted);
  margin-top: 6px;
  font-size: 13px;
}

.k {
  font-size: 12px;
  /* Retracted in place (hardening P12, UX-07) -- was a fixed `#334155`,
     1.53:1 on the dark `--surface` (measured). `--muted` is the near-
     identical light-mode ink shade every other secondary-label rule
     below already resolves to. */
  color: var(--muted);
  text-transform: uppercase;
  font-weight: 900;
  letter-spacing: .05em;
}

.v {
  margin-top: 6px;
  font-weight: 800;
}

.muted {
  color: var(--legacy-muted);
  font-size: 12px;
  font-weight: 700;
  margin-top: 4px;
}

.mini {
  font-size: 12px;
  color: var(--legacy-muted);
  font-weight: 800;
}

.hint {
  font-size: 12px;
  color: var(--legacy-muted);
  margin-top: 6px;
}

/* UX-13 -- `.list-plain` was used by every list row across both phone
   apps (drops, statements, coming up, arrivals, history) but defined
   NOWHERE (grep of this file, farm.css, buy.css found only a comment
   naming it), so rows fell through to the UA's default bulleted <ul>
   plus the folded `a { font-weight: 700 }` rule above -- browser bullets
   and bold blue underlines on what reads as a finished list. The row
   layout itself (48px min-height, no bullet marker, flex row) is
   shell-scoped (`.farm-shell .list-plain`/`.buy-shell .list-plain`,
   farm.css/buy.css); this is just the list-reset every shell needs. */
.list-plain {
  list-style: none;
  margin: 0;
  padding: 0;
}

a {
  color: var(--brand);
  text-decoration: none;
  font-weight: 700;
}

a:hover {
  text-decoration: underline;
}

/* Components */
.card {
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--legacy-shadow);
  overflow: hidden;
  padding: 16px;
  margin-bottom: 14px;
}

.actions {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  align-items: center;
}

.row-actions {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
  margin-top: 8px;
}

/* BP-05 (hardening P22): forms whose PRIMARY submit is first in the DOM (so
   Enter's implicit submit is the primary, never "Save & add another") are
   laid out visually in reverse; `flex-end` under row-reverse packs left,
   matching the plain row's alignment. */
.row-actions.primary-first {
  flex-direction: row-reverse;
  justify-content: flex-end;
}

/* hardening P15 (§4 F1) -- hub_stock.html's Expected card used to carry
   three hand-typed `style=` attributes for these exact computed values
   (SPEC §3's token rule: no inline style, no bare hex outside tokens.css
   -- these three rules use no hex at all). `.expected-row` also gets
   `.row-actions`'s own `display:flex; gap:8px; flex-wrap:wrap` from that
   class (kept on the element alongside this one, unchanged); the extra
   rules below are additive. */
.expected-date { font-weight: 600; }
.expected-farm { font-weight: 600; margin-top: 6px; }
.expected-row {
  flex-wrap: wrap;
  align-items: flex-start;
  border-top: 1px solid var(--line);
  padding-top: 6px;
  margin-top: 6px;
}

.btn {
  border: 1px solid var(--border);
  background: var(--card);
  color: var(--brand);
  padding: 10px 12px;
  border-radius: 12px;
  cursor: pointer;
  font-weight: 700;
  box-shadow: 0 1px 0 rgba(2, 6, 23, .03);
  display: inline-block;
  text-decoration: none;
}

.btn.primary {
  background: var(--brand);
  /* Retracted in place (hardening P12, UX-07/UX-01) -- this was a fixed
     `#fff`, fine while `--brand` was itself a fixed light-mode blue, but
     now that `--brand` resolves to the dark-mode `--accent` (`#8FB0F0`),
     plain white text on it is 2.18:1 -- below WCAG AA. `--accent-ink` is
     the token every other on-`--accent` surface already uses; its LIGHT
     value is `#FFFFFF`, so light mode is unchanged. */
  color: var(--accent-ink);
  border-color: transparent;
}

.btn.small {
  padding: 7px 10px;
  border-radius: 10px;
  font-size: 12px;
}

/* Retracted in place (hardening P23, BP-11) -- this used `var(--danger)`,
   a fixed `#b91c1c` in the legacy alias block with no dark value (2.45:1
   on the dark surface). `--danger` stays fixed on purpose: `.alert` paints
   it on a fixed light `#fee2e2`, and a dark value there would make alerts
   unreadable. `--critical` is the themed token (light #B3261E, dark
   #F09A93) the `.rd .btn.danger` rule already uses. */
.btn.danger {
  color: var(--critical);
  border-color: var(--critical);
}

.btn.danger-solid {
  background-color: #dc2626;
  color: white;
  border: 0;
}

.btn:hover {
  filter: brightness(.98);
}

.btn:active {
  transform: translateY(1px);
}

.pill {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  border: 1px solid var(--border);
  padding: 6px 10px;
  border-radius: 999px;
  background: #fff;
  font-weight: 800;
  font-size: 12px;
  color: #0f172a;
}

.alert {
  background: #fee2e2;
  color: var(--danger);
  padding: 10px;
  border-radius: 12px;
  margin-bottom: 12px;
  border: 1px solid rgba(185, 28, 28, .25);
  font-weight: 900;
}

/* Tables */
table {
  border-collapse: collapse;
  width: 100%;
}

th,
td {
  border-bottom: 1px solid var(--border);
  padding: 12px;
  text-align: left;
  vertical-align: top;
}

th {
  background: var(--surface-2); /* was #f8fafc -- stayed white in dark mode (Chrome pass 2026-09-24) */
  font-size: 12px;
  color: var(--muted); /* was #334155, unreadable on the dark surface */
  text-transform: uppercase;
  letter-spacing: .05em;
}

td {
  font-size: 14px;
}

/* Forms */
.field {
  margin: 10px 0;
}

label {
  display: block;
  font-weight: 900;
  font-size: 12px;
  /* Retracted in place (hardening P12, UX-07) -- was a fixed `#334155`,
     1.53:1 on the dark `--surface` (measured). `--ink-2` is the near-
     identical light-mode ink shade. Size/weight stay hub-scoped here on
     purpose -- UX-14's larger 15px/600 label is shell-specific
     (`.farm-shell label`/`.buy-shell label`, farm.css/buy.css), not a
     change to every hub form label. */
  color: var(--ink-2);
  margin-bottom: 6px;
}

/* Retracted in place (hardening P12, UX-01/UX-16) -- this rule used to be
   `input, select` only, with a fixed `background: #fff` and no `color`
   at all, so in dark mode every legacy field (both apps' phone shells
   reuse this rule wholesale, plus every un-re-skinned hub form) rendered
   white text on a white box -- typed and prefilled values were
   completely invisible (measured: `color rgb(255,255,255)` on
   `background rgb(255,255,255)`). `textarea` is added here too (UX-16 --
   it previously fell through to the UA default, a small monospace box
   with no border/background of its own); `border` moves from `--border`
   (= `--line`) to the slightly stronger `--line-strong` per this phase's
   own instruction. Font size/min-height are deliberately NOT set here --
   that stays shell-scoped (`.farm-shell input`/`.buy-shell input`,
   farm.css/buy.css) so this hub-wide rule's box model is otherwise
   unchanged. */
input,
select,
textarea {
  width: 100%;
  padding: 10px;
  border: 1px solid var(--line-strong);
  border-radius: 12px;
  outline: none;
  background: var(--surface);
  color: var(--ink);
  box-sizing: border-box;
}

input:focus,
select:focus,
textarea:focus {
  border-color: var(--focus);
  box-shadow: 0 0 0 3px var(--accent-soft);
}

/* The global input/select rule above (full-width, padded, rounded) isn't
   meant for checkboxes/radios -- it hit the allocBasis radios, the case-size
   radios, and the manifest's checkbox column (stretching it into a
   full-width rounded box). These are the only checkbox/radio inputs in the
   app, so one global override here is safe rather than a local escape hatch
   per usage. */
input[type="checkbox"],
input[type="radio"] {
  width: auto;
  padding: 0;
  border: 0;
  border-radius: 0;
}

.error-input {
  border-color: var(--danger);
  /* Retracted in place (hardening P12, UX-01) -- was a fixed `#fff5f5`.
     Now that the field rule above (input, select, textarea) sets an
     explicit `color: var(--ink)` that follows the theme, a light-only
     background here would go unreadable in dark mode on the hub's own
     forms (route/product setup, intake) the instant that field is also
     flagged as an error. `--critical-soft` is the token pair for
     `--critical`, already tuned for contrast in both themes. */
  background: var(--critical-soft);
}

.error-text {
  color: var(--danger);
  font-size: 12px;
  margin-top: 6px;
  font-weight: 800;
}

/* Modals */
.modal-backdrop {
  position: fixed;
  inset: 0;
  background: rgba(15, 23, 42, .55);
  display: none;
  align-items: center;
  justify-content: center;
  padding: 18px;
  z-index: 50;
}

.modal {
  width: min(920px, 100%);
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 18px;
  box-shadow: var(--legacy-shadow);
  overflow: hidden; /* keeps the rounded corners */
  /* C3 -- caps the modal's own height so a tall one (vehicle, hub
     settings, overhead, manage load, per-leg actuals) never runs off the
     bottom of a short viewport (1366x768, ~1280x720): the header and
     .modal-actions footer stay pinned as flex children, .modal-body
     (below) is the one that scrolls. */
  max-height: calc(100vh - 36px);
  display: flex;
  flex-direction: column;
}

/* C3 fix (review round) -- every modal EXCEPT modal_manage_load.html wraps
   .modal-body + .modal-actions inside a single <form>, which is .modal's
   actual direct flex child (.modal-body is the form's child, not
   .modal's) -- .modal-body's own flex:1 1 auto/min-height:0 (below) had no
   effect on it, since a plain block-level <form> parent isn't a flex
   container at all: the form just grew to its content's full height,
   .modal's max-height clipped it, and overflow:hidden left no scrollbar.
   Making the form itself a shrinkable flex column is what lets
   .modal-body's own rule finally apply. modal_manage_load.html (whose
   .modal-body/.modal-actions ARE .modal's direct children already) is
   unaffected either way. */
.modal > form {
  display: flex;
  flex-direction: column;
  flex: 1 1 auto;
  min-height: 0;
}

.modal-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 14px 16px;
  border-bottom: 1px solid var(--border);
  background: #fbfdff;
}

.modal-title {
  font-weight: 900;
}

.x {
  border: 1px solid var(--border);
  background: #fff;
  border-radius: 12px;
  padding: 8px 10px;
  cursor: pointer;
  font-weight: 900;
  color: #334155;
}

.modal-body {
  padding: 16px;
  /* C3 -- the one part of the modal that actually scrolls (see .modal,
     above). flex:1 1 auto + min-height:0 is what lets a flex child
     shrink below its content size at all -- without min-height:0 this
     would just keep growing the modal past max-height instead of
     scrolling internally. */
  overflow-y: auto;
  flex: 1 1 auto;
  min-height: 0;
}

.hr-modal {
  border: 0;
  border-top: 1px solid var(--border);
  margin: 16px 0;
}

.modal-subtitle {
  font-weight: 900;
  margin: 14px 0 8px;
}

.modal-sm {
  width: 400px;
  max-width: 100%;
}

.modal-actions {
  display: flex;
  justify-content: flex-end;
  gap: 10px;
  padding: 14px 16px;
  border-top: 1px solid var(--border);
  background: #fbfdff;
}

/* Page-specific styles */

/* Login / Register / TOTP / Reset pages -- REDESIGN P11 (SPEC §4.11): a
   centred `.card` on `--ground`, using the tokens, no rail -- copy, field
   names, ids and JS unchanged. Token substitutions only (see this
   section's own header comment); box model/layout values are unchanged
   from style.css's own. */
.login-body {
  display: flex;
  justify-content: center;
  align-items: center;
  height: 100vh;
  background: var(--ground);
  /* REDESIGN PHASE P11 fix (SPEC §4.11, gate-confirmed must-fix, iteration
     1) -- retracted in place: this rule used to set no `color` at all,
     which was fine while style.css also loaded and its own `body { color:
     #0f172a }` rule (light-mode-only, hardcoded) supplied it. Now that
     tokens.css switches `--ground`/`--surface` to dark values in dark
     mode, text on these pages fell through to the folded legacy
     `body { color: var(--ink) }` rule (unscoped, still #0f172a-equivalent
     light-mode ink until the theme flips it) -- readable in light mode,
     but near-black text on the dark `.login-container` card in dark mode
     (worst on totp_setup, the mandatory 2FA page). Setting `color` here
     explicitly makes it track the theme the same way every other page's
     text already does. */
  color: var(--ink);
  font-family: var(--sans);
  /* REDESIGN PHASE P11 (SPEC §3.13, phase brief E) -- 16px gutter, same
     as every other page's phone gutter, so the card never sits flush
     against the viewport edge at 400px (the card itself is `width:100%;
     max-width:400px`, so this is what actually keeps a gap on a viewport
     that narrow). */
  padding: 16px;
  box-sizing: border-box;
}

.login-container {
  background: var(--surface);
  padding: 2rem;
  border-radius: var(--r-lg);
  border: 1px solid var(--line);
  box-shadow: var(--shadow);
  width: 100%;
  max-width: 400px;
  /* REDESIGN PHASE P11 (SPEC §3.13, phase brief E) -- without this, the
     2rem padding + 1px border add ON TOP of `width: 100%` (this file
     carries no global `*{box-sizing:border-box}` reset), so at exactly a
     400px viewport (`.login-body` has no side padding of its own) the
     card's rendered box comes out to ~466px -- wider than the viewport
     it's meant to stay fluid inside. */
  box-sizing: border-box;
}

.login-container h2 {
  margin-top: 0;
  color: var(--ink);
  text-align: center;
}

/* REDESIGN PHASE P11 fix (SPEC §4.11, gate-confirmed must-fix, iteration 1)
   -- new rule, not a style.css fold. The auth pages' links (Sign up / Try
   Without an Account / Forgot password, on login/register/reset) carried
   no color of their own, so they fell through to the folded legacy
   `a { color: var(--brand) }` rule. Retracted in place (hardening P12,
   UX-07) -- this used to say that fallback was "a fixed dark blue that
   stays legible on light `--ground` but reads as near-illegible dark-on-
   dark in dark mode". `--brand` is no longer a fixed hex; it now
   resolves to `var(--accent)` (P12's own `--brand` fix, above), so the
   folded `a` rule alone would now also clear dark mode. This rule stays
   anyway -- it is harmless (light value identical, dark value the same
   token) and removing it would be an unrelated cleanup outside this
   phase's own scope. */
.login-container a {
  color: var(--accent);
}

.form-group {
  margin-bottom: 1rem;
}

.form-group label {
  display: block;
  margin-bottom: 0.5rem;
  color: var(--ink-2);
  font-weight: normal;
}

.form-group input {
  width: 100%;
  padding: 0.75rem;
  border: 1px solid var(--line-strong);
  border-radius: var(--r-sm);
  box-sizing: border-box;
  font-size: 1rem;
  background: var(--surface);
  color: var(--ink);
}

.auth-footer {
  margin-top: 1rem;
  text-align: center;
  font-size: 0.9rem;
  color: var(--ink-2);
}

/* hardening P15 (UX-22) -- `.auth-footer-links` (the `<ul>` login.html's
   footer now renders, one `<li>` per link/form) gets a plain list reset;
   its rows are the actual 48px tap targets, below. */
.auth-footer-links { list-style: none; margin: 0; padding: 0; }

/* SECURITY-SPEC.md SEC-18 (hardening P4) -- login.html's "Try Without an
   Account" is a one-button POST form now, not a bare <a>.
   Retracted in place (hardening P15, UX-22): this comment used to end
   "...sitting inline among the other auth-footer links" -- the footer's
   links are stacked rows now (`.auth-footer-links`, above), never an
   inline run of text, so `.link-form` no longer needs `display: inline`
   to avoid an unwanted line break; it is scoped to `.auth-footer-links`
   so it still wins over a block-level <form>'s own default, but now sets
   `display: block; width: 100%` so the form fills its own 48px `<li>`
   row exactly like the plain `<a>` rows either side of it. `.link-btn`
   overrides `.login-container button`'s full-width primary-button look
   (below) back down to plain link styling, so it reads exactly like the
   <a> it replaces. */
.auth-footer-links .link-form { display: block; width: 100%; margin: 0; }
.auth-footer-links .link-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  min-height: 48px;
  padding: 0;
  margin: 0;
  background: none;
  color: var(--accent);
  border: none;
  border-radius: 0;
  font-size: inherit;
  font-family: inherit;
  cursor: pointer;
  text-decoration: none;
}
.auth-footer-links .link-btn:hover { filter: none; text-decoration: underline; }

/* hardening P15 (UX-22) -- every plain `<a>` row (Sign up's own `<li>`,
   Forgot password?) grows to the same 48px tap target the `.link-btn`
   row above gets, centred the same way. */
.auth-footer-links li > a {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 48px;
}

.login-container button {
  width: 100%;
  padding: 0.75rem;
  background-color: var(--accent);
  color: var(--accent-ink);
  border: none;
  border-radius: var(--r-sm);
  font-size: 1rem;
  cursor: pointer;
  transition: background 0.2s;
  /* hardening P15 (UX-22) -- was a 42px-tall button (0.75rem padding,
     1rem text, no explicit height); a real 48px tap target now. */
  min-height: 48px;
}

.login-container button:hover {
  filter: brightness(1.08);
}

/* Retract in place: style.css used to declare `.error-msg, .success-msg`
   together (shared box model) plus a separate hardcoded-hex `.error-msg`
   color rule and a `.success-msg` color rule. `.success-msg` was proven
   unused by a repo-wide grep (this section's own header comment) and
   dropped; its box-model declarations moved onto `.error-msg` alone,
   below, with its color swapped for the `--critical`/`--critical-soft`
   tokens per SPEC §4.11. */
.error-msg {
  color: var(--critical);
  background-color: var(--critical-soft);
  border: 1px solid var(--critical);
  padding: 0.75rem;
  border-radius: var(--r-sm);
  margin-bottom: 1rem;
  display: none;
}

/* TOTP Setup */
.totp-qr {
  text-align: center;
  margin-bottom: 16px;
}

.totp-qr img {
  width: 200px;
  height: 200px;
}

/* REDESIGN PHASE P11 fix (SPEC §4.11, gate-confirmed must-fix, iteration
   1) -- retracted in place: `background`/`border` here used to be
   style.css's own hardcoded `#f4f4f5`/`#d1d5db` (unchanged by the initial
   P11 fold, since the phase brief's token-substitution list only named
   `.login-body`/`.login-container` at the time). Once `.login-body` above
   gained `color: var(--ink)`, this box's inherited text turned light in
   dark mode while its own background stayed the light, hardcoded
   `#f4f4f5` -- light text on a light box, on totp_setup, the mandatory
   2FA page. Applying the same SPEC §4.11 token substitution used on the
   rest of this auth block (`--surface-2` for the box, `--line` for the
   border, an explicit `--ink` for its own text so it never again depends
   on what it inherits) fixes both themes; every other declaration here is
   unchanged. */
.totp-secret {
  background: var(--surface-2);
  border: 1px solid var(--line);
  color: var(--ink);
  border-radius: 6px;
  padding: 12px;
  text-align: center;
  font-family: monospace;
  font-size: 18px;
  letter-spacing: 2px;
  margin-bottom: 8px;
  word-break: break-all;
}

.totp-instructions {
  text-align: left;
  margin-bottom: 20px;
  padding-left: 20px;
  line-height: 1.8;
  font-size: 14px;
}

.copy-btn {
  margin-bottom: 20px;
  font-size: 13px;
  cursor: pointer;
  background: none;
  border: none;
  color: var(--brand);
  text-decoration: underline;
}

.text-muted-sm {
  font-size: 13px;
  color: var(--legacy-muted);
  margin-bottom: 8px;
}

/* Map View */
.map-body {
  position: relative;
}

#map {
  width: 100%;
  height: 100%;
}

#info {
  position: absolute;
  top: 12px;
  left: 12px;
  display: none;
  background: rgba(255, 255, 255, .92);
  border-radius: 10px;
  padding: 10px 12px;
  font: 13px/1.3 system-ui, Arial, sans-serif;
  box-shadow: 0 6px 18px rgba(0, 0, 0, .15);
  z-index: 2;
  pointer-events: auto;
  max-width: min(90vw, 320px);
}

#info .value {
  font-weight: 700;
}

@media (max-width: 640px) {
  #info {
    top: 8px;
    left: 8px;
    padding: 8px 10px;
    font-size: 12px;
  }
}

/* SECURITY-SPEC.md SEC-05 (hardening P6) -- the map page's own "no
   browser token configured" panel (map_view.html), shown INSTEAD of the
   map/Mapbox GL script entirely, never alongside it. Fills the same
   `.map-body` the real map would, centered -- same general "readable
   panel over a plain background" shape as `#info` above, not the
   Mapbox-layer-color exception SPEC §3.1 carves out for the map's OWN
   markers/route line, so this uses the shared token palette. */
.map-unavailable {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 100%;
  /* hardening P23, BP-16 -- content-box sizing made 100% + 48px padding
     = 308px inside the 260px iframe (an inner scrollbar). */
  box-sizing: border-box;
  overflow: hidden;
  padding: 24px;
  text-align: center;
  font: 15px/1.4 system-ui, Arial, sans-serif;
  color: var(--legacy-muted);
}

/* Utilities */
.mr-auto { margin-right: auto; }

.flex { display: flex; }

.gap-10 { gap: 10px; }

.grid-3 { display: grid; grid-template-columns: repeat(3, 1fr); gap: 12px; }
.grid-5 { display: grid; grid-template-columns: repeat(5, 1fr); gap: 12px; }

.mt-4 { margin-top: 4px; }
.mb-4 { margin-bottom: 4px; }
.mb-10 { margin-bottom: 10px; }
.mb-12 { margin-bottom: 12px; }
.mb-16 { margin-bottom: 16px; }

.block-divider { border-bottom: 1px solid var(--border); padding-bottom: 12px; }
/* Owner note N12 (9/10) -- recovery-page meters (macro_meter.html);
   inline SVG, no rounded data-end, no status colors (the tick is a
   naive baseline, never a verdict). */
.meter { display:block; width:100%; height:10px; margin:6px 0 4px; }
.meter-track { fill:var(--legacy-meter-track); stroke:var(--border); stroke-width:1; vector-effect:non-scaling-stroke; }
.meter-seg-fill { fill:var(--brand); }
.meter-seg-opening { fill:var(--meter-light); }
.meter-gap { stroke:#fff; stroke-width:2; }
.meter-tick { stroke:var(--text); stroke-width:2; }
.w-100 { width: 100%; }
.bold { font-weight: 900; }
.nowrap { white-space: nowrap; }

/* ---- Hub intake / stock (Task A) -- age/no-destination chips. --------- */
.ml-6 { margin-left: 6px; }

.mt-16 { margin-top: 16px; }

/* ---- Searchable selects -------------------------------------------------
   Every <select> gets a type-to-search combobox layered on top of it (see
   static/searchable-select.js). The original <select> is kept in the DOM --
   it's still the real submitting form control -- but visually hidden via
   the standard "visually-hidden" technique (not display:none, so it stays
   reachable/predictable, just removed from the visual flow). */

select.ss-native-select {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
  opacity: 0;
}

.ss-wrap {
  position: relative;
}

.ss-input {
  /* Inherits width/padding/border/radius/background from the app's global
     `input, select` rule above -- this is what gives it the same native
     look as every other field. */
  cursor: text;
  padding-right: 28px;
  background-image: linear-gradient(45deg, transparent 50%, var(--legacy-muted) 50%),
    linear-gradient(135deg, var(--legacy-muted) 50%, transparent 50%);
  background-position: calc(100% - 18px) center, calc(100% - 13px) center;
  background-size: 5px 5px, 5px 5px;
  background-repeat: no-repeat;
}

.ss-input:disabled {
  background-color: #f1f5f9;
  color: var(--legacy-muted);
  cursor: not-allowed;
}

.ss-list {
  display: none;
  position: fixed; /* escapes .modal's overflow:hidden -- see position() in
                       searchable-select.js, which sets top/left/width. */
  z-index: 999;
  max-height: 240px;
  overflow-y: auto;
  background: #fff;
  border: 1px solid var(--border);
  border-radius: 12px;
  box-shadow: var(--legacy-shadow);
  padding: 4px;
}

.ss-list.ss-open {
  display: block;
}

.ss-option {
  padding: 8px 10px;
  border-radius: 8px;
  font-size: 14px;
  cursor: pointer;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.ss-option.ss-active {
  background: #eef2ff;
}

.ss-option.ss-selected {
  font-weight: 800;
}

.ss-empty {
  padding: 8px 10px;
  font-size: 12px;
  color: var(--legacy-muted);
  font-weight: 700;
}

.ss-disabled .ss-input {
  background-color: #f1f5f9;
}

/* Slice D (§3), specialist review round -- the far-stop allocation card's
   table, while nothing on it is actually SAVED yet (still a suggestion
   or a remembered split, offered only as a convenience pre-fill): same
   de-emphasis idiom as .muted-row (dropped, unused), so the figures never
   read as "this is what's happening right now" until a save makes that
   true. */
.far-alloc-unsaved {
  opacity: .7;
}

/* Slice F -- the transcription-barrier code: a string meant to be READ and
   COPIED, never guessed or typed reflexively (the decision doc's own
   "not a password" reasoning) -- a monospace, letter-spaced block makes it
   legible as a sequence of individual characters rather than a word. */
.transcription-code {
  font-family: monospace;
  font-size: 20px;
  font-weight: 800;
  letter-spacing: 4px;
  background: #f4f6f9;
  border: 1px solid #d8dee7;
  border-radius: 6px;
  padding: 6px 10px;
  display: inline-block;
  margin: 6px 0;
}

/* Slice F, n4 -- the read-only "History" disclosure every financial form
   gains: a plain <details> (no JS needed to open/close it), its rows
   styled as a compact table. A superseded version is de-emphasised the
   same way `.muted-row` (dropped, unused) used to treat a voided route. */
.version-history-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 12px;
  margin-top: 6px;
}
.version-history-table td, .version-history-table th {
  padding: 3px 6px;
  border-bottom: 1px solid #eef1f5;
  text-align: left;
}
.version-history-table tr.superseded {
  opacity: .55;
}

/* Round-of-review should-fix #6 (accountant) -- marks the ONE row
   financials_in_force actually returns as of today, so a later
   version still governing (the exact state blocker 1's intervening-
   version choice is about) is visible in the table itself, not just
   inferable by eye from dates. */
.current-badge {
  display: inline-block;
  margin-left: 6px;
  padding: 1px 6px;
  border-radius: 10px;
  background: #e6f4ea;
  color: #1a7f37;
  font-size: 10px;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: .03em;
}

/* Round-of-review Blocker 1 -- the intervening-version choice panel. */
.intervening-choice {
  border: 1px solid #d8dee7;
  border-radius: 6px;
  padding: 8px;
  margin-top: 6px;
  background: #fff8ec;
}
.intervening-choice label {
  display: flex;
  gap: 6px;
  align-items: flex-start;
  font-weight: 700;
  margin-bottom: 6px;
}

/* C3 -- responsive breakpoints. Nothing above this point changes at any
   width; these narrow the multi-column grids, stack the page-level
   title+actions rows, and let a wide table scroll INSIDE its own card
   rather than force the page body to scroll sideways. `.grid-4` dropped
   from these shared selector lists -- proven unused, see this section's
   own header comment. */
@media (max-width: 900px) {
  .grid-5 { grid-template-columns: repeat(3, 1fr); }
  .grid-3, .grid2, .grid3 { grid-template-columns: repeat(2, 1fr); }

  /* .topbar (header.html's own former title+actions row, and
     routes_list.html's second one) and .top (route_view.html/
     statements.html/statement_view.html/recovery_report.html's identical
     title+actions shape, different class name) both stack the title
     above the actions rather than squeezing them onto one row. */
  .topbar, .top {
    flex-direction: column;
    align-items: stretch;
  }

  /* A wide manifest/statement/report table scrolls inside its own card --
     the page body itself must never scroll horizontally. */
  .card { overflow-x: auto; }
}

@media (max-width: 640px) {
  .grid-5, .grid-3, .grid2, .grid3 { grid-template-columns: repeat(2, 1fr); }
}

/* ---------- Print (SPEC §3.12), moved here (REDESIGN PHASE P11 fix,
   iteration 1, gate-confirmed must-fix) --------------------------------
   Retracted in place: this rule used to sit BEFORE the "Folded from
   static/style.css" section above (right after the "Print (SPEC §3.12)"
   header comment near the top of this file, where that comment and the
   folded section's own header still point). At that position it lost the
   cascade to the folded section's own same-specificity `body`/`.card`
   screen rules, which come later in source order: printed pages got the
   folded block's #f6f8fc-ish page background and the legacy card drop
   shadow instead of this rule's white background and hairline border,
   contrary to SPEC §3.12 and the pre-flight "cascade order stays
   unchanged" rule (both reviewers + the gate flagged this). Moving the
   whole rule to the very end of the file -- after the folded section's own
   last rule -- restores the win, the same way style.css's own trailing
   `@media print` block used to win when style.css loaded after app.css.
   Contents are unchanged from before the move; only the position and this
   comment changed. */
@media print {
  .rail, .topbar.shell-topbar, .toast, .drawer, .no-print { display: none !important; }
  .shell { display: block; }
  .main { padding: 0; }
  .meter .fill, .meter .opening, .meter .track { print-color-adjust: exact; -webkit-print-color-adjust: exact; }
  .print-head { display: block; position: fixed; top: 0; left: 0; right: 0; font-size: 11px; color: var(--muted); padding: 4px 0; }

  /* Folded from style.css's own `@media print` block (REDESIGN P11, phase
     brief A.3) -- merged into this rule rather than duplicated, so
     SPEC §3.12's "body white; cards hairline, break-inside: avoid; meters
     keep print-color-adjust: exact" lives in one place. `.no-print` is
     already covered above (this file's own P1 rule); style.css's own
     duplicate of it is not repeated here. */
  body { background: #fff; }
  .card { box-shadow: none; border: 1px solid var(--border); break-inside: avoid; }
  .audit-block { page-break-inside: avoid; }
  a[href]:after { content: ""; } /* no printed URL clutter after links */
  /* Owner note N12 (9/10) -- SVG fills are "background graphics" to most
     print engines' default off switch; force them to print anyway, same
     reasoning macro_meter.html's own header comment gives for using SVG
     rects instead of CSS-background divs in the first place. This is the
     LEGACY flat-class meter (macro_meter.html's own markup); the nested
     `.meter .fill/.opening/.track` rule two lines up is the new system's
     equivalent -- both are needed, neither is a duplicate of the other. */
  .meter-track, .meter-seg, .meter-gap, .meter-tick { print-color-adjust: exact; -webkit-print-color-adjust: exact; }
}
