/* ==========================================================================
   Order Form Generator — design tokens
   Ledger-inspired: paper surfaces, brass accent, monospaced data.
   Single, centered column — quiet and deliberate.
   ========================================================================== */
:root {
  /* Industrial / hazard system — same brand tokens and visual language as
     the Warehouse Cleanup app (site-header, hazard stripes, JetBrains Mono
     labels, Oswald headings, sharp edges) carried over to every app in the
     suite so the whole Workspace reads as one product. */
  --brand-blue:       #1a4fa0;
  --brand-blue-light: #4d74d6;
  --brand-blue-dim:   rgba(26, 79, 160, 0.10);
  --brand-red:        #b3413a;
  --brand-black:      #0f1318;

  --bg:        #eef0f3;   /* soft neutral floor tone */
  --bg-grad:   none;
  --surface:   #FFFFFF;
  --surface-2: #f4f5f7;
  --border:    #dde1e7;
  --border-hi: #b7bec9;
  --text:      var(--brand-black);
  --text-dim:  #343d4b;
  --text-mute: #444d5b; /* was #8991a0 — 3.2:1 on white, failed WCAG AA for normal text; this reads ~4.8:1 */

  --accent:     var(--brand-blue);   /* LYR brand blue — links, focus, active nav */
  --accent-hi:  #123b7d;
  --accent-dim: rgba(26, 79, 160, 0.08);
  --accent-ink: #FFFFFF;

  --ok:      #2c8a56;   /* green — success / create actions */
  --ok-dim:  rgba(44, 138, 86, 0.10);
  --err:     var(--brand-red);   /* muted red — errors / destructive */
  --err-dim: rgba(179, 65, 58, 0.08);
  --warn:    #ad7c50;   /* amber — pending / needs attention */
  --warn-dim: rgba(173, 124, 80, 0.12);
  --violet:  #7C5CE0;   /* extra icon-distinction color */
  --teal:    #0EA5A8;   /* extra icon-distinction color */

  --ink:     var(--brand-black);
  --hazard:  var(--brand-red);

  --font-display: 'Oswald', 'Inter', -apple-system, Segoe UI, Roboto, sans-serif;
  --font-body:    'Inter', -apple-system, Segoe UI, Roboto, sans-serif;
  --font-mono:    'JetBrains Mono', ui-monospace, SFMono-Regular, monospace;

  --radius:    6px;
  --radius-sm: 4px;
  --shadow-1: 0 1px 2px rgba(16,24,40,0.04), 0 1px 3px rgba(16,24,40,0.05);
  --shadow-2: 0 2px 8px rgba(16,24,40,0.06);
  --shadow-3: 0 16px 40px rgba(16,24,40,0.16), 0 2px 8px rgba(16,24,40,0.08);
  --ease: cubic-bezier(0.22, 1, 0.36, 1);
  --spring: cubic-bezier(0.34, 1.56, 0.64, 1);

  /* density-first spacing scale — used going forward so paddings/gaps
     stay consistent across the suite instead of one-off numbers per
     component. Existing hardcoded values are untouched for now; new
     work (icons, upgraded cards) pulls from these. */
  --space-1: 4px;
  --space-2: 8px;
  --space-3: 12px;
  --space-4: 16px;
  --space-5: 24px;
  --row-h: 34px;
  --icon-size: 15px;
}

* { -webkit-tap-highlight-color: transparent; }
.app-nav, .rail-inner, .uploaded-list, .results-dock, .supplier-list {
  -webkit-overflow-scrolling: touch;
  overscroll-behavior: contain;
}
::-webkit-scrollbar { width: 8px; height: 8px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb { background: rgba(0,0,0,0.18); border-radius: 8px; }
::-webkit-scrollbar-thumb:hover { background: rgba(0,0,0,0.28); }

@media (prefers-reduced-motion: no-preference) {
  @view-transition { navigation: auto; }
}

/* Quiet accent line — a slim, single-tone strip instead of a hazard
   chevron, used under the sidebar brand and the topbar/home header. */
.hazard-bar {
  height: 3px;
  width: 100%;
  flex: none;
  background: var(--accent);
  opacity: 0.55;
}

* { box-sizing: border-box; }
html, body { height: 100%; }
html { scroll-behavior: smooth; }
body {
  font-family: var(--font-body);
  background: var(--bg);
  background-image:
    radial-gradient(circle at 20% 30%, rgba(0,0,0,0.035) 0, transparent 45%),
    radial-gradient(circle at 80% 70%, rgba(0,0,0,0.035) 0, transparent 50%),
    repeating-linear-gradient(0deg, rgba(0,0,0,0.028) 0px, rgba(0,0,0,0.028) 1px, transparent 1px, transparent 96px),
    repeating-linear-gradient(90deg, rgba(0,0,0,0.028) 0px, rgba(0,0,0,0.028) 1px, transparent 1px, transparent 96px);
  background-attachment: fixed;
  color: var(--text);
  margin: 0;
  padding: 12px 18px 14px;
  font-size: calc(13px * var(--fs, 1));
  line-height: 1.35;
  -webkit-font-smoothing: antialiased;
}

.wrap {
  /* Full-bleed: fill the viewport edge to edge (minus a small breathing
     gutter) instead of floating a narrow column in a sea of empty --neu-bg.
     Was a hard 1560px cap -- fine on a laptop, but left large dead margins
     on anything wider. 2200px is a soft ceiling so extreme ultrawide
     monitors don't stretch a two-column form into unreadable line lengths. */
  max-width: min(2200px, 100vw - 32px);
  margin: 0 auto;
  display: flex;
  flex-direction: column;
  gap: 10px;
}

/* topbar / progress panel span the full layout width (rails + main) */
.topbar, #generateProgress.progress-panel {
  width: 100%;
}

::selection { background: var(--accent-dim); color: var(--accent-hi); }

/* scrollbars */
* { scrollbar-width: thin; scrollbar-color: var(--border-hi) transparent; }
*::-webkit-scrollbar { width: 8px; height: 8px; }
*::-webkit-scrollbar-thumb { background: var(--border-hi); border-radius: var(--radius-sm); }
*::-webkit-scrollbar-track { background: transparent; }

h1 {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: calc(20px * var(--fs, 1));
  margin: 0;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  color: var(--ink);
}
h2 {
  font-family: var(--font-mono);
  font-weight: 700;
  font-size: calc(11.5px * var(--fs, 1));
  letter-spacing: 0.07em;
  text-transform: uppercase;
  margin: 0 0 8px;
  color: var(--ink);
  display: flex;
  align-items: center;
  gap: 9px;
}

/* ---------- header icons (Lucide-style inline SVG, currentColor) ---------- */
.h-icon {
  flex: none;
  width: var(--icon-size);
  height: var(--icon-size);
  color: var(--text-dim);
}
.card h2 .h-icon { color: var(--accent); }
.rail-card-title .h-icon { color: var(--text-dim); width: 15px; height: 15px; }
.btn-icon {
  flex: none;
  width: 15px;
  height: 15px;
  margin-right: 6px;
  vertical-align: -2px;
}

/* ---------- phase numerals (signature element) ---------- */
.stack { counter-reset: phase; }
.card { counter-increment: phase; }
.card h2::before {
  content: "0" counter(phase);
  font-family: var(--font-mono);
  font-size: calc(11px * var(--fs, 1));
  font-weight: 700;
  letter-spacing: 0;
  color: var(--accent-ink);
  background: var(--ink);
  border: 1px solid var(--border-hi);
  border-radius: var(--radius-sm);
  padding: 2px 7px;
  text-transform: none;
}

/* ambient "in-flight request" bar removed from here -- it's now the one
   shared chibi loader in nav-loading.js (WsLoader), driven by the global
   window.setBusy() in nav-loading.js instead of each page's own markup.
   See fonts.css for the styling and nav-loading.js for the behavior. */

/* ---------- generate progress panel (determinate, step-aware) ---------- */
.progress-panel {
  background: var(--surface);
  border: 1px solid var(--border-hi);
  border-top: 3px solid var(--accent);
  border-radius: var(--radius-sm);
  padding: 10px 16px;
  display: flex;
  flex-direction: column;
  gap: 8px;
  box-shadow: var(--shadow-1);
}
.progress-row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
}
.progress-label {
  font-family: var(--font-mono);
  font-size: calc(12px * var(--fs, 1));
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  color: var(--text-dim);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.progress-percent {
  font-family: var(--font-mono);
  font-size: calc(12.5px * var(--fs, 1));
  font-weight: 700;
  color: var(--hazard);
  flex: none;
  font-variant-numeric: tabular-nums;
}
.progress-track {
  height: 8px;
  border-radius: var(--radius-sm);
  background: var(--surface-2);
  border: 1px solid var(--border-hi);
  overflow: hidden;
}
.progress-fill {
  height: 100%;
  border-radius: var(--radius-sm);
  background: var(--accent);
  box-shadow: 0 0 8px rgba(26, 79, 160, 0.22);
  transition: width .35s ease;
  animation: fill-working 1.7s ease-in-out infinite;
}
@keyframes fill-working {
  0%, 100% { filter: brightness(1); }
  50%      { filter: brightness(1.25); }
}
/* Same progress-panel look, for actions with no real percentage to
   report (a single synchronous request rather than a polled background
   job) -- a bar of fixed width sliding back and forth reads as "working"
   without pretending to know how far along it is. */
.progress-fill.indeterminate {
  width: 35% !important;
  animation: indeterminate-slide 1.3s ease-in-out infinite;
}
@keyframes indeterminate-slide {
  0%   { margin-left: 0%; }
  50%  { margin-left: 65%; }
  100% { margin-left: 0%; }
}
@keyframes dot-pulse {
  0%, 100% { opacity: 1; transform: scale(1); }
  50% { opacity: 0.35; transform: scale(0.55); }
}
@keyframes done-badge-pop {
  0% { transform: scale(0); opacity: 0; }
  55% { transform: scale(1.25); opacity: 1; }
  100% { transform: scale(1); opacity: 1; }
}

/* ==========================================================================
   App-switcher sidebar — persistent left nav, present on every app page.
   Replaces the old home-screen launcher: switching apps is a hover + click
   here instead of a trip back to a landing page.
   ========================================================================== */
:root {
  --nav-w-icon: 56px;
  --nav-w:      178px;
  /* Height of the fixed top bar on sidebar-based tool pages (.app-header
     + .po-strip, which pins itself over the same rect). One variable so
     every place that has to stay in sync with it (canvas min-height,
     content top-padding) only needs changing here. */
  --header-h: 36px;
}

body.app-shell { padding-left: calc(var(--nav-w) + 22px); }

.app-nav {
  position: fixed;
  top: 0; left: 0; bottom: 0;
  width: var(--nav-w);
  background: var(--ink);
  border-right: 1px solid var(--border-hi);
  display: flex;
  flex-direction: column;
  padding: 0 0 16px;
  overflow-y: auto;
  overflow-x: hidden;
  z-index: 40;
  box-shadow: var(--shadow-1);
  animation: nav-slide-in .4s var(--ease) both;
}
@keyframes nav-slide-in {
  from { opacity: 0; transform: translateX(-8px); }
  to { opacity: 1; transform: translateX(0); }
}

.app-nav::before {
  content: "";
  display: block;
  height: 3px;
  flex: none;
  background: var(--accent);
  opacity: 0.55;
}

.app-nav-brand {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 14px 14px 14px;
  margin-bottom: 6px;
  border-bottom: 1px solid rgba(255,255,255,0.12);
  text-decoration: none;
  color: var(--surface-2);
}
.app-nav-brand-mark {
  flex: none;
  width: 28px; height: 28px;
  border-radius: var(--radius-sm);
  background: var(--hazard);
  color: #fff;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-family: var(--font-display);
  font-size: calc(13px * var(--fs, 1));
  font-weight: 700;
}
.app-nav-brand-text {
  font-family: var(--font-display);
  font-size: calc(13px * var(--fs, 1));
  font-weight: 700;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: #fff;
}

.app-nav-list {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: 8px 10px 0;
}

.app-nav-footer {
  margin-top: auto;
  padding: 8px 10px 4px;
  border-top: 1px solid rgba(255,255,255,0.12);
}

.app-nav-logout {
  opacity: 0.75;
}
.app-nav-logout:hover { opacity: 1; }
.app-nav-logout .app-nav-icon { background: transparent; border-color: rgba(255,255,255,0.3); }

.app-nav-item {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 8px;
  border-radius: var(--radius-sm);
  border-left: 3px solid transparent;
  text-decoration: none;
  color: #b9c2d6;
  font-family: var(--font-mono);
  font-size: calc(11.5px * var(--fs, 1));
  font-weight: 600;
  letter-spacing: 0.01em;
  line-height: 1.3;
  transition: background .18s var(--ease), color .18s var(--ease), border-color .18s var(--ease), transform .12s var(--ease);
}
.app-nav-item:hover { background: rgba(255,255,255,0.06); color: #fff; transform: translateX(2px); }
.app-nav-item.active {
  background: rgba(2, 59, 188, 0.28);
  border-left-color: var(--hazard);
  color: #fff;
  font-weight: 700;
}

.app-nav-icon {
  flex: none;
  width: 26px; height: 26px;
  border-radius: var(--radius-sm);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-family: var(--font-mono);
  font-size: calc(10px * var(--fs, 1));
  font-weight: 700;
  color: #fff;
  border: 1px solid rgba(255,255,255,0.25);
  opacity: 0.88;
}
.app-nav-item:hover .app-nav-icon,
.app-nav-item.active .app-nav-icon { opacity: 1; }
.app-nav-blue   .app-nav-icon { background: var(--accent); }
.app-nav-amber  .app-nav-icon { background: var(--warn); }
.app-nav-green  .app-nav-icon { background: var(--ok); }
.app-nav-violet .app-nav-icon { background: var(--violet); }
.app-nav-teal   .app-nav-icon { background: var(--teal); }
.app-nav-red    .app-nav-icon { background: var(--err); }
.app-nav-black  .app-nav-icon { background: #000; border-color: var(--hazard); }

/* Small pulsing dot marking a freshly-added nav entry — same pulse used
   for "in progress" indicators in the Warehouse app, for a consistent
   suite-wide motion language. */
.app-nav-live-dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--hazard);
  margin-left: auto;
  flex: none;
  animation: app-nav-dot-pulse 1.4s ease-in-out infinite;
}
@keyframes app-nav-dot-pulse {
  0%, 100% { opacity: 1; transform: scale(1); }
  50% { opacity: 0.35; transform: scale(0.6); }
}
@media (max-width: 900px) { .app-nav-live-dot { display: none; } }

.app-nav-label {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Icon-only rail once the window gets tight — still one click/hover away
   from every other app, just without the label text. */
@media (max-width: 900px) {
  :root { --nav-w: var(--nav-w-icon); }
  .app-nav { padding: 0 0 12px; align-items: center; }
  .app-nav-brand { justify-content: center; padding: 12px 0 12px; }
  .app-nav-brand-text { display: none; }
  .app-nav-list { align-items: center; }
  .app-nav-item { justify-content: center; padding: 8px; border-left: none; border-top: 3px solid transparent; }
  .app-nav-item.active { border-top-color: var(--hazard); border-left-color: transparent; }
  .app-nav-label { display: none; }
}

@media (max-width: 560px) {
  body.app-shell { padding-left: calc(var(--nav-w-icon) + 14px); }
}

/* ---------- top control strip ----------
   Single header shell used on every page in the suite — Home, My Account,
   Manage Accounts, and every tool app including Warehouse Cleanup and
   Receiving Receipts. Same dark ink bar, same height, same brand-mark /
   crumb / cta anatomy everywhere, so moving between apps never feels like
   a different product. Kept short on purpose: this is a control strip,
   not a banner. */
.topbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px 24px;
  flex-wrap: wrap;
  padding: 5px 16px;   /* corporate pass: thinner control strip (was 7px) */
  /* Pinned so the header (and its Generate/action controls) stay reachable
     while the page scrolls, same as the two side rails. */
  position: sticky;
  top: 0;
  z-index: 30;
  background: var(--ink);
  border-bottom: 1px solid var(--border-hi);
  box-shadow: var(--shadow-2);
  animation: topbar-fade-in .4s var(--ease) both .06s;
}
@keyframes topbar-fade-in {
  from { opacity: 0; transform: translateY(-4px); }
  to { opacity: 1; transform: translateY(0); }
}
.topbar h1 { color: #fff; font-size: calc(17px * var(--fs, 1)); }
.brand { display: flex; align-items: center; gap: 12px; }
.brand-mark {
  font-family: var(--font-display);
  font-size: calc(12px * var(--fs, 1));
  font-weight: 700;
  color: #fff;
  background: var(--hazard);
  border: 1px solid #fff;
  border-radius: var(--radius-sm);
  width: 26px;   /* was 30px — thinner header */
  height: 26px;  /* was 30px */
  display: inline-flex;
  align-items: center;
  justify-content: center;
  letter-spacing: 0.02em;
  flex: none;
  box-shadow: var(--shadow-1);
  overflow: hidden;
}
.brand-mark img { width: 100%; height: 100%; object-fit: contain; }

/* .brand-mark defaults to a 30x30 square badge, sized for a couple of
   letters ("W", "OF", "A"...). The real LYR Motorparts logo is a wide
   wordmark (476x110), so dropping it straight into that square shrinks
   it to an unreadable ~30x7px sliver behind the badge's solid-color
   background. Every page's header now uses the actual logo instead of
   a lettered badge, so this variant sizes itself off the logo's own
   aspect ratio instead: fixed height to match the row it sits in,
   width free to follow, no background/border since the logo isn't a
   solid-color initial. */
.brand-mark-logo {
  width: auto;
  height: 26px;  /* was 30px — thinner header */
  background: none;
  border: none;
  box-shadow: none;
  padding: 0;
}
.brand-mark-logo img { width: auto; height: 100%; object-fit: contain; }
.brand-text { display: flex; flex-direction: column; gap: 3px; min-width: 0; }

/* .crumb is the one pill-link style for every bit of header navigation:
   "back to Workspace", "My account", "Manage accounts", "Log out", app
   cross-links — anything that isn't the primary page action. */
.crumb {
  display: inline-flex;
  align-items: center;
  align-self: flex-start;
  gap: 6px;
  /* .crumb is also used on <button> elements (e.g. "About this app" on
     My Account) alongside its usual <a> links -- strip native button
     chrome so both render identically instead of a button-shaped crumb
     next to link-shaped ones. */
  appearance: none;
  -webkit-appearance: none;
  margin: 0;
  font-family: var(--font-mono);
  font-size: calc(10.5px * var(--fs, 1));
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: #b9c2d6;
  text-decoration: none;
  background: rgba(255,255,255,0.06);
  border: 1px solid rgba(255,255,255,0.25);
  border-radius: var(--radius-sm);
  padding: 5px 11px 5px 8px;
  transition: color .12s ease, border-color .12s ease, background .12s ease, transform .08s ease;
  cursor: pointer;
}
.crumb-back::before {
  content: "";
  width: 5px; height: 5px;
  border-left: 1px solid currentColor;
  border-bottom: 1px solid currentColor;
  transform: rotate(45deg);
  flex: none;
}
.crumb:hover { color: #fff; border-color: var(--brand-blue); background: var(--brand-blue); }
.crumb:active { transform: translateY(1px); }

/* .back-btn is the icon-only "return to Workspace" control that sits to
   the left of the logo. Square and sized to the logo's own height so the
   two line up on the same row, with the app title free to center itself
   vertically in the remaining space (no more stacked crumb+h1 column). */
.back-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 24px;   /* was 28px — thinner header */
  height: 24px;  /* was 28px */
  flex: none;
  font-size: calc(14px * var(--fs, 1));
  font-weight: 700;
  line-height: 1;
  color: #b9c2d6;
  text-decoration: none;
  background: rgba(255,255,255,0.06);
  border: 1px solid rgba(255,255,255,0.25);
  border-radius: var(--radius-sm);
  transition: color .12s ease, border-color .12s ease, background .12s ease, transform .08s ease;
  cursor: pointer;
}
.back-btn:hover { color: #fff; border-color: var(--brand-blue); background: var(--brand-blue); }
.back-btn:active { transform: translateY(1px); }
/* .back-btn itself is now unused (all pages moved to the .home-icon-btn
   button used by Warehouse Cleanup) but left in place in case anything
   still references it. */
.brand-sub {
  margin: 0;
  font-size: calc(12px * var(--fs, 1));
  color: #b9c2d6;
  letter-spacing: 0.005em;
}
.cta { display: flex; align-items: center; gap: 12px; flex: none; }
/* Status text, labels, and buttons inherited from the rest of the app
   assume a light page background — repin the ones that live in the dark
   header so they stay legible here too. */
.topbar .status { color: #b9c2d6; }
.topbar .status.error { color: #ff9a90; }
.topbar .status.ok { color: #79e0a6; }
.topbar .field-label,
.topbar label { color: #b9c2d6; }
.topbar button.primary { border-color: rgba(255,255,255,0.35); }

/* ---------- layout: card grid (2-up on desktop, single column narrow) ---------- */
.stack {
  display: flex;
  flex-direction: column;
  gap: 7px;  /* was 9px — shorter panels */
  min-width: 0;
  padding-bottom: 6px;
}

.card {
  background: var(--surface);
  border: 1px solid var(--border-hi);
  border-top: 3px solid var(--accent);
  border-radius: var(--radius-sm);
  padding: 8px 12px;  /* was 10px 14px — shorter panels */
  box-shadow: var(--shadow-1);
  transition: border-color .18s var(--ease), box-shadow .18s var(--ease), transform .18s var(--ease);
  animation: card-fade-in .38s var(--ease) both;
  animation-delay: calc(var(--card-i, 0) * 55ms);
}
.card:hover { box-shadow: var(--shadow-2); transform: translateY(-1px); }
.card[id] { scroll-margin-top: 24px; }
@keyframes card-fade-in {
  from { opacity: 0; transform: translateY(6px); }
  to { opacity: 1; transform: translateY(0); }
}
.stack > .card:nth-child(1) { --card-i: 0; }
.stack > .card:nth-child(2) { --card-i: 1; }
.stack > .card:nth-child(3) { --card-i: 2; }
.stack > .card:nth-child(4) { --card-i: 3; }
.stack > .card:nth-child(5) { --card-i: 4; }
.stack > .card:nth-child(6) { --card-i: 5; }

.card-top { display: flex; flex-direction: column; }

/* settings cards made of stacked accordions */
.card-stack { display: flex; flex-direction: column; }

/* ---------- three-region layout: nav rail | main column | output rail ---------- */
.layout {
  display: block;
}

.rail { min-width: 0; }
.rail-inner {
  display: flex;
  flex-direction: column;
  gap: 10px;
}

/* rail modules: same material as the main cards, quieter proportions */
.rail-card {
  background: var(--surface);
  border: 1px solid var(--border-hi);
  border-top: 3px solid var(--hazard);
  border-radius: var(--radius-sm);
  padding: 9px 10px 10px;  /* was 11px 12px 12px — shorter panels */
  box-shadow: var(--shadow-1);
}
.rail-card-grow { min-height: 0; display: flex; flex-direction: column; }
.rail-card-grow .results-dock { flex: 1; min-height: 0; }
.rail-card-title {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  font-family: var(--font-mono);
  font-size: calc(10.5px * var(--fs, 1));
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink);
  margin: 0 0 8px;
  padding-bottom: 6px;
  border-bottom: 1px dashed var(--border-hi);
}
.rail-card-title-count {
  font-family: var(--font-mono);
  font-size: calc(11px * var(--fs, 1));
  font-weight: 500;
  letter-spacing: 0;
  text-transform: none;
  color: var(--text-dim);
}

/* left rail: step nav, numerals echo the card-header badges for continuity */
.phase-nav {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.phase-nav a {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 7px 9px;
  border-radius: var(--radius-sm);
  color: var(--text-dim);
  text-decoration: none;
  font-size: calc(12.5px * var(--fs, 1));
  line-height: 1.3;
  transition: color .12s ease, background .12s ease;
}
.phase-nav a:hover { color: var(--text); background: rgba(16, 24, 40, 0.04); }
.phase-nav a.active {
  color: var(--accent-hi);
  background: var(--accent-dim);
}
.phase-nav .phase-num {
  font-family: var(--font-mono);
  font-size: calc(10px * var(--fs, 1));
  font-weight: 600;
  color: var(--accent);
  background: var(--accent-dim);
  border: 1px solid rgba(2, 59, 188, 0.35);
  border-radius: var(--radius-sm);
  padding: 1px 5px;
  flex: none;
  transition: background .12s ease, color .12s ease, border-color .12s ease;
}
.phase-nav a.active .phase-num {
  color: var(--accent-ink);
  background: var(--accent);
  border-color: var(--accent);
}

/* right rail: readiness checklist, same row language as the nav */
.readiness-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.readiness-list li {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 4px 9px;
  border-radius: var(--radius-sm);
  border-left: 3px solid transparent;
  font-family: var(--font-mono);
  font-size: calc(11.5px * var(--fs, 1));
  text-transform: uppercase;
  letter-spacing: 0.02em;
  color: var(--text-mute);
}
.readiness-list .tick {
  font-family: var(--font-mono);
  font-weight: 700;
  font-size: calc(11px * var(--fs, 1));
  width: 16px; height: 16px;
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  color: var(--text-mute);
  border: 1px solid var(--border-hi);
}
.readiness-list li.check-ok { color: var(--text); border-left-color: var(--ok); }
.readiness-list li.check-ok .tick {
  color: #fff;
  background: var(--ok);
  border-color: var(--ok);
  animation: done-badge-pop 0.4s ease-out;
}
.readiness-list .label { flex: 1 1 auto; min-width: 0; }
/* Optional sources sit in the same list but read a notch quieter, with a
   small "optional" tag, so the required six still lead the eye. */
.readiness-list li.check-optional { font-size: calc(11px * var(--fs, 1)); }
.readiness-list .optional-tag {
  flex: none;
  font-family: var(--font-body);
  font-size: calc(9.5px * var(--fs, 1));
  font-weight: 600;
  text-transform: none;
  letter-spacing: 0;
  color: var(--text-mute);
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 1px 6px;
}
.readiness-list:empty::after {
  content: "Add source files to see requirements.";
  display: block;
  font-size: calc(12px * var(--fs, 1));
  color: var(--text-mute);
  padding: 4px 9px;
}

/* "Run" card pre-flight summary — same row language as the readiness list,
   but plain (no ok/pending states): just a recap of what's about to be
   generated, so the card isn't a dead header with nothing under it. */
.run-summary {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.run-summary li {
  display: flex;
  align-items: baseline;
  gap: 8px;
  padding: 6px 9px;
  border-radius: var(--radius-sm);
  font-family: var(--font-mono);
  font-size: calc(11.5px * var(--fs, 1));
}
.run-summary li:nth-child(odd) { background: var(--surface-2); }
.run-summary .run-summary-label {
  color: var(--text-mute);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  font-size: calc(10.5px * var(--fs, 1));
  flex: none;
  width: 132px;
}
.run-summary .run-summary-value { color: var(--ink); flex: 1 1 auto; min-width: 0; }
.run-summary .run-summary-value.is-empty { color: var(--text-mute); font-style: italic; }
.run-summary:empty::after {
  content: "Upload files above to see a summary here.";
  display: block;
  font-size: calc(12px * var(--fs, 1));
  color: var(--text-mute);
  padding: 4px 9px;
}

/* right rail: live output, in normal flow (not floating over dead space) */
.results-dock {
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.rail-empty {
  margin: 0;
  padding: 16px 14px;
  border: 1.5px dashed var(--border-hi);
  border-radius: var(--radius-sm);
  font-family: var(--font-mono);
  font-size: calc(11.5px * var(--fs, 1));
  text-transform: uppercase;
  letter-spacing: 0.02em;
  color: var(--text-mute);
  text-align: center;
}
#resultsEmpty { display: none; }
.results-dock:has(#logOutput.hidden):has(#resultsBox:empty) #resultsEmpty { display: block; }

/* ---------- form controls ---------- */
.row { display: flex; gap: 6px; align-items: center; margin-bottom: 6px; flex-wrap: wrap; }

input[type=text],
input[type=number] {
  background: var(--surface);
  border: 1px solid var(--border-hi);
  color: var(--text);
  padding: 8px 11px;
  border-radius: var(--radius-sm);
  font-size: calc(13px * var(--fs, 1));
  font-family: var(--font-body);
  transition: border-color .18s var(--ease), box-shadow .18s var(--ease);
}
input[type=text] { flex: 1; min-width: 140px; }
input[type=number] { width: 100px; font-family: var(--font-mono); }
table input[type=number] { width: 78px; }

input[type=text]:focus,
input[type=number]:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-dim);
}
input::placeholder { color: var(--text-mute); }

.field-label { min-width: 120px; }
.row-spaced { margin-top: 4px; }
.table-spaced { margin-top: 14px; }

.hint-banner {
  margin: -6px 0 4px;
  padding: 7px 12px;
  background: var(--accent-dim);
  border: 1px solid rgba(2, 59, 188, 0.28);
  border-left: 3px solid var(--accent);
  border-radius: var(--radius-sm);
  color: var(--text-dim);
}

.checkbox-row {
  display: flex;
  align-items: center;
  gap: 7px;
  font-size: calc(12px * var(--fs, 1));
  color: var(--text-dim);
  margin: 7px 0 3px;
  cursor: pointer;
}
.checkbox-row input[type=checkbox] {
  appearance: none;
  width: 15px; height: 15px;
  border: 1px solid var(--border-hi);
  border-radius: var(--radius-sm);
  background: var(--surface);
  cursor: pointer;
  display: inline-grid;
  place-content: center;
  transition: border-color .12s ease, background .12s ease;
}
.checkbox-row input[type=checkbox]:checked {
  background: var(--ink);
  border-color: var(--ink);
}
.checkbox-row input[type=checkbox]:checked::after {
  content: "";
  width: 4px; height: 8px;
  border: solid var(--hazard);
  border-width: 0 2px 2px 0;
  transform: rotate(45deg) translate(-1px, -1px);
}

select {
  background: var(--surface);
  color: var(--text);
  border: 1px solid var(--border-hi);
  padding: 5px 7px;
  border-radius: var(--radius-sm);
  font-size: calc(12px * var(--fs, 1));
  font-family: var(--font-body);
  transition: border-color .18s var(--ease), box-shadow .18s var(--ease);
}
select:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-dim);
}

/* ---------- buttons ---------- */
/* Ink-and-hazard, sharp-edged, uppercase mono — same button language as
   the Warehouse Cleanup app's action buttons. Everything sits on one
   consistent height scale so a row of mixed buttons always lines up. */
button {
  background: var(--surface);
  color: var(--ink);
  border: 1px solid var(--border-hi);
  padding: 7px 13px;
  border-radius: var(--radius-sm);
  cursor: pointer;
  font-family: var(--font-mono);
  font-size: calc(11.5px * var(--fs, 1));
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  line-height: 1.3;
  transition: border-color .18s var(--ease), background .18s var(--ease), color .18s var(--ease), box-shadow .18s var(--ease), transform .12s var(--ease);
}
button:hover { background: var(--ink); color: #fff; box-shadow: var(--shadow-2); transform: translateY(-1px); }
button:active { transform: scale(0.94); box-shadow: var(--shadow-1); transition: transform .12s var(--spring); }

button.secondary {
  background: transparent;
  color: var(--text-dim);
  border-color: var(--border-hi);
}
button.secondary:hover { color: var(--ink); border-color: var(--ink); background: var(--surface-2); }
button:disabled:hover, button.primary:disabled:hover { transform: none; box-shadow: none; }

/* Primary action button — used by every generate/merge button across all
   apps (order-form, intransit, intransit-merge, and any future app). Keyed
   off the class, not an element id, so it works no matter what a given
   app's button happens to be called. */
button.primary {
  background: var(--ink);
  color: var(--hazard);
  border: 1px solid var(--border-hi);
  font-weight: 700;
  padding: 9px 18px;
  font-size: calc(12px * var(--fs, 1));
  box-shadow: var(--shadow-1);
}
button.primary:hover { background: var(--accent-hi); color: #fff; box-shadow: var(--shadow-3); transform: translateY(-2px); }

/* "Download ready" state — same button, swapped to green once a file is
   ready to grab. This is the single source of truth for that green download
   button color; every app should reuse class="primary" + "btn-download-ready"
   rather than inventing a new style. */
button.primary.btn-download-ready {
  background: var(--ok);
  border-color: var(--ok);
  color: #fff;
  box-shadow: var(--shadow-1);
}
button.primary.btn-download-ready:hover { background: #157d44; border-color: #157d44; box-shadow: var(--shadow-2); }
button:disabled,
button.primary:disabled {
  background: var(--surface-2);
  color: var(--border-hi);
  border-color: var(--border);
  box-shadow: none;
  cursor: not-allowed;
}

/* ---------- tables ---------- */
table {
  width: 100%;
  border-collapse: collapse;
  margin: 8px 0;
  font-size: calc(12.5px * var(--fs, 1));
}
th, td {
  text-align: left;
  padding: 7px 7px;
  border-bottom: 1px solid var(--border);
}
th {
  color: #fff;
  font-weight: 700;
  font-family: var(--font-mono);
  font-size: calc(10px * var(--fs, 1));
  letter-spacing: 0.06em;
  text-transform: uppercase;
  background: var(--ink);
}
td { font-variant-numeric: tabular-nums; transition: background .12s var(--ease); }
tr:last-child td { border-bottom: none; }
tr:hover td { background: var(--surface-2); }
.app-nav-icon { transition: transform .18s var(--ease), opacity .12s ease; }
.app-nav-item:hover .app-nav-icon { transform: scale(1.08); }

#requiredFilesTable { font-size: calc(11px * var(--fs, 1)); }
#requiredFilesTable td:first-child { white-space: nowrap; }

/* ---------- status ---------- */
.status { font-family: var(--font-mono); font-size: calc(11.5px * var(--fs, 1)); color: var(--text-dim); }
.status.error { color: var(--err); font-weight: 700; }
.status.ok { color: var(--ok); font-weight: 700; }
/* !important is deliberate: this is a utility "force hidden" class used
   across many different components (progress rings, search dropdowns,
   tab panels, ...), several of which declare their own later `display:`
   rule at the same specificity. Without !important, source order alone
   decides the tie -- so any component whose own display rule happens to
   be declared further down this file silently wins over .hidden and
   never actually hides. That already happened for real (the Order
   Form's supplier search, the home screen's "no results" message, and
   every tab panel each needed their own one-off #id.hidden / .class.hidden
   override elsewhere in this file just to force it back off), and was
   the exact bug behind the Generation Progress ring showing immediately
   on page load on Intransit/Intransit-Branch/Intransit-Merge/
   Intransit-Add-Branch instead of staying hidden until Generate/Merge is
   clicked -- Branch Organizer was the one page that had already been
   patched around it (see templates/organizer.html history), which is
   why it looked "different" from the others. .hidden should just always
   mean hidden, everywhere, full stop -- no more per-page patches needed. */
.hidden { display: none !important; }
.small { font-size: calc(11.5px * var(--fs, 1)); color: var(--text-mute); margin: 5px 0 0; }

/* ---------- disclosures ---------- */
details { margin-top: 7px; border-top: 1px solid var(--border); padding-top: 7px; }
summary {
  cursor: pointer;
  font-size: calc(12.5px * var(--fs, 1));
  font-weight: 500;
  color: var(--text-dim);
  letter-spacing: 0.01em;
  list-style: none;
  display: flex;
  align-items: center;
  gap: 7px;
}
summary::-webkit-details-marker { display: none; }
summary::before {
  content: "";
  width: 0; height: 0;
  border-left: 4px solid var(--text-mute);
  border-top: 3px solid transparent;
  border-bottom: 3px solid transparent;
  transition: transform .12s ease;
}
details[open] > summary::before { transform: rotate(90deg); }
summary:hover { color: var(--accent-hi); }

/* Static section header -- same look as summary (label above a block's
   content) but for sections that are always shown and never meant to be
   collapsed, so no chevron or pointer cursor implying otherwise. */
.section-block { margin-top: 7px; border-top: 1px solid var(--border); padding-top: 7px; }
.section-label {
  font-size: calc(12.5px * var(--fs, 1));
  font-weight: 500;
  color: var(--text-dim);
  letter-spacing: 0.01em;
}

/* ---------- dropzone ---------- */
/* Compact single row — an icon, a line of copy, and a small button. Not a
   big empty box; it should read as one control, not a hero panel. */
.dropzone {
  display: flex;
  align-items: center;
  justify-content: flex-start;
  gap: 10px;
  width: fit-content;
  max-width: 100%;
  border: 2px dashed var(--ink);
  border-radius: var(--radius-sm);
  padding: 8px 14px;
  text-align: center;
  margin: 0 0 10px;
  background: var(--surface-2);
  transition: border-color .15s, background .15s;
}
.dropzone::before {
  content: "";
  flex: none;
  width: 15px; height: 15px;
  background: var(--text-mute);
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4'/%3E%3Cpolyline points='17 8 12 3 7 8'/%3E%3Cline x1='12' y1='3' x2='12' y2='15'/%3E%3C/svg%3E") center / contain no-repeat;
  mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4'/%3E%3Cpolyline points='17 8 12 3 7 8'/%3E%3Cline x1='12' y1='3' x2='12' y2='15'/%3E%3C/svg%3E") center / contain no-repeat;
}
.dropzone::after {
  content: "Drag files here, or";
  font-size: calc(12px * var(--fs, 1));
  color: var(--text-mute);
}
.dropzone.drag-over {
  border-color: var(--accent);
  background: var(--accent-dim);
}
.dropzone button {
  padding: 5px 12px;
  font-size: calc(12px * var(--fs, 1));
}
@media (max-width: 560px) {
  .dropzone { padding: 8px 12px; flex-wrap: wrap; }
  .dropzone::after { display: none; }
}

/* ---------- lists ---------- */
.uploaded-list { margin: 4px 0; max-height: 220px; overflow-y: auto; }
.uploaded-list:empty { margin: 0; }
.uploaded-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px 10px;
  background: var(--surface);
  border: 1px solid var(--border-hi);
  border-left: 4px solid var(--accent);
  border-radius: var(--radius-sm);
  padding: 10px 12px;
  margin-bottom: 6px;
  font-size: calc(13px * var(--fs, 1));
  box-shadow: var(--shadow-1);
}
/* Filename always gets its own full-width line and truncates there, so it
   never has to fight the badge/Remove button for space on narrow columns
   (that's what was crushing names down to a letter or two before). The
   badge + Remove sit together on a second line beneath it. */
.uploaded-row .file-name {
  flex: 1 1 100%;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-weight: 600;
  color: var(--text);
}
.uploaded-row .badge {
  flex: 0 1 auto;
  min-width: 0;
  font-family: var(--font-mono);
  font-weight: 800;
  font-size: calc(11px * var(--fs, 1));
  text-transform: uppercase;
  letter-spacing: 0.04em;
  background: var(--ok-dim);
  color: var(--ok);
  padding: 4px 9px;
  border-radius: var(--radius-sm);
  border: 1px solid rgba(44, 138, 86, 0.4);
  white-space: normal;
  line-height: 1.35;
}
.uploaded-row .badge.unmatched {
  background: var(--err-dim);
  color: var(--err);
  border-color: rgba(179, 65, 58, 0.36);
}
.uploaded-row .badge.note {
  background: var(--accent-dim);
  color: var(--accent);
  border-color: rgba(26, 79, 160, 0.36);
}
.uploaded-row button.remove-file {
  flex: 0 0 auto;
  margin-left: auto;
  background: transparent;
  color: var(--err);
  font-weight: 700;
  font-size: calc(11px * var(--fs, 1));
  text-transform: uppercase;
  letter-spacing: 0.03em;
  border: 1px solid var(--border-hi);
  border-radius: var(--radius-sm);
  padding: 4px 10px;
}
.uploaded-row button.remove-file:hover { background: var(--err-dim); border-color: rgba(179, 65, 58, 0.4); color: var(--err); }

/* Per-row sender assignment (Branch-to-Branch Intransit): a compact label
   + text input inline in the upload row, sized to sit comfortably between
   the filename and the badge instead of fighting either for flex space. */
.uploaded-row .sender-label {
  flex: 0 0 auto;
  font-size: calc(11px * var(--fs, 1));
  color: var(--text-mute);
  white-space: nowrap;
}
.uploaded-row input.sender-input {
  flex: 0 0 auto;
  width: 170px;
  min-width: 0;
  padding: 4px 8px;
  font-size: calc(12px * var(--fs, 1));
  border-width: 1.5px;
  text-transform: uppercase;
}
/* Nested per-file disclosure (e.g. "Destinations (n)") -- lighter than the
   page-level `details` rule so it doesn't read as its own section divider
   inside a compact upload row. */
.uploaded-row-detail {
  margin: -2px 0 5px;
  padding: 0 0 0 9px;
  border-top: none;
}
.uploaded-row-detail summary {
  font-size: calc(11px * var(--fs, 1));
  color: var(--text-mute);
}
.uploaded-row-detail summary:hover { color: var(--accent-hi); }
.uploaded-row-detail p { margin: 4px 0 2px; }

/* History panel (PO Order Form) -- past generations grouped by month,
   same collapsible-section idea as .uploaded-row-detail above. */
.history-months { margin-top: 6px; }
.history-month {
  border: 1px solid var(--border-hi);
  border-radius: var(--radius-sm);
  background: var(--surface-2);
  margin-bottom: 8px;
  overflow: hidden;
}
.history-month summary {
  cursor: pointer;
  list-style: none;
  padding: 9px 12px;
  font-size: calc(12.5px * var(--fs, 1));
  font-weight: 600;
  display: flex;
  align-items: center;
  gap: 6px;
}
.history-month summary::-webkit-details-marker { display: none; }
.history-month summary .small { font-weight: 400; }
.history-month summary:hover { color: var(--accent-hi); }
.history-month-items { border-top: 1px solid var(--border-hi); }
.history-item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  padding: 7px 12px;
  font-size: calc(12px * var(--fs, 1));
}
.history-item + .history-item { border-top: 1px solid var(--border-hi); }
.history-item-main { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.history-item-name {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.history-item-actions { display: flex; align-items: center; gap: 4px; flex: none; }
.history-icon-btn {
  width: 28px;
  height: 28px;
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: var(--radius-sm);
  border: 1px solid var(--border-hi);
  background: var(--surface);
  color: var(--text-dim);
  cursor: pointer;
  transition: background 0.12s, color 0.12s, border-color 0.12s;
}
.history-icon-btn svg { width: 17px; height: 17px; }
.history-icon-btn:hover { background: var(--surface-2); color: var(--accent-hi); border-color: var(--accent-hi); }
.history-icon-btn-disabled { opacity: 0.35; cursor: default; pointer-events: none; }
.history-icon-btn-danger:hover { background: var(--err-dim); color: var(--err); border-color: var(--err); }
.history-icon-btn-danger:disabled { opacity: 0.5; cursor: default; }

#supplierSearch { display: block; width: 100%; box-sizing: border-box; margin-top: 10px; flex: none; }
#supplierSearch.hidden { display: none; }

.supplier-list:empty { display: none; }
.supplier-list {
  max-height: 260px;
  overflow-y: auto;
  border: 1px solid var(--border-hi);
  border-radius: var(--radius-sm);
  padding: 10px 12px;
  margin-top: 10px;
  background: var(--surface-2);
}
.supplier-row {
  display: flex;
  align-items: center;
  gap: 7px;
  padding: 4px 2px;
  font-size: calc(12px * var(--fs, 1));
}
.supplier-row input[type=checkbox] {
  appearance: none;
  width: 14px; height: 14px;
  border: 1px solid var(--border-hi);
  border-radius: var(--radius-sm);
  background: var(--surface);
  cursor: pointer;
  display: inline-grid;
  place-content: center;
}
.supplier-row input[type=checkbox]:checked {
  background: var(--ink);
  border-color: var(--ink);
}
.supplier-row input[type=checkbox]:checked::after {
  content: "";
  width: 3px; height: 6px;
  border: solid var(--hazard);
  border-width: 0 2px 2px 0;
  transform: rotate(45deg) translate(-1px, -1px);
}

/* ---------- results / log ---------- */
.result-file {
  display: flex;
  justify-content: space-between;
  align-items: center;
  background: var(--surface);
  border: 1px solid var(--border-hi);
  border-left: 4px solid var(--ok);
  border-radius: var(--radius-sm);
  padding: 8px 13px;
  margin-top: 6px;
  font-size: calc(12px * var(--fs, 1));
  animation: done-badge-pop 0.3s ease-out;
}
.result-file a { color: var(--accent-hi); text-decoration: none; font-family: var(--font-mono); font-weight: 700; }
.result-file a:hover { text-decoration: underline; }

.log {
  background: #0b0c0d;
  border: 1px solid var(--border-hi);
  border-radius: var(--radius-sm);
  padding: 12px 14px;
  font-family: var(--font-mono);
  font-size: calc(11px * var(--fs, 1));
  color: #9fe3b0;
  max-height: min(46vh, 420px);
  overflow-y: auto;
  white-space: pre-wrap;
  box-shadow: inset 0 0 0 1px rgba(255,255,255,0.04);
}
#resultsBox:not(:empty) {
  display: flex;
  flex-direction: column;
  gap: 6px;
  max-height: min(46vh, 420px);
  overflow-y: auto;
}

/* once the rail has a real fixed height (app-shell mode), the OUTPUT card
   gets its own fixed height instead of stretching to fill the leftover
   column space -- that stretch made it a giant near-empty box before a
   generation ran, and made the log's growth/shrink jump around depending
   on whatever height happened to be left over. A fixed height means the
   card looks and scrolls the same whether it's empty or full of logs. */
@media (min-width: 860px) {
  .rail-card-grow { flex: 0 0 auto; height: 340px; }
  .rail-card-grow .results-dock { display: flex; flex-direction: column; }
  .rail-card-grow .log,
  .rail-card-grow #resultsBox:not(:empty) {
    max-height: none;
    flex: 1;
    min-height: 60px;
  }
}

.topbar + .hazard-bar { margin-top: -14px; margin-bottom: 2px; }

/* ==========================================================================
   Home screen — neumorphic app launcher
   Soft-UI system, scoped entirely to classes prefixed "home-" so it never
   touches the ink-bar/.topbar/.crumb look every other page in the suite
   still uses. One flat base tone, elements defined only by dual soft
   shadows (light top-left, dark bottom-right) — raised at rest, pressed-in
   on hover/active. Built to hold dozens of tools: compact cards, grouped
   sections, a search box, and category pills.
   ========================================================================== */
.home-neu-body {
  background: #eceff4;
  background-image: none;
}
.home-wrap.home-neu {
  max-width: 1680px;
  margin: 0 auto;
  display: flex;
  flex-direction: column;
  gap: 18px;
}
/* Pages that render the shared sidebar (.app-side, injected by
   _topnav.html) get pushed right by the rail's width instead of being
   centered under a top header -- :has() scopes this to only those pages,
   so templates/index.html (its own self-contained header, no sidebar)
   is untouched. Left-anchored rather than centered, and the width cap
   dropped, so modules get the full remaining viewport width instead of
   being boxed into 1680px on wide monitors. */
.home-wrap.home-neu:has(> .app-side) {
  max-width: none;
  margin: 0;
  padding-left: calc(var(--nav-w) + 6px);
  padding-top: var(--header-h); /* clears the fixed .app-header */
}
/* The base `body` rule (near the top of this file) carries a 12px/14px
   top/bottom padding meant for the older, non-sidebar page layouts. It
   leaks through onto every sidebar-based page too, since nothing here
   ever zeroed it back out -- so the document was always ~26px taller
   than the viewport it was actually laid out for. Harmless when nothing
   depended on an exact viewport-height box, but the compact-canvas pages
   below size `.po-canvas` to fill the remaining viewport height exactly,
   so that stray 26px showed up as the whole page being "a little"
   scrollable and, worse, could shift the fixed-position header/sidebar's
   idea of where the canvas sits relative to a scrolled document. Zeroed
   here, scoped to only the sidebar-based pages so older non-sidebar pages
   that still rely on this padding for their own spacing are untouched. */
body:has(> .home-wrap.home-neu > .app-side) { padding: 0; }

/* ---------- header (attached to the sidebar's top-right corner) ----------
   Tall enough to host a page's own toolbar (see .po-strip below, which
   pins itself over this same rect); pages with nothing to put here just
   show it empty, as before. */
.app-header {
  position: fixed;
  top: 0;
  left: var(--nav-w);
  right: 0;
  height: var(--header-h);
  background: var(--neu-light);
  border-bottom: 1px solid var(--neu-edge);
  box-shadow: var(--neu-raised-sm);
  z-index: 30;
}

/* ---------- sidebar (replaces the old fixed top bar) ---------- */
.app-side {
  position: fixed;
  top: 0; left: 0; bottom: 0;
  width: var(--nav-w);
  background: var(--neu-light);
  border-right: 1px solid var(--neu-edge);
  display: flex;
  flex-direction: column;
  padding: 16px 10px;
  z-index: 60;
  box-shadow: var(--neu-raised-sm);
}
.app-side-brand {
  flex: none;
  display: flex;
  align-items: center;
  height: 30px;
  margin: 0 4px 18px;
}
.app-side-brand img { height: 100%; width: auto; object-fit: contain; }

.app-side-nav {
  flex: 1;
  display: flex;
  flex-direction: column;
  gap: 16px;
  overflow-y: auto;
}
.app-side-group { display: flex; flex-direction: column; gap: 2px; }
.app-side-group-toggle {
  display: flex;
  align-items: center;
  justify-content: flex-start;
  width: 100%;
  gap: 8px;
  padding: 6px 8px;
  font: inherit;
  text-align: left;
}
/* Overrides the app-wide `button { ... !important }` pill/panel treatment
   -- these class selectors outrank the plain "button" tag selector, so
   they win despite that rule also using !important. */
.app-side-group-toggle,
.app-side-group-toggle:hover,
.app-side-group-toggle:active {
  background: transparent !important;
  border: none !important;
  box-shadow: none !important;
  border-radius: 0 !important;
  transform: none !important;
}
.app-side-group-label {
  font-family: var(--font-display);
  font-size: calc(10px * var(--fs, 1));
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--neu-mute);
}
.app-side-group-icon { flex: none; width: 15px; height: 15px; display: inline-flex; color: var(--neu-mute); }
.app-side-group-icon svg { width: 15px; height: 15px; }
.app-side-group-toggle:hover .app-side-group-icon { color: var(--neu-blue); }
.app-side-group-toggle:hover .app-side-group-label { color: var(--neu-blue); }
.app-side-group-chevron {
  flex: none;
  width: 12px;
  height: 12px;
  color: var(--neu-mute);
  transition: transform .15s ease;
}
.app-side-group.is-open .app-side-group-chevron { transform: rotate(180deg); }
.app-side-items {
  display: none;
  flex-direction: column;
  gap: 2px;
  padding-left: 14px;
}
.app-side-group.is-open .app-side-items { display: flex; }

.app-side-item {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px;
  border-radius: var(--neu-radius-sm);
  text-decoration: none;
  color: var(--neu-dim);
  font-family: var(--font-display);
  font-size: calc(12.5px * var(--fs, 1));
  font-weight: 600;
  cursor: pointer;
  background: none;
  border: none;
  width: 100%;
  text-align: left;
  transition: background .12s ease, color .12s ease, box-shadow .12s ease;
}
a.app-side-item:hover,
button.app-side-item:hover { background: var(--neu-bg); color: var(--neu-blue); box-shadow: none; }
.app-side-item.is-active { background: var(--neu-bg); color: var(--neu-blue); box-shadow: var(--neu-inset-sm); font-weight: 700; }
.app-side-badge {
  margin-left: auto; flex: none; min-width: 18px; padding: 1px 6px; border-radius: 999px;
  background: var(--neu-blue, #1a4fa0); color: #fff; font-size: calc(10.5px * var(--fs, 1)); font-weight: 700;
  line-height: 16px; text-align: center; font-family: var(--font-mono, monospace);
}
.app-side-badge[hidden] { display: none; }
.app-side-badge.is-overdue { background: #b3413a; }
.app-side-group.is-open .app-side-badge-dot { display: none; }
/* Admin-only "Online" group: one row per person with a status dot, name and the page they're on. */
.app-side-online { margin-top: auto; }
.app-side-online .app-side-items { gap: 1px; max-height: 34vh; overflow-y: auto; }
.app-side-online-count { background: #2c8a56; }
.app-side-presence-row { display: flex; align-items: center; gap: 9px; padding: 5px 8px; border-radius: var(--neu-radius-sm); min-width: 0; }
.app-side-presence-row:hover { background: var(--neu-bg); }
.app-side-presence-dot { flex: none; width: 8px; height: 8px; border-radius: 50%; background: #2c8a56; box-shadow: 0 0 0 3px rgba(44, 138, 86, .18); }
.app-side-presence-row.is-away .app-side-presence-dot { background: #d9a441; box-shadow: 0 0 0 3px rgba(217, 164, 65, .2); }
.app-side-presence-text { display: flex; flex-direction: column; min-width: 0; line-height: 1.25; }
.app-side-presence-name { font-family: var(--font-display); font-size: calc(12.5px * var(--fs, 1)); font-weight: 600; color: var(--neu-dim); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.app-side-presence-name i { font-style: normal; font-weight: 500; color: var(--neu-mute); }
.app-side-presence-row.is-away .app-side-presence-name { opacity: .7; }
.app-side-presence-page { font-size: calc(11px * var(--fs, 1)); color: var(--neu-mute); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.app-side-presence-empty { padding: 4px 8px; font-size: calc(11.5px * var(--fs, 1)); color: var(--neu-mute); }
.app-side-item-soon { opacity: 0.5; cursor: default; }
.app-side-item-soon:hover { background: none; color: var(--neu-dim); }

/* Tabs this account can't open: still listed (so the sidebar looks the same
   for everyone) but dimmed, padlocked and inert -- the link has no href.
   The real lock is the server-side permission gate, not this styling. */
.app-side-item.is-locked { opacity: 0.45; cursor: not-allowed; }
a.app-side-item.is-locked:hover { background: none; color: var(--neu-dim); box-shadow: none; }
.app-side-lock { flex: none; margin-left: auto; width: 12px; height: 12px; color: var(--neu-mute); }
/* The Intransit toggle and the group headers only expand/collapse, so they
   stay clickable even when everything inside is locked -- just dimmed. */
.app-side-sub-toggle.is-locked { cursor: pointer; }
.app-side-group.is-locked .app-side-group-label { opacity: 0.5; }
.app-side-soon-tag {
  margin-left: auto;
  font-family: var(--font-mono);
  font-size: calc(9px * var(--fs, 1));
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--neu-mute);
  background: var(--surface-2);
  border-radius: 999px;
  padding: 1px 6px;
  flex: none;
}

.app-side-item-icon { flex: none; width: 20px; height: 20px; display: inline-flex; color: inherit; }
.app-side-item-icon svg { width: 20px; height: 20px; }
.app-side-item-label { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* ---------- third nav level: a sub-category inside a category ----------
   Only Intransit uses this (WH to Bs / Bs to WH & Bs). The toggle row is
   a normal .app-side-item so it lines up pixel-for-pixel with its
   siblings (Order Form, Organizer); the only additions are a trailing
   chevron and the indented list it reveals.

   The toggle row never takes .is-active -- it isn't a destination. When
   one of its children is the current page it takes .is-current instead,
   which reads as "the thing you're in lives here" without competing with
   the real active row underneath it. */
.app-side-sub { display: flex; flex-direction: column; gap: 2px; }
.app-side-sub-toggle,
.app-side-sub-toggle:hover,
.app-side-sub-toggle:active {
  background: transparent !important;
  border: none !important;
  box-shadow: none !important;
  border-radius: var(--neu-radius-sm) !important;
  transform: none !important;
  /* This row is a <button> (it expands/collapses) while its siblings are <a>
     links, so it picks up the app-wide button look (mono, uppercase, letter-
     spaced). Put it back to exactly the .app-side-item look. (This used to be
     `font: inherit`, which also wiped .app-side-item's own font.) */
  font-family: var(--font-display) !important;
  font-size: calc(12.5px * var(--fs, 1)) !important;
  font-weight: 600 !important;
  letter-spacing: normal !important;
  text-transform: none !important;
  line-height: inherit;
}
button.app-side-sub-toggle:hover { background: var(--neu-bg) !important; color: var(--neu-blue); }
.app-side-sub-toggle.is-current { color: var(--neu-blue); }
.app-side-sub-chevron {
  flex: none;
  margin-left: auto;
  width: 12px;
  height: 12px;
  color: var(--neu-mute);
  transition: transform .15s ease;
}
.app-side-sub.is-open .app-side-sub-chevron { transform: rotate(180deg); }
.app-side-subitems {
  display: none;
  flex-direction: column;
  gap: 2px;
  /* Indented and hung off a hairline so the two reports read as belonging
     to the row above rather than as two more top-level tools. */
  margin-left: 8px;
  padding-left: 9px;
  border-left: 1px solid rgba(23, 32, 48, 0.13);
}
.app-side-sub.is-open .app-side-subitems { display: flex; }
.app-side-subitem { font-size: calc(12px * var(--fs, 1)); padding: 6px 8px; }
.app-side-subitem .app-side-item-icon,
.app-side-subitem .app-side-item-icon svg { width: 18px; height: 18px; }

.app-side-footer { flex: none; margin-top: 8px; padding-top: 10px; border-top: 1px solid var(--neu-edge); position: relative; }
.app-side-account-btn,
.app-side-account-btn:hover,
.app-side-account-btn:active {
  background: transparent !important;
  border: none !important;
  box-shadow: none !important;
  border-radius: var(--neu-radius-sm) !important;
  transform: none !important;
  font: inherit;
}
.app-side-account-btn:hover { background: var(--neu-bg) !important; color: var(--neu-blue); }

/* Settings menu now lives in the sidebar footer instead of a top-right
   corner -- reuses .home-settings-item rows as-is, just repositioned to
   open upward/rightward so it clears the bottom of the viewport and
   doesn't get clipped by the narrow rail. */
.app-side-settings-menu {
  top: auto !important;
  bottom: calc(100% + 8px);
  left: 0;
  right: auto;
}
/* Fit the menu to the sidebar footer instead of its 220px minimum: it was wider than the rail, so the sidebar
   clipped the right edge ("Sir C", "About this ap", "Log ou"). Rows also stack icon + label from the left
   (they were space-between, which flung every label to the clipped edge). Long names truncate with an ellipsis. */
.home-settings-menu.app-side-settings-menu { left: 0; right: 0; min-width: 0; box-sizing: border-box; }
.app-side-settings-menu .home-settings-item { justify-content: flex-start; text-align: left; min-width: 0; }
.app-side-settings-menu .home-settings-item > span { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* Icon-only rail once the window gets tight -- still one click away from
   every tool, just without the label text. Same breakpoint/variable the
   (currently unused) legacy .app-nav rail used. */
@media (max-width: 900px) {
  :root { --nav-w: var(--nav-w-icon); }
  .app-side { padding: 16px 6px; align-items: center; }
  .app-side-brand { justify-content: center; margin: 0 0 18px; }
  .app-side-nav { align-items: center; width: 100%; }
  .app-side-group { align-items: center; width: 100%; }
  .app-side-group-toggle { display: none; }
  .app-side-items { display: flex; }
  .app-side-online { display: none; }
  .app-side-item-label,
  .app-side-soon-tag,
  .app-side-lock { display: none; }
  .app-side-item { justify-content: center; width: auto; position: relative; }
  /* icon-only rail: the count sits on the icon's corner instead of beside a label */
  .app-side-item .app-side-badge { position: absolute; top: -2px; right: -4px; margin: 0; min-width: 15px; padding: 0 4px; font-size: calc(9.5px * var(--fs, 1)); line-height: 15px; }
  .app-side-footer { display: flex; justify-content: center; }
  .app-side-settings-menu { left: 4px; }
  .home-settings-menu.app-side-settings-menu { right: auto; min-width: 200px; }   /* icon-only rail has no width to fit to */

  /* There's no room to nest at icon-only width, and no label to nest
     under -- so the Intransit sub-category flattens: its own toggle row
     disappears (it was only ever a label + chevron) and its two reports
     sit inline with the other tool icons, exactly as they did before
     the sub-category existed. */
  .app-side-sub { align-items: center; width: 100%; }
  .app-side-sub-toggle { display: none; }
  .app-side-subitems {
    display: flex;
    align-items: center;
    margin-left: 0;
    padding-left: 0;
    border-left: none;
  }
  .app-side-subitem { padding: 8px; }
  .app-side-subitem .app-side-item-icon,
  .app-side-subitem .app-side-item-icon svg { width: 20px; height: 20px; }
}

/* ---------- Warehouse Cleanup board: clear the fixed rail + header ----------
   That page is a full-bleed flex column (html/body are height:100%,
   overflow:hidden) and it includes the shared sidebar, but only the
   sidebar itself sits inside .home-wrap -- the floor map is a body-level
   sibling. The .home-wrap padding rule near the top of this section
   therefore never reached it, so the map started at x=0/y=0 and spent
   its first 172x36px underneath the rail and the header strip.

   Padding goes on the map rather than on <body>: the
   `body:has(> .home-wrap.home-neu > .app-side) { padding: 0 }` rule
   above carries three classes inside its :has(), which outranks any
   plain body.<class> selector no matter where it sits in the file.
   The rest of that page's chrome (legend, bulk toolbar, marquee) is
   position:fixed to the bottom/right/center and already clears both. */
body.wh-shell > .map-full {
  padding-left: calc(var(--nav-w) + 20px);
  padding-top: calc(var(--header-h) + 14px);
}

/* soft-shadow tokens, local to the home screen only */
.home-neu {
  --neu-bg:      #eceff4;
  --neu-light:   #ffffff;
  --neu-dark:    #b6c0d1;
  --neu-ink:     #121826;
  --neu-dim:     #333c4b;
  --neu-mute:    #444d5b; /* was #8a93a6 — 3.1:1 on white, unreadable; .home-neu re-points
                             --text-mute to this on nearly every page, so this one token
                             controlled most of the app's "faint" secondary text */
  --neu-blue:    #1a4fa0;
  --neu-blue-ink: #ffffff;
  --neu-ok:      #1f9d55;
  --neu-ok-bg:   rgba(31, 157, 85, 0.12);
  --neu-radius:  8px;   /* was 16px — flattened for the corporate pass */
  --neu-radius-sm: 6px;  /* was 12px */
  --neu-raised:  1px 1px 3px var(--neu-dark), -1px -1px 3px var(--neu-light);
  --neu-raised-sm: 1px 1px 2px var(--neu-dark), -1px -1px 2px var(--neu-light);
  --neu-pressed: inset 2px 2px 4px var(--neu-dark), inset -2px -2px 4px var(--neu-light);
  --neu-pressed-lg: inset 2px 2px 5px var(--neu-dark), inset -2px -2px 5px var(--neu-light);

  /* Companion tokens for pieces of the home-neu screens (PO Order Form's
     .po-* cards, the summary modal, results strip, etc.) that were built
     against the *global* design tokens (--surface/--border/--text/--accent/
     --shadow-*) instead of these local --neu-* ones. Those globals only
     ever get their value baked in from :root, so anything using them
     stayed pale without this. Defining values for them here, right where
     --neu-ink/--neu-dim/etc. already live, is enough on its own. */
  --neu-border:     rgba(23, 32, 48, 0.32);
  --neu-border-hi:  rgba(23, 32, 48, 0.32);
  --neu-err:        #b3413a;
  --neu-err-bg:     rgba(179, 65, 58, 0.08);
  --neu-warn:       #ad7c50;
  --neu-warn-bg:    rgba(173, 124, 80, 0.12);
  --neu-accent-hi:  #123b7d;
  --neu-accent-dim: rgba(26, 79, 160, 0.08);

  /* Re-point the shared tokens to the local ones above. Because custom
     properties resolve var() at the element the *consuming* declaration
     sits on, redeclaring them here (on .home-neu itself) makes every
     var(--surface)/var(--text)/etc. used by descendants re-resolve against
     THIS scope's --neu-* values instead of the frozen value inherited
     from :root. */
  --surface:    var(--neu-bg);
  --surface-2:  var(--neu-bg);
  --border:     var(--neu-border);
  --border-hi:  var(--neu-border-hi);
  --text:       var(--neu-ink);
  --text-dim:   var(--neu-dim);
  --text-mute:  var(--neu-mute);
  --accent:     var(--neu-blue);
  --accent-hi:  var(--neu-accent-hi);
  --accent-dim: var(--neu-accent-dim);
  --ok:         var(--neu-ok);
  --ok-dim:     var(--neu-ok-bg);
  --err:        var(--neu-err);
  --err-dim:    var(--neu-err-bg);
  --warn:       var(--neu-warn);
  --warn-dim:   var(--neu-warn-bg);
  --shadow-1:   var(--neu-raised-sm);
  --shadow-2:   var(--neu-raised);
  --shadow-3:   var(--neu-raised);

  /* `color` is an inherited property, but inheritance passes down the
     already-resolved value, not a live var() formula. Plain text with no
     color rule of its own (table cells, list-item labels, etc.) was
     therefore inheriting whatever the <body> tag resolved once, at the top
     of the page, using the *global* --text -- never this scope's. Setting
     it again here, explicitly, forces that plain text to inherit THIS
     scope's fresh value instead. */
  color: var(--text);
}

/* ==========================================================================
   Compact topbar shell — home.html ONLY.
   All new class names (wsnav-*) so nothing here touches .home-topbar /
   .home-cta / .home-settings / .home-card* etc., which every other tool
   page's own in-page header still relies on unchanged. The #homeTopbar /
   #homeHeaderSpacer IDs are kept so the shared header-height-sync script
   in home.js keeps working without any change to that logic, and the
   avatar trigger reuses #homeSettings / #homeSettingsBtn / #homeSettingsMenu
   (and the .home-settings-item family) so the existing dropdown JS and
   "About this app" modal wiring all work unmodified.
   ========================================================================== */
.wsnav-bar {
  position: fixed;
  top: 0; left: 0; right: 0;
  z-index: 60;
  background: var(--neu-light);
  border-bottom: 1px solid var(--neu-border);
}
.wsnav-bar-inner {
  max-width: 1680px;
  margin: 0 auto;
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 0 18px;
  min-height: 40px;
}
.wsnav-logo {
  flex: none;
  width: auto;
  height: 30px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  margin-right: 26px;
}
.wsnav-logo img { width: 100%; height: 100%; object-fit: contain; }

.wsnav-nav { display: flex; align-items: center; gap: 22px; flex: 1; flex-wrap: wrap; }
.wsnav-cat { position: relative; }
.wsnav-cat-btn {
  position: relative;
  font-family: var(--font-display);
  font-size: calc(11.5px * var(--fs, 1));
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--neu-dim);
  background: none;
  border: none;
  box-shadow: none;
  padding: 12px 1px;
  cursor: pointer;
  transition: color .12s ease;
}
.wsnav-cat-btn::after {
  content: "";
  position: absolute;
  left: 0; right: 0; bottom: 0;
  height: 2px;
  background: var(--brand-blue);
  transform: scaleX(0);
  transition: transform .15s ease;
}
/* Hover: color shift only, no underline, no box. */
.wsnav-cat-btn:hover { color: var(--brand-blue); }
/* Active / current-section state (dropdown open): color + underline —
   the only "container-like" element allowed on a tab. */
.wsnav-cat.is-open .wsnav-cat-btn { color: var(--brand-blue); }
.wsnav-cat.is-open .wsnav-cat-btn::after { transform: scaleX(1); }

/* The shared neumorphic `button` rules (background/border/box-shadow,
   plus the button:hover lift) target every <button> in the app, including
   these tab triggers, which made them render as raised/pressable boxes.
   Tabs are plain text labels — strip that box treatment back out in every
   state so only color + the ::after underline remain. */
.wsnav-cat-btn,
.wsnav-cat-btn:hover,
.wsnav-cat-btn:active,
.wsnav-cat.is-open .wsnav-cat-btn {
  background: transparent !important;
  border: none !important;
  box-shadow: none !important;
  border-radius: 0 !important;
  transform: none !important;
}

.wsnav-dropdown {
  position: absolute;
  top: calc(100% + 2px);
  left: -6px;
  min-width: 208px;
  display: flex;
  flex-direction: column;
  gap: 1px;
  background: var(--neu-light);
  border-radius: var(--neu-radius-sm);
  box-shadow: var(--neu-raised);
  padding: 6px;
  z-index: 70;
  opacity: 0;
  visibility: hidden;
  transform: translateY(-6px);
  pointer-events: none;
  transition: opacity .14s ease, transform .14s ease, visibility 0s linear .14s;
}
.wsnav-cat.is-open .wsnav-dropdown {
  opacity: 1;
  visibility: visible;
  pointer-events: auto;
  transform: translateY(0);
  transition: opacity .14s ease, transform .14s ease;
}
.wsnav-dd-item {
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 7px 9px;
  border-radius: var(--neu-radius-sm);
  text-decoration: none;
  color: var(--neu-ink);
  font-family: var(--font-body);
  font-size: calc(12.5px * var(--fs, 1));
  font-weight: 600;
  transition: background .12s ease, box-shadow .12s ease, color .12s ease;
}
a.wsnav-dd-item:hover { background: var(--neu-bg); box-shadow: var(--neu-raised-sm); color: var(--brand-blue); }
.wsnav-dd-icon {
  flex: none;
  width: 21px;
  height: 21px;
  border-radius: 6px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  color: #fff;
}
.wsnav-dd-icon svg { width: 14px; height: 14px; }
.wsnav-dd-icon.is-blue { background: var(--brand-blue); }
.wsnav-dd-icon.is-blue-light { background: var(--brand-blue-light); }
.wsnav-dd-icon.is-violet { background: var(--violet); }
.wsnav-dd-icon.is-teal { background: var(--teal); }
.wsnav-dd-icon.is-muted { background: var(--neu-mute); }

.wsnav-dd-item-soon {
  cursor: default;
  opacity: 0.62;
  justify-content: space-between;
}
.wsnav-dd-soon-tag {
  flex: none;
  font-family: var(--font-body);
  font-size: calc(9.5px * var(--fs, 1));
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--neu-mute);
  background: var(--neu-bg);
  box-shadow: var(--neu-pressed);
  border-radius: 999px;
  padding: 2px 8px;
}

.wsnav-profile { position: relative; flex: none; }
.home-icon-btn.wsnav-avatar-btn { width: 34px; height: 34px; }

/* ---------- idle canvas ---------- */
.wsnav-spacer { width: 100%; flex: none; }
.wsnav-canvas {
  min-height: calc(100vh - 40px);
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--neu-bg);
  border-radius: var(--neu-radius-sm);
}
.wsnav-canvas-logo {
  width: 190px;
  max-width: 42vw;
  opacity: 0.92;
}

/* ---------- Home tab placeholder: mascot + "still building" note ---------- */
.wsnav-canvas-dev { flex-direction: column; gap: 14px; text-align: center; }
/* Canonical mascot size -- the ONE size every Cogsy (lyr_bot(),
   styled in nav-loading.js) appearance uses, whether it's alone on the
   dashboard placeholder or sitting in a tool page's idle canvas
   (see .po-canvas-idle below). Do not size a mascot any other way;
   add this class instead so they all stay visually identical. */
.wsnav-canvas-mascot {
  width: 210px;
  max-width: 55vw;
  height: auto;
}
.wsnav-canvas-greeting {
  margin: 0;
  font-size: calc(14px * var(--fs, 1));
  color: var(--neu-text-soft, #434c5a);
  max-width: 320px;
}

@media (max-width: 640px) {
  .wsnav-bar-inner { padding: 0 14px; gap: 4px; }
  .wsnav-logo { margin-right: 14px; }
  .wsnav-nav { gap: 14px; }
  .wsnav-cat-btn { font-size: calc(10.5px * var(--fs, 1)); }
  .wsnav-dropdown { left: 0; min-width: 190px; }
}

/* ---------- in-page sub-navigation (e.g. Intransit's Merge months /
   Add branch, relocated off the old topbar dropdown) ---------- */
.wsnav-subtabs { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin: 0 0 var(--space-3); }
.wsnav-subtab {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-family: var(--font-body);
  font-size: calc(12px * var(--fs, 1));
  font-weight: 600;
  color: var(--text-dim);
  text-decoration: none;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 999px;
  padding: 6px 13px;
  transition: color .12s ease, border-color .12s ease;
}
.wsnav-subtab svg { width: 15px; height: 15px; flex: none; }
.wsnav-subtab:hover { color: var(--accent); border-color: var(--accent); }

/* ---------- top bar ----------
   Fixed to the viewport so it never scrolls away — brand, search+filters,
   and account/settings all live in one row to save vertical space. Height
   isn't fixed in CSS (it can wrap on narrow screens), so home.js measures
   it and sizes .home-header-spacer to match, keeping content from sliding
   under it. */
.home-topbar {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 60;
  background: var(--neu-bg);
  box-shadow: var(--neu-raised);
}
.home-topbar-inner {
  max-width: 1680px;
  margin: 0 auto;
  display: flex;
  align-items: center;
  gap: 10px 18px;
  flex-wrap: wrap;
  padding: 8px 18px;  /* was 12px — thinner header */
}
.home-header-spacer { width: 100%; flex: none; }

.home-brand { display: flex; align-items: center; gap: 12px; min-width: 0; flex: none; }
.home-logo-badge {
  flex: none;
  width: 36px;   /* was 44px — thinner header */
  height: 36px;  /* was 44px */
  border-radius: 50%;
  background: var(--neu-bg);
  box-shadow: var(--neu-raised-sm);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 5px;  /* was 7px */
}
.home-logo-badge img { width: 100%; height: 100%; object-fit: contain; }
.home-brand-text { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.home-brand-text h1 {
  margin: 0;
  font-family: var(--font-display);
  font-size: calc(18px * var(--fs, 1));
  font-weight: 700;
  text-transform: none;
  letter-spacing: normal;
  color: var(--neu-ink);
  white-space: nowrap;
}
.home-brand-sub { margin: 0; font-size: calc(12px * var(--fs, 1)); color: var(--neu-mute); white-space: nowrap; }

.home-cta { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; flex: none; margin-left: auto; }

.home-icon-btn {
  flex: none;
  width: 42px;
  height: 42px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  background: var(--neu-bg);
  box-shadow: var(--neu-raised-sm);
  border: none;
  padding: 0;
  font-family: var(--font-body);
  text-transform: none;
  letter-spacing: normal;
  color: var(--neu-mute);
  cursor: pointer;
  transition: box-shadow .15s ease, color .15s ease, transform .1s ease;
}
.home-icon-btn svg { width: 21px; height: 21px; }
.home-icon-btn:hover { color: var(--neu-blue); background: var(--neu-bg); box-shadow: var(--neu-raised-sm); transform: none; }
.home-icon-btn:active { box-shadow: var(--neu-pressed); background: var(--neu-bg); transform: none; }

.home-pill {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  font-family: var(--font-body);
  font-size: calc(13px * var(--fs, 1));
  font-weight: 600;
  color: var(--neu-ink);
  text-decoration: none;
  background: var(--neu-bg);
  box-shadow: var(--neu-raised-sm);
  border-radius: 999px;
  padding: 8px 16px;
  cursor: pointer;
  transition: box-shadow .15s ease, color .15s ease;
}
.home-pill-icon { width: 17px; height: 17px; flex: none; color: var(--neu-blue); }
.home-pill:hover { color: var(--neu-blue); }
.home-pill:active { box-shadow: var(--neu-pressed); }
.home-pill-user { color: var(--neu-blue); font-weight: 700; }
.home-pill-muted { color: var(--neu-mute); }
.home-pill-muted .home-pill-icon { color: var(--neu-mute); }

/* ---------- search row ----------
   Now lives inside the fixed header, between the brand and the account/
   settings cluster, so it takes up the header's spare width instead of
   its own separate row. */
.home-search-row {
  display: flex;
  align-items: center;
  gap: 14px;
  flex-wrap: wrap;
  flex: 1 1 420px;
  min-width: 0;
}
.home-search-box {
  flex: 1 1 220px;
  min-width: 160px;
  display: flex;
  align-items: center;
  gap: 10px;
  background: var(--neu-bg);
  box-shadow: var(--neu-pressed);
  border-radius: 999px;
  padding: 9px 18px;
}
.home-search-icon { width: 19px; height: 19px; flex: none; color: var(--neu-mute); }
.home-search-input {
  flex: 1;
  border: none;
  background: none;
  outline: none;
  font-family: var(--font-body);
  font-size: calc(14px * var(--fs, 1));
  color: var(--neu-ink);
}
.home-search-input::placeholder { color: var(--neu-mute); }

.home-filter-pills { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.home-pill-filter {
  font-family: var(--font-body);
  font-size: calc(12.5px * var(--fs, 1));
  font-weight: 600;
  text-transform: none;
  letter-spacing: normal;
  color: var(--neu-dim);
  background: var(--neu-bg);
  box-shadow: var(--neu-raised-sm);
  border: none;
  border-radius: 999px;
  padding: 9px 16px;
  cursor: pointer;
  transition: box-shadow .15s ease, color .15s ease, background .15s ease;
  white-space: nowrap;
}
.home-pill-filter:hover { background: var(--neu-bg); transform: none; box-shadow: var(--neu-raised-sm); }
.home-pill-filter:active { transform: none; }
.home-pill-filter:hover { color: var(--neu-blue); }
.home-pill-filter.is-active {
  color: var(--neu-blue-ink);
  background: var(--neu-blue);
  box-shadow: 3px 3px 8px var(--neu-dark), -2px -2px 6px var(--neu-light);
}

/* ---------- settings dropdown (About / Manage accounts / Log out) ---------- */
.home-settings { position: relative; flex: none; }

.home-settings-menu {
  position: absolute;
  top: calc(100% + 10px);
  right: 0;
  min-width: 220px;
  display: flex;
  flex-direction: column;
  gap: 2px;
  background: var(--neu-bg);
  border-radius: var(--neu-radius-sm);
  box-shadow: var(--neu-raised);
  padding: 8px;
  z-index: 70;
  opacity: 0;
  transform: translateY(-6px) scale(0.98);
  pointer-events: none;
  transition: opacity .14s ease, transform .14s ease;
}
.home-settings.is-open .home-settings-menu {
  opacity: 1;
  transform: translateY(0) scale(1);
  pointer-events: auto;
}

/* Some of these menu rows (About this app) are real <button>
   elements, which a later app-wide rule ("button, .btn-icon, ...") stamps
   with a raised-card look via !important. Match that specificity here so
   every row -- button or <a> -- reads the same flat way at rest, and only
   picks up a shadow on hover. */
.home-settings-item,
button.home-settings-item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  width: 100%;
  font-family: var(--font-body);
  font-size: calc(13px * var(--fs, 1));
  font-weight: 600;
  text-transform: none;
  letter-spacing: normal;
  text-decoration: none;
  color: var(--neu-ink);
  background: none !important;
  border: none !important;
  box-shadow: none !important;
  border-radius: var(--radius-sm, 10px) !important;
  padding: 9px 10px;
  cursor: pointer;
  text-align: left;
}
.home-settings-item:hover { background: var(--neu-bg) !important; box-shadow: var(--neu-raised-sm) !important; color: var(--neu-blue); }
.home-settings-item:hover .home-settings-item-icon { color: var(--neu-blue); }
.home-settings-item-icon { width: 18px; height: 18px; flex: none; color: var(--neu-mute); }
.home-settings-item-label { display: inline-flex; align-items: center; gap: 10px; }
.home-settings-item-danger:hover { color: var(--err, #c0392b); }
.home-settings-item-danger:hover .home-settings-item-icon { color: var(--err, #c0392b); }

.home-settings-divider {
  height: 1px;
  margin: 6px 4px;
  background: var(--neu-dark);
  opacity: 0.35;
}

/* ---------- content ---------- */
.home-grid-wrap { display: flex; flex-direction: column; gap: 22px; }
.home-card-group.is-hidden { display: none; }
.home-group-label {
  margin: 0 0 12px;
  font-family: var(--font-body);
  font-size: calc(12px * var(--fs, 1));
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--neu-mute);
}
.home-card-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(190px, 1fr));
  gap: 16px;
}

/* ---------- Home dashboard (greeting + live-numbers card grid) ----------
   Width matches .po-shell's own 1320px cap (its immediate parent already
   enforces that ceiling) so the dashboard fills the same page width every
   other tool page uses, rather than the old 960px cap that stranded a few
   hundred px of dead space on anything wider than a laptop. */
.home-dash {
  width: 100%;
  max-width: 1320px;
  margin: 0 auto;
  padding: 12px var(--space-4, 16px) 18px;
  display: flex;
  flex-direction: column;
  gap: var(--space-4, 16px);
}
.home-dash-greeting h1 {
  margin: 0 0 5px;
  font-family: var(--font-display);
  font-weight: 600;
  font-size: calc(27px * var(--fs, 1));
  letter-spacing: -0.01em;
  text-transform: none; /* the app-wide h1 rule shouts this in caps; a
    conversational greeting reads better in normal case */
  color: var(--text);
}
.home-dash-date {
  margin: 0;
  font-family: var(--font-mono);
  font-size: calc(12px * var(--fs, 1));
  font-weight: 600;
  letter-spacing: 0.03em;
  text-transform: uppercase;
  color: var(--text-mute, var(--neu-mute));
}

/* Three weighted groups instead of one flat grid: Today (act now), By the
   numbers (operational KPIs), Snapshot (background business state) -- see
   the matching comment in dashboard.html for why these three and in this
   order. Each section gets its own eyebrow label so the page reads as
   grouped information instead of seven equal-weight boxes. */
.home-dash-sections {
  display: flex;
  flex-direction: column;
  gap: 14px;
}
/* Eyebrow gets a small accent tick so each section reads as a distinct
   group at a glance, instead of just another line of small caps text. */
.home-dash-eyebrow {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 8px !important;
}
.home-dash-eyebrow::before {
  content: "";
  width: 12px;
  height: 2px;
  border-radius: 2px;
  background: var(--accent);
  flex: none;
}
/* Base card lift for every home-dash card: softer radius + a shadow with
   actual depth, so cards separate from the page instead of blending into
   it behind a 1px border. Padding trimmed below the shared .po-card
   default -- seven cards need to fit the fold without scrolling more
   than one clean row's worth. */
.home-dash-card {
  border-radius: 12px;
  border-color: var(--border);
  padding: 10px 16px 12px;
  box-shadow: 0 1px 2px rgba(16,24,40,.04), 0 6px 20px -4px rgba(16,24,40,.08);
  transition: box-shadow .18s var(--ease, ease), transform .18s var(--ease, ease);
}
.home-dash-card:hover {
  box-shadow: 0 2px 4px rgba(16,24,40,.05), 0 14px 32px -6px rgba(16,24,40,.13);
}
.home-dash-card .po-card-title-row { margin-bottom: 4px; }
/* Per-card icon tint, keyed off each card's own id (already unique in
   dashboard.html) -- small color cue so the seven cards are easier to
   tell apart at a glance instead of all sharing one flat accent chip. */
#runDayCard .po-card-icon { background: var(--accent-dim); color: var(--accent); }
#dispatchKpiCard .po-card-icon { background: var(--violet-dim, rgba(124,92,224,.12)); color: var(--violet); }
#receivingKpiCard .po-card-icon { background: var(--teal-dim, rgba(14,165,168,.12)); color: var(--teal); }
#settlementKpiCard .po-card-icon { background: var(--accent-dim); color: var(--accent); }
#salesSnapshotCard .po-card-icon { background: var(--ok-dim); color: var(--ok); }
#warehouseProgressCard .po-card-icon { background: var(--accent-dim); color: var(--accent); }
/* Today: the two actionable "things to do" cards. Right column (Best Day
   to Run) gets exactly 1/3 of the row -- same 2fr:1fr ratio as Snapshot
   above -- so its left edge lines up with Warehouse Cleanup Progress's
   and Settlement Overview's below, instead of each row being sized
   independently. Attention Today still gets the larger 2fr share since
   it's the one with an actual list that grows; Best Day to Run is a
   single line and doesn't need more than its third. */
.home-dash-row-today {
  display: grid;
  grid-template-columns: minmax(0, 2fr) minmax(0, 1fr);
  gap: var(--space-3, 12px);
  align-items: stretch;
}
@media (max-width: 860px) {
  .home-dash-row-today { grid-template-columns: 1fr; }
}

/* By the numbers: was a full-width stack (3 tall cards = a lot of
   scrolling); now a responsive grid so cards sit side by side and the
   stat grid inside each just wraps to a second row of cells instead of
   the whole card claiming a full row of vertical space. Stretched (not
   "start") so the three cards match height in a row: the discrepancy
   cards carry up to 10 stat cells and the Settlement card only 4, so
   left un-stretched their KPI grids wrap to a different number of rows
   and the cards end at different heights, leaving ragged bottoms. */
.home-dash-row-overview {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
  gap: var(--space-3, 12px);
  align-items: stretch;
}
.home-dash-row-overview .home-dash-card {
  display: flex;
  flex-direction: column;
}
/* The stat grid grows to fill whatever height the tallest card in the
   row claims, and its own rows share out that extra space evenly, so a
   card with fewer, wider cells (Settlement's hero variant) fills the
   same footprint instead of floating at the top with dead space below. */
.home-dash-row-overview .home-dash-stat-grid {
  flex: 1;
  align-content: stretch;
}

/* Snapshot: the two lighter "state of the business" cards. Right column
   (Warehouse Cleanup Progress) is sized to the same 1/3 share Settlement
   Overview occupies in the 3-column "By the numbers" row below, via the
   same 2fr:1fr ratio Today uses -- see .home-dash-row-today for why, and
   why this specific ratio -- so that column's left edge lines up with
   Best Day to Run's and Settlement Overview's instead of each row
   splitting to its own independent fraction. Stacks below ~640px. */
.home-dash-row-snapshot {
  display: grid;
  grid-template-columns: minmax(0, 2fr) minmax(0, 1fr);
  gap: var(--space-3, 12px);
}
.home-dash-row-snapshot .home-dash-card { min-width: 0; }
@media (max-width: 640px) {
  .home-dash-row-snapshot { grid-template-columns: 1fr; }
}

/* Attention Today's emphasis treatment: a colored edge + tinted icon that
   tracks the same data-state dashboard-home.js already sets ("attention"
   vs "caught-up"), so it reads as the one card to look at first when
   there's something waiting, and settles down once it's clear. */
.home-dash-card-primary {
  border-left: 3px solid var(--border-hi);
  transition: border-color .2s ease, background .2s ease;
}
.home-dash-card-primary .po-card-title-row h2 { font-size: calc(16.5px * var(--fs, 1)); }
.home-dash-card-primary[data-state="attention"] {
  border-left-color: var(--warn);
  background: linear-gradient(180deg, var(--warn-dim), var(--surface) 60%);
}
.home-dash-card-primary[data-state="attention"] .po-card-icon {
  background: var(--warn-dim);
  color: var(--warn);
}
.home-dash-card-primary[data-state="caught-up"] { border-left-color: var(--ok); }
.home-dash-card-primary[data-state="caught-up"] .po-card-icon {
  background: var(--ok-dim);
  color: var(--ok);
}
.home-dash-card-body {
  margin: 0;
  font-family: var(--font-body);
  font-size: calc(14px * var(--fs, 1));
  color: var(--text-dim, var(--text));
}
.home-dash-card[data-state="ready"] .home-dash-card-body { color: var(--accent); font-weight: 600; }
.home-dash-card[data-state="error"] .home-dash-card-body { color: var(--danger, #c0392b); }

/* Loading skeleton, shared by all seven cards. Reuses the same shimmer
   look/keyframes as the tool pages' .is-booting/.skel-target convention
   (see the "Skeletons" rules further down this file) rather than inventing
   a second style -- these cards just toggle it off each card's own
   data-state instead of one page-wide .is-booting class, since they load
   independently rather than as a single boot sequence.
   - `.skel-target` on an inline value (a KPI number, the run-day message,
     a sales figure) shimmers in place while its card is loading.
   - `.skel-rows.skel-block` is for cards whose real content stays
     `hidden` until data arrives (Attention Today, Sales Snapshot,
     Warehouse Cleanup Progress) -- without this they're just a blank
     card body until the fetch resolves. `.skel-block` itself already
     defaults to `display: none` globally; this only turns it back on for
     the loading state. */
.home-dash-card[data-state="loading"] .skel-target {
  position: relative;
  color: transparent !important;
  background: linear-gradient(
    90deg,
    rgba(23, 32, 48, 0.07) 25%,
    rgba(23, 32, 48, 0.13) 37%,
    rgba(23, 32, 48, 0.07) 63%
  );
  background-size: 400% 100%;
  border-radius: var(--radius-sm);
  animation: skel-sweep 1.4s ease-in-out infinite;
  user-select: none;
  display: inline-block;
  min-width: 1.6em;
}
.home-dash-card[data-state="loading"] .skel-rows.skel-block { display: grid; }
@media (prefers-reduced-motion: reduce) {
  .home-dash-card[data-state="loading"] .skel-target,
  .home-dash-card[data-state="loading"] .skel-rows.skel-block .skel {
    animation: none;
    background-position: 0 50%;
  }
}

/* Card #2: "Attention Today" -- up to three stat rows, each a link. */
.home-dash-stat-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.home-dash-stat-row[hidden] { display: none; }
.home-dash-stat-link {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 10px;
  margin: 0 -10px;
  border-radius: var(--radius-sm, 8px);
  text-decoration: none;
  color: inherit;
  transition: background .12s ease, transform .12s ease;
}
.home-dash-stat-link:hover { background: var(--surface-2); transform: translateX(1px); }
/* Number gets its own pill instead of sitting bare in the line, and an
   arrow fades in on hover so each row reads as "go here", not just text. */
.home-dash-stat-n {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 24px;
  height: 24px;
  padding: 0 7px;
  border-radius: 999px;
  background: var(--accent-dim);
  font-family: var(--font-mono);
  font-weight: 700;
  font-size: calc(13px * var(--fs, 1));
  color: var(--accent);
  flex: none;
}
.home-dash-stat-label {
  font-family: var(--font-body);
  font-size: calc(13.5px * var(--fs, 1));
  color: var(--text-dim, var(--text));
  flex: 1;
}
.home-dash-stat-link::after {
  content: "\2192";
  color: var(--text-mute, var(--neu-mute));
  opacity: 0;
  transform: translateX(-3px);
  transition: opacity .12s ease, transform .12s ease;
  flex: none;
}
.home-dash-stat-link:hover::after { opacity: 1; transform: translateX(0); }
.home-dash-card[data-state="caught-up"] .home-dash-card-body { color: var(--accent); }
.home-dash-stat-list[hidden] { display: none; }

/* Cards #3/#4: "Dispatch/Receiving Discrepancy Overview" -- a small KPI
   stat grid plus a manual refresh button in the title row (these are
   fetched once on load, not polled, unlike the badge-backed cards above).
   Sizing is handled by .home-dash-row-overview/.home-dash-row-snapshot
   now (see above), not a per-card width modifier. */
.home-dash-card-refresh {
  flex: none;
  width: 26px;
  height: 26px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: var(--radius-sm, 8px);
  background: none;
  border: none;
  color: var(--neu-mute, var(--text-dim));
  cursor: pointer;
  padding: 0;
}
/* Opts out of the shared neumorphic `button` treatment (raised box,
   border, shadow) a few rules down -- that treatment is meant for real
   pressable buttons, not a quiet 26px icon-only control sitting inline
   in a card title row, where it renders as a squashed, bordered chip. */
.home-dash-card-refresh {
  background: none !important;
  border: none !important;
  box-shadow: none !important;
  border-radius: var(--radius-sm, 8px) !important;
}
.home-dash-card-refresh:hover { color: var(--neu-blue, var(--accent)); background: var(--surface-2); }
.home-dash-card-refresh svg { width: 14px; height: 14px; }
.home-dash-card-refresh.is-spinning svg { animation: home-dash-kpi-spin .6s linear; }
@keyframes home-dash-kpi-spin { to { transform: rotate(360deg); } }
/* Bordered "chip grid" instead of numbers floating loose in whitespace --
   a 1px hairline (via the background-color + gap trick) between every
   cell, so up to 10 stats reads as one coherent table instead of a
   scattered list. */
.home-dash-stat-grid {
  display: flex;
  flex-wrap: wrap;
  margin-top: 8px;
  border: 1px solid var(--border);
  border-radius: 10px;
  overflow: hidden;
}
.home-dash-stat-grid .home-dash-stat-cell {
  flex: 1 1 100px;
  min-width: 90px;
  background: var(--surface);
  padding: 8px 10px 9px;
  border-right: 1px solid var(--border);
  border-bottom: 1px solid var(--border);
}
/* Same "cells get too tight, shrink the floor" move as .review-map-row's
   380px breakpoint -- the KPI cards carry up to 10 cells, and 110px each
   is too wide to keep more than 2 per row once the card has collapsed to
   full-width-on-a-phone. */
@media (max-width: 380px) {
  .home-dash-stat-grid { grid-template-columns: repeat(auto-fit, minmax(78px, 1fr)); }
}

/* Semantic coloring, keyed off the field names dashboard-home.js already
   fills in (data-kpi="..."), so the numbers that matter (what's still
   open, what's overdue) carry visual weight instead of every cell being
   the same flat black. Deliberately sparse -- most cells stay neutral so
   the colored ones stand out. */
.home-dash-stat-grid [data-kpi="settled"] { color: var(--ok); }
.home-dash-stat-grid [data-kpi="disputed"],
.home-dash-stat-grid [data-kpi="overdue"],
.home-dash-stat-grid [data-kpi="units_short"],
.home-dash-stat-grid [data-kpi="units_over"],
.home-dash-stat-grid [data-kpi="units_damaged"],
.home-dash-stat-grid [data-kpi="escaped_to_branch"] { color: var(--err); }
.home-dash-stat-grid [data-kpi="unsettled"],
.home-dash-stat-grid [data-kpi="in_progress"] { color: var(--warn); }

/* Settlement Overview's variant: only 4 cells (vs. up to 10 on the
   discrepancy cards), so left at the same floor they'd stretch thin and
   sparse across a full-width row. Capped and left-aligned instead, with
   the numbers sized up -- this was originally just a fit-the-space move,
   not a deliberate hierarchy; see .home-dash-stat-cell-hero below for the
   one stat within it that's now actually called out as the headline. */
.home-dash-stat-grid-hero .home-dash-stat-cell { flex-basis: 130px; padding: 12px 14px 13px; }
.home-dash-stat-grid-hero .home-dash-stat-cell-n { font-size: calc(26px * var(--fs, 1)); }
.home-dash-stat-grid-hero .home-dash-stat-cell-label { font-size: calc(12px * var(--fs, 1)); }
.home-dash-stat-grid-hero .home-dash-stat-cell-sub { font-size: calc(10.5px * var(--fs, 1)); }
.home-dash-stat-cell { display: flex; flex-direction: column; gap: 2px; }
.home-dash-stat-cell-n {
  font-family: var(--font-mono);
  font-weight: 700;
  font-size: calc(18px * var(--fs, 1));
  color: var(--text);
}
.home-dash-stat-cell-label {
  font-family: var(--font-body);
  font-size: calc(11px * var(--fs, 1));
  line-height: 1.25;
  color: var(--text-dim, var(--neu-mute));
}
.home-dash-stat-cell-sub {
  font-family: var(--font-mono);
  font-size: calc(10px * var(--fs, 1));
  line-height: 1.3;
  color: var(--text-mute, var(--neu-mute));
  opacity: 0.85;
}

/* Headline vs. supporting-detail hierarchy for the two discrepancy cards
   (Dispatch/Receiving): every one of their up to 10 cells used to sit at
   the same 18px/700-weight, so nothing told you where to look first.
   .home-dash-stat-grid-weighted steps every cell in the grid down to a
   quieter size/weight by default; .home-dash-stat-cell-hero on the one
   stat that matters (Unsettled on both cards) then steps back up past
   the original 18px baseline, so it's the one number in the grid that
   still reads at a glance. The semantic color rules above (still keyed
   off data-kpi) are untouched -- this only changes scale/weight. */
.home-dash-stat-grid-weighted .home-dash-stat-cell-n { font-size: calc(15px * var(--fs, 1)); font-weight: 600; }
.home-dash-stat-grid-weighted .home-dash-stat-cell-label { font-size: calc(10px * var(--fs, 1)); }
.home-dash-stat-grid-weighted .home-dash-stat-cell-hero { background: var(--warn-dim); }
.home-dash-stat-grid-weighted .home-dash-stat-cell-hero .home-dash-stat-cell-n {
  font-size: calc(23px * var(--fs, 1));
  font-weight: 800;
}
.home-dash-stat-grid-weighted .home-dash-stat-cell-hero .home-dash-stat-cell-label {
  font-size: calc(11.5px * var(--fs, 1));
  font-weight: 600;
  color: var(--text-dim, var(--neu-mute));
}

/* Settlement Overview already sizes every cell up via .home-dash-stat-grid-hero
   (all 4 stats were "hero" only because there was room, not because any one
   of them led). Overdue is the one that should actually draw the eye, so it
   gets a further step up + the same tint used above, matching the discrepancy
   cards' headline treatment instead of introducing a third visual language. */
.home-dash-stat-grid-hero .home-dash-stat-cell-hero { background: var(--err-dim); }
.home-dash-stat-grid-hero .home-dash-stat-cell-hero .home-dash-stat-cell-n {
  font-size: calc(32px * var(--fs, 1));
  font-weight: 800;
}
.home-dash-stat-grid-hero .home-dash-stat-cell-hero .home-dash-stat-cell-label {
  font-size: calc(12.5px * var(--fs, 1));
  font-weight: 600;
}

/* Card #6: "Sales Snapshot" -- latest month's headline sales cost + change,
   plus a branches-covered stat cell (reuses .home-dash-stat-cell above). */
.home-dash-sales {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 16px;
  flex-wrap: wrap;
  margin-top: 2px;
}
/* Same `[hidden]` fix as .home-dash-state below -- without it, a real
   sales figure and the "no data yet" message can render stacked
   together instead of one replacing the other. */
.home-dash-sales[hidden] { display: none; }
.home-dash-sales-main { display: flex; flex-direction: column; gap: 2px; }
.home-dash-sales-month {
  font-family: var(--font-body);
  font-size: calc(11px * var(--fs, 1));
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--text-dim, var(--neu-mute));
}
.home-dash-sales-cost {
  font-family: var(--font-mono);
  font-weight: 700;
  font-size: calc(28px * var(--fs, 1));
  color: var(--text);
}
.home-dash-sales-change {
  display: inline-flex;
  align-items: center;
  margin-top: 2px;
  padding: 3px 8px;
  border-radius: 999px;
  font-family: var(--font-mono);
  font-size: calc(11.5px * var(--fs, 1));
  font-weight: 700;
  width: fit-content;
}
.home-dash-sales-change[data-dir="up"] { color: var(--ok); background: var(--ok-dim); }
.home-dash-sales-change[data-dir="down"] { color: var(--err); background: var(--err-dim); }

/* One shared empty-state pattern for the dashboard. Before this, Sales,
   Warehouse and Attention each used a different bare sentence; most visibly,
   Attention left a wide, empty list card when there was nothing to show.
   A compact icon + title + next-step pattern makes an empty state feel like
   a purposeful, stable part of the page rather than missing content. */
.home-dash-state {
  display: flex;
  align-items: center;
  gap: 10px;
  min-height: 58px;
  padding: 8px 2px 4px;
}
.home-dash-state[hidden] { display: none; }
.home-dash-state-icon {
  width: 32px;
  height: 32px;
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: 10px;
  color: var(--accent);
  background: var(--accent-dim);
}
.home-dash-state-icon svg { width: 17px; height: 17px; }
.home-dash-state-title {
  margin: 0;
  font-family: var(--font-body);
  font-size: calc(13.5px * var(--fs, 1));
  font-weight: 700;
  color: var(--text);
}
.home-dash-state-copy {
  margin: 2px 0 0;
  font-family: var(--font-body);
  font-size: calc(12.5px * var(--fs, 1));
  line-height: 1.35;
  color: var(--text-dim, var(--neu-mute));
}
.home-dash-state-link {
  display: inline-block;
  margin-top: 4px;
  font-family: var(--font-body);
  font-size: calc(12px * var(--fs, 1));
  font-weight: 600;
  color: var(--accent);
  text-decoration: none;
}
.home-dash-state-link:hover { text-decoration: underline; }
.home-dash-state-success .home-dash-state-icon { background: var(--ok-dim); color: var(--ok); }
.home-dash-state-error .home-dash-state-icon { background: var(--err-dim); color: var(--err); }
.home-dash-card[data-state="caught-up"] .home-dash-state { min-height: 52px; }

/* Card #7: "Warehouse Cleanup Progress" -- a small ring (SVG circle
   stroke-dashoffset animated in JS) plus a done/total caption. */
.home-dash-progress {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  padding-top: 4px;
}
/* Same `[hidden]` fix as .home-dash-state/.home-dash-sales -- this is
   the one that actually caused the misalignment: the ring kept
   rendering under the "No tasks set up yet." message instead of being
   replaced by it, so the card's real content sat well below where
   Settlement Overview's content starts right under its header. */
.home-dash-progress[hidden] { display: none; }
.home-dash-progress { padding-top: 2px; gap: 8px; }
.home-dash-progress-ring { position: relative; width: 80px; height: 80px; flex: none; }
.home-dash-progress-ring svg { transform: rotate(-90deg); width: 80px; height: 80px; }
.home-dash-progress-track { fill: none; stroke: var(--surface-2, #e5e7eb); stroke-width: 7; }
.home-dash-progress-arc {
  fill: none;
  stroke: var(--accent);
  stroke-width: 7;
  stroke-linecap: round;
  transition: stroke-dashoffset .5s var(--ease, ease);
}
.home-dash-progress-pct {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: var(--font-mono);
  font-weight: 700;
  font-size: calc(19px * var(--fs, 1));
  color: var(--text);
}
.home-dash-progress-caption {
  margin: 0;
  font-family: var(--font-body);
  font-size: calc(12.5px * var(--fs, 1));
  font-weight: 500;
  color: var(--text-dim, var(--neu-mute));
  text-align: center;
}

/* Attention Today: top-5 task list under the "tasks due/overdue" row. Title
   (single line, ellipsis) | status chip | assignee initials. The chip carries
   its own text, so colour is never the only signal. */
.home-dash-attn-tasks { list-style: none; margin: 0 0 2px; padding: 0; }
.home-dash-attn-task {
  display: flex; align-items: center; gap: 8px;
  padding: 6px 0 6px 34px; min-width: 0;
  text-decoration: none; color: inherit;
  border-radius: var(--radius-sm, 8px);
}
.home-dash-attn-task:hover { background: var(--surface-2); }
.home-dash-attn-title {
  flex: 1; min-width: 0;
  font-family: var(--font-body); font-size: calc(13px * var(--fs, 1)); color: var(--text);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.home-dash-chip {
  flex: none; font-family: var(--font-mono); font-weight: 700; font-size: calc(9.5px * var(--fs, 1));
  text-transform: uppercase; letter-spacing: .03em; white-space: nowrap;
  padding: 2px 7px; border-radius: 999px; border: 1px solid transparent;
}
.home-dash-chip[data-state="overdue"] { background: var(--err-dim); color: var(--err); border-color: rgba(179, 65, 58, 0.35); }
.home-dash-chip[data-state="due_today"] { background: var(--warn-dim); color: #8a5a2a; border-color: rgba(173, 124, 80, 0.4); }
.home-dash-avatar {
  flex: none; width: 24px; height: 24px; border-radius: 50%;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--accent-dim); color: var(--accent);
  font-family: var(--font-mono); font-weight: 700; font-size: calc(10px * var(--fs, 1));
}
.home-dash-more {
  display: block; padding: 2px 0 4px 34px; margin: 0;
  font-family: var(--font-mono); font-size: calc(11px * var(--fs, 1));
  color: var(--text-mute); text-decoration: none;
}
.home-dash-more[hidden] { display: none; }
a.home-dash-more:hover { color: var(--accent); text-decoration: underline; }
@media (max-width: 480px) {
  .home-dash-attn-task, .home-dash-more { padding-left: 4px; }
}

/* Warehouse Cleanup Progress: per-zone bars (replaces the single ring). */
.home-dash-overall {
  flex: none; font-family: var(--font-mono); font-weight: 700; font-size: calc(11px * var(--fs, 1));
  color: var(--accent); background: var(--accent-dim);
  padding: 2px 8px; border-radius: 999px; white-space: nowrap;
}
.home-dash-overall[hidden] { display: none; }
.home-dash-progress { align-items: stretch; }
.home-dash-zones { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 10px; }
.home-dash-zone-head { display: flex; align-items: baseline; justify-content: space-between; gap: 8px; margin-bottom: 4px; }
.home-dash-zone-name {
  min-width: 0; font-family: var(--font-mono); font-size: calc(11.5px * var(--fs, 1)); color: var(--text-dim);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.home-dash-zone-pct { flex: none; font-family: var(--font-mono); font-weight: 700; font-size: calc(12px * var(--fs, 1)); color: var(--text); }
.home-dash-bar { height: 6px; border-radius: 999px; background: var(--surface-2, #e5e7eb); overflow: hidden; }
.home-dash-bar-fill { display: block; height: 100%; border-radius: 999px; transition: width .4s var(--ease, ease); }
.home-dash-bar-fill[data-tier="green"] { background: var(--ok); }
.home-dash-bar-fill[data-tier="blue"] { background: var(--accent); }
.home-dash-bar-fill[data-tier="amber"] { background: #d4a017; }
.home-dash-bar-fill[data-tier="orange"] { background: #e07b25; }
.home-dash-bar-fill[data-tier="red"] { background: var(--err); }
.home-dash-more[data-progress-more], p.home-dash-more { padding-left: 0; }
@media (prefers-reduced-motion: reduce) { .home-dash-bar-fill { transition: none; } }

/* Home dashboard, desktop: tighter vertical density so the whole page fits
   one screen without vertical scrolling. Nothing is clipped or hidden and no
   card is given a fixed height: every card keeps all its content and just uses
   less padding / gap / line height, so the three "By the numbers" cards are
   compacted exactly like the rest instead of being shrunk to make room for
   them. Attention Today and Best Day to Run are only as tall as their rows.
   Phones and narrow windows (under 861px wide) keep the roomier layout. */
@media (min-width: 861px) {
  .home-dash { padding: 8px 16px 10px; gap: 6px; }
  .home-dash-greeting { display: flex; align-items: baseline; flex-wrap: wrap; gap: 4px 14px; }
  .home-dash-greeting h1 { margin: 0; font-size: calc(22px * var(--fs, 1)); }
  .home-dash-sections { gap: 8px; }
  .home-dash-eyebrow { margin-bottom: 4px !important; font-size: calc(10.5px * var(--fs, 1)); }
  .home-dash-row-snapshot, .home-dash-row-today, .home-dash-row-overview { gap: 10px; }
  .home-dash-card { padding: 8px 14px 10px; }
  .home-dash-card .po-card-title-row { margin-bottom: 2px; }
  .home-dash-card .po-card-icon { width: 26px; height: 26px; }
  .home-dash-card .po-card-title-row h2 { font-size: calc(15px * var(--fs, 1)); }
  .home-dash-card-primary .po-card-title-row h2 { font-size: calc(15px * var(--fs, 1)); }

  /* Sales Snapshot */
  .home-dash-sales { margin-top: 0; }
  .home-dash-sales-cost { font-size: calc(24px * var(--fs, 1)); }
  .home-dash-sales-change { margin-top: 0; padding: 2px 8px; }

  /* Attention Today: 4 task rows + "+N more", no dead space under them. */
  .home-dash-stat-list { gap: 2px; }
  .home-dash-stat-link { padding: 4px 10px; }
  .home-dash-attn-tasks { margin: 0; }
  .home-dash-attn-task { padding-top: 3px; padding-bottom: 3px; }
  .home-dash-more { padding-top: 0; padding-bottom: 2px; }

  /* Warehouse zones */
  .home-dash-zones { gap: 6px; }
  .home-dash-zone-head { margin-bottom: 2px; }

  /* By the numbers: same content, more cells per row, tighter cells. */
  .home-dash-stat-grid { margin-top: 4px; }
  .home-dash-stat-grid .home-dash-stat-cell { flex: 1 1 88px; min-width: 76px; padding: 5px 9px 6px; }
  .home-dash-stat-cell-n { font-size: calc(16px * var(--fs, 1)); }
  .home-dash-stat-grid-weighted .home-dash-stat-cell-n { font-size: calc(15px * var(--fs, 1)); }
  .home-dash-stat-grid-weighted .home-dash-stat-cell-hero .home-dash-stat-cell-n { font-size: calc(20px * var(--fs, 1)); }
  .home-dash-stat-grid-hero .home-dash-stat-cell { flex-basis: 96px; padding: 6px 10px 7px; }
  .home-dash-stat-grid-hero .home-dash-stat-cell-n { font-size: calc(20px * var(--fs, 1)); }
  .home-dash-stat-grid-hero .home-dash-stat-cell-hero .home-dash-stat-cell-n { font-size: calc(26px * var(--fs, 1)); }

  /* Settlement Overview: 2 x 2 (Overdue / Open on top, In Progress / Settled below). */
  .home-dash-stat-grid-hero .home-dash-stat-cell { flex: 1 1 50%; min-width: 0; }
  .home-dash-stat-grid-hero .home-dash-stat-cell-n { font-size: calc(24px * var(--fs, 1)); }
  .home-dash-stat-grid-hero .home-dash-stat-cell-hero .home-dash-stat-cell-n { font-size: calc(30px * var(--fs, 1)); }

  /* Keep summary cards content-sized. The old "fill the remaining screen"
     rule made the KPI cells expand into very tall empty blocks whenever the
     Attention card was short or clear, which is why the dashboard looked
     unbalanced on larger monitors. */
  .home-dash-section { display: block; }
}

/* ---------- compact card ---------- */
.home-card {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 8px;
  min-height: 122px;
  background: var(--neu-bg);
  box-shadow: var(--neu-raised);
  border-radius: var(--neu-radius-sm);
  padding: 14px 16px;
  text-decoration: none;
  color: inherit;
  cursor: pointer;
  transition: box-shadow .16s ease, opacity .12s ease;
}
a.home-card:hover { box-shadow: 8px 8px 16px var(--neu-dark), -8px -8px 16px var(--neu-light); }
a.home-card:active,
.home-card-expand-trigger:active ~ * { box-shadow: var(--neu-pressed-lg); }
.home-card.is-filtered-out { display: none; }

.home-card-top { display: flex; align-items: center; justify-content: space-between; }

.home-card-icon {
  font-family: var(--font-mono);
  font-size: calc(11px * var(--fs, 1));
  font-weight: 700;
  letter-spacing: 0.02em;
  color: var(--neu-blue-ink);
  background: var(--neu-blue);
  width: 34px;
  height: 34px;
  border-radius: 50%;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  box-shadow: 2px 2px 5px var(--neu-dark), -2px -2px 5px var(--neu-light);
}
.home-card-icon-muted {
  color: var(--neu-mute);
  background: var(--neu-bg);
  box-shadow: var(--neu-pressed);
}

.home-card-status {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  font-family: var(--font-body);
  font-size: calc(11px * var(--fs, 1));
  font-weight: 700;
  color: var(--neu-mute);
}
.home-card-status i {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--neu-mute);
  flex: none;
}
.home-card-status.is-live { color: var(--neu-ok); }
.home-card-status.is-live i { background: var(--neu-ok); box-shadow: 0 0 0 3px var(--neu-ok-bg); }
.home-card-status.is-soon { color: var(--neu-mute); }

.home-card-title {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: calc(15px * var(--fs, 1));
  margin: 2px 0 0;
  color: var(--neu-ink);
}
.home-card-desc {
  margin: 0;
  font-size: calc(12px * var(--fs, 1));
  line-height: 1.4;
  color: var(--neu-dim);
}

/* not-yet-built cards: same shape, quieter, inert */
.home-card-soon { cursor: default; opacity: 0.7; }
.home-card-soon .home-card-title,
.home-card-soon .home-card-desc { color: var(--neu-mute); }

/* trailing "more tools" placeholder */
.home-card-ghost {
  align-items: center;
  justify-content: center;
  text-align: center;
  gap: 6px;
  box-shadow: var(--neu-pressed);
  cursor: default;
}
.home-card-ghost-icon {
  font-family: var(--font-mono);
  font-size: calc(18px * var(--fs, 1));
  font-weight: 700;
  color: var(--neu-mute);
  width: 30px;
  height: 30px;
  border-radius: 50%;
  background: var(--neu-bg);
  box-shadow: var(--neu-raised-sm);
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
.home-card-ghost-text {
  margin: 0;
  font-size: calc(11px * var(--fs, 1));
  font-weight: 600;
  line-height: 1.35;
  color: var(--neu-mute);
}

/* ---------- expandable card (Intransit, Discrepancy Monitoring) ---------- */
.home-card-expand { padding: 0; }
.home-card-expand.is-open { z-index: 30; }
.home-card-expand-trigger {
  display: flex;
  flex-direction: column;
  gap: 8px;
  width: 100%;
  height: 100%;
  padding: 14px 16px;
  background: none;
  border: none;
  text-align: left;
  cursor: pointer;
  font-family: inherit;
  font-size: inherit;
  font-weight: inherit;
  text-transform: none;
  letter-spacing: normal;
  line-height: inherit;
  color: inherit;
  box-shadow: none;
  transition: none;
}
.home-card-expand-trigger:hover,
.home-card-expand-trigger:active {
  background: none;
  color: inherit;
  box-shadow: none;
  transform: none;
}
.home-card-expand-trigger .home-card-title,
.home-card-expand-trigger .home-card-desc,
.home-card-expand-trigger .home-card-status,
.home-card-expand-trigger .home-card-icon { text-transform: none; }

.home-card-options {
  position: absolute;
  top: calc(100% + 10px);
  left: 0;
  right: 0;
  min-width: 260px;
  display: flex;
  flex-direction: column;
  gap: 4px;
  background: var(--neu-bg);
  border-radius: var(--neu-radius-sm);
  box-shadow: 10px 10px 22px var(--neu-dark), -10px -10px 22px var(--neu-light);
  padding: 8px;
  z-index: 30;
  opacity: 0;
  visibility: hidden;
  transform: translateY(-6px) scale(0.98);
  transform-origin: top left;
  pointer-events: none;
  transition: opacity .16s ease, transform .16s ease, visibility 0s linear .16s;
}
.home-card-options.is-open {
  opacity: 1;
  visibility: visible;
  transform: translateY(0) scale(1);
  pointer-events: auto;
  transition: opacity .16s ease, transform .16s ease;
}
.home-card-option {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  padding: 9px 10px;
  border-radius: var(--neu-radius-sm);
  text-decoration: none;
  color: inherit;
  transition: box-shadow .12s ease;
}
.home-card-option:hover { box-shadow: var(--neu-pressed); }
.home-card-option-icon {
  flex: none;
  width: 26px;
  height: 26px;
  border-radius: 50%;
  background: var(--neu-bg);
  box-shadow: var(--neu-raised-sm);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  color: var(--neu-blue);
}
.home-card-option-icon svg { width: 15px; height: 15px; }
.home-card-option-text { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.home-card-option-title { font-family: var(--font-body); font-weight: 700; font-size: calc(12.5px * var(--fs, 1)); color: var(--neu-ink); }
.home-card-option-desc { font-size: calc(11px * var(--fs, 1)); line-height: 1.4; color: var(--neu-dim); }

.home-card-option-soon {
  cursor: default;
  opacity: 0.65;
  justify-content: space-between;
  align-items: center;
}
.home-card-option-soon:hover { box-shadow: none; }
.home-card-option-soon-tag {
  flex: none;
  font-size: calc(10px * var(--fs, 1));
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--neu-mute);
  background: var(--neu-bg);
  box-shadow: var(--neu-pressed);
  border-radius: 999px;
  padding: 3px 9px;
}

.home-grid-wrap.has-open-card .home-card:not(.is-open) { opacity: 0.55; }

.home-empty-msg,
.home-no-results {
  font-family: var(--font-body);
  color: var(--neu-mute);
  padding: 20px 4px;
}
.home-no-results.hidden { display: none; }

@media (max-width: 640px) {
  .home-topbar { padding: 12px 16px; }
  .home-brand-text h1 { font-size: calc(18px * var(--fs, 1)); }
  .home-card-grid { grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); }
}

/* ---------- "About this app" presentation modal ---------- */
.summary-modal {
  position: fixed;
  inset: 0;
  z-index: 100;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
  opacity: 0;
  visibility: hidden;
  transition: opacity .2s var(--ease), visibility 0s linear .2s;
}
.summary-modal.is-open {
  opacity: 1;
  visibility: visible;
  transition: opacity .2s var(--ease);
}
.summary-modal-backdrop {
  position: absolute;
  inset: 0;
  background: rgba(16, 17, 18, 0.72);
  backdrop-filter: blur(2px);
}
.summary-modal-panel {
  position: relative;
  width: 100%;
  max-width: 1400px;
  height: calc(100vh - 48px);
  max-height: calc(100vh - 48px);
  min-height: 380px;
  background: var(--surface);
  border: 1px solid var(--border-hi);
  border-top: 4px solid var(--accent);
  border-radius: var(--radius-sm);
  box-shadow: var(--shadow-3);
  padding: 56px 64px 32px;
  display: flex;
  flex-direction: column;
  transform: translateY(8px) scale(0.98);
  transition: transform .2s var(--ease);
}
.summary-modal.is-open .summary-modal-panel { transform: translateY(0) scale(1); }
.summary-modal-close {
  position: absolute;
  top: 12px;
  right: 12px;
  width: 30px;
  height: 30px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: calc(20px * var(--fs, 1));
  line-height: 1;
  color: var(--text-mute);
  background: none;
  border: none;
  box-shadow: none;
  cursor: pointer;
}
.summary-modal-close:hover { color: var(--ink); background: var(--surface-2); transform: none; box-shadow: none; }

/* slides: stacked and cross-faded rather than reflowing the panel height */
.summary-slide {
  display: none;
  flex-direction: column;
  justify-content: center;
  gap: 36px;
  flex: 1;
  width: 100%;
  max-width: 100%;
  animation: summary-slide-in .28s var(--ease) both;
}
.summary-slide.is-active { display: flex; }
@keyframes summary-slide-in {
  from { opacity: 0; transform: translateX(10px); }
  to { opacity: 1; transform: translateX(0); }
}
.summary-slide-eyebrow {
  font-family: var(--font-mono);
  font-size: calc(16px * var(--fs, 1));
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--accent);
}
.summary-slide-title {
  font-family: var(--font-display);
  font-size: calc(60px * var(--fs, 1));
  line-height: 1.1;
  font-weight: 700;
  color: var(--ink);
  margin: 0;
}
.summary-slide-list {
  margin: 0;
  padding: 0;
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: 26px;
}
.summary-slide-list li {
  position: relative;
  padding-left: 30px;
  font-size: calc(24px * var(--fs, 1));
  line-height: 1.6;
  color: var(--text-dim);
}
.summary-slide-list li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 13px;
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background: var(--accent);
}
.summary-slide-list li strong { color: var(--ink); }

.summary-modal-nav {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  margin-top: 24px;
  padding-top: 16px;
  border-top: 1px solid var(--border);
}
.summary-nav-btn {
  font-family: var(--font-mono);
  font-size: calc(13px * var(--fs, 1));
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  color: var(--text-dim);
  background: none;
  border: 1px solid var(--border-hi);
  border-radius: var(--radius-sm);
  padding: 10px 18px;
  cursor: pointer;
}
.summary-nav-btn:hover { color: var(--ink); border-color: var(--ink); background: var(--surface-2); box-shadow: none; transform: none; }
.summary-nav-btn:disabled { opacity: 0.35; cursor: default; pointer-events: none; }
.summary-dots { display: flex; gap: 9px; }
.summary-dot {
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: var(--border-hi);
  cursor: pointer;
  transition: background .15s ease, transform .15s ease;
}
.summary-dot.is-active { background: var(--accent); transform: scale(1.2); }

@media (max-width: 560px) {
  .summary-modal-panel { padding: 30px 22px 18px; }
  .summary-slide-title { font-size: calc(28px * var(--fs, 1)); }
  .summary-slide-list li { font-size: calc(15px * var(--fs, 1)); }
}
.month-countdown-num {
  font-family: var(--font-mono);
  font-size: calc(13px * var(--fs, 1));
  font-weight: 700;
  line-height: 1;
  color: #6be095;
}
.month-countdown-num.soon { color: var(--brand-blue-light); }
.month-countdown-num.last-day,
.month-countdown-num.wiped { color: var(--hazard); }
.month-countdown-label {
  font-family: var(--font-mono);
  font-size: calc(10.5px * var(--fs, 1));
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: #b9c2d6;
}

@media (max-width: 560px) {
  .home-wrap { gap: 26px; }
  .app-grid { grid-template-columns: 1fr; }
}

/* ---------- responsive layout ---------- */

/* The page always uses ordinary document scrolling — no locked/fixed-height
   viewport mode. That model kept fighting itself: fixed-height rails needed
   more vertical room than short or restored windows had, which chopped
   content off flush against the edge with no visible scrollbar. A single
   natural scroll never clips anything; it just scrolls a bit further.
   Both side rails use position: sticky (added per width breakpoint below,
   since they're only shown as columns from 860px up) so they stay in view
   as the page scrolls, without needing their own independent scroll area. */

/* wide desktop: main column (its own 2-up card grid) + output rail. Main
   column now fills whatever width is left after the sidebar and right
   rail, instead of stopping early -- the goal is one screen's worth of
   cards, not a narrow column with empty space on either side. */
@media (min-width: 900px) {
  .layout {
    display: grid;
    /* ~70/30 split like the reference dashboard, not a fixed narrow rail --
       the rail scales with the page instead of stopping at 320px and
       leaving the extra width to the main column alone. */
    grid-template-columns: minmax(520px, 1fr) minmax(280px, 30%);
    gap: 14px;
    align-items: start;
  }
  .stack {
    display: grid;
    /* Single column, full width, every card — no pairing. Even cards
       that look similarly-sized (Branch Settings vs. Working Days &
       Stocking Days) can mismatch in height depending on which
       <details> happen to be open at the time, so pairing anything
       is unreliable. This is the simplest option that never breaks. */
    grid-template-columns: 1fr;
    grid-auto-rows: min-content;
    gap: 9px;
    align-content: start;
    align-items: start;
  }
  /* odd card out (3 cards, 5 cards...) spans the full row instead of
     leaving a half-width gap next to it */
  .stack .card:last-child:nth-child(odd) { grid-column: 1 / -1; }
  /* explicit opt-in for a card that should always span both columns,
     for pages where a trailing hidden results card throws off the
     odd/even count above */
  .stack .card-span-2 { grid-column: 1 / -1; }
  .rail-right {
    /* Topbar is padding:7px top/bottom + ~30px logo + 1px border ≈ 45px,
       plus the same 14px gap used elsewhere between the layout's own
       columns, so the rail sits flush with the topbar instead of
       leaving a visible dead gap above it (was 88px — almost double
       the real topbar height, which is what caused the misalignment). */
    position: sticky;
    top: 58px;
  }
}

/* narrow: single stacked column, output rail falls in flow below the form —
   the whole page scrolls together here, same as before. */
@media (max-width: 899.98px) {
  .layout { display: flex; flex-direction: column; gap: 16px; }
}

@media (max-width: 560px) {
  body { padding: 32px 14px 88px; }
  .wrap { gap: 22px; }
  .card { padding: 18px 16px; border-radius: var(--radius-sm); }
  .topbar { justify-content: center; text-align: center; }
  .cta { width: 100%; justify-content: center; }
}

/* ---------- results viewer (in-browser, no Excel needed) ---------- */
.results-strip {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin: 10px 0 16px;
}
.results-strip .stat {
  display: flex;
  flex-direction: column;
  gap: 2px;
  background: var(--surface-2);
  border: 1px solid var(--border-hi);
  border-radius: var(--radius-sm);
  padding: 8px 13px;
  min-width: 92px;
}
.results-strip .stat .n {
  font-family: var(--font-mono);
  font-weight: 700;
  font-size: calc(17px * var(--fs, 1));
  line-height: 1;
}
.results-strip .stat .l {
  font-family: var(--font-mono);
  font-size: calc(10px * var(--fs, 1));
  letter-spacing: 0.03em;
  text-transform: uppercase;
  color: var(--text-mute);
}
.results-strip .stat.ok .n { color: var(--ok); }
.results-strip .stat.check .n { color: var(--err); }
.results-strip .stat.notup .n { color: var(--text-mute); }
.results-strip .stat.logged .n { color: var(--warn); }
.results-strip .stat.download {
  cursor: pointer;
  background: var(--accent-dim);
  border-color: var(--accent);
}
.results-strip .stat.download .l { color: var(--accent-hi); }
.results-strip .stat.download:hover { background: rgba(2, 59, 188, 0.18); }

.tab-bar {
  display: flex;
  gap: 4px;
  border-bottom: 2px solid var(--ink);
  margin-bottom: 12px;
}
.tab-btn {
  background: transparent;
  border: none;
  border-bottom: 3px solid transparent;
  border-radius: var(--radius-sm);
  padding: 8px 4px;
  margin-right: 18px;
  margin-bottom: -2px;
  font-family: var(--font-mono);
  font-size: calc(11.5px * var(--fs, 1));
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  color: var(--text-mute);
  cursor: pointer;
  box-shadow: none;
  transition: color .18s var(--ease), border-color .22s var(--ease);
}
.tab-btn:hover { color: var(--ink); background: transparent; }
.tab-btn.active { color: var(--ink); border-bottom-color: var(--hazard); }
.tab-btn:active { transform: none; }
.tab-panel.hidden { display: none; }

.table-controls {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 8px;
  flex-wrap: wrap;
}
.table-search {
  flex: 1 1 220px;
  min-width: 160px;
  font-size: calc(12.5px * var(--fs, 1));
  padding: 7px 10px;
}
#summaryFilter {
  font-size: calc(12.5px * var(--fs, 1));
  padding: 7px 10px;
  border: 1px solid var(--border-hi);
  border-radius: var(--radius-sm);
  background: var(--surface);
  color: var(--text);
  font-family: var(--font-body);
}
.table-count {
  font-size: calc(11px * var(--fs, 1));
  color: var(--text-mute);
  font-family: var(--font-mono);
  white-space: nowrap;
  margin-left: auto;
}

.table-scroll {
  max-height: 480px;
  overflow: auto;
  border: 1px solid var(--border-hi);
  border-radius: var(--radius-sm);
}
.table-scroll table { margin: 0; font-size: calc(12px * var(--fs, 1)); }
.table-scroll thead th {
  position: sticky;
  top: 0;
  background: var(--ink);
  color: #fff;
  z-index: 1;
  box-shadow: 0 2px 0 var(--hazard);
}
.table-scroll tbody tr.row-ok td:first-child { border-left: 3px solid var(--ok); }
.table-scroll tbody tr.row-check td:first-child { border-left: 3px solid var(--err); }
.table-scroll tbody tr.row-notup td:first-child { border-left: 3px solid var(--text-mute); }
.table-scroll tbody tr.row-logged td:first-child { border-left: 3px solid var(--warn); }
.table-scroll .empty-row td {
  text-align: center;
  color: var(--text-mute);
  padding: 20px;
  font-style: italic;
}

.pill {
  display: inline-block;
  font-family: var(--font-mono);
  font-weight: 700;
  font-size: calc(10px * var(--fs, 1));
  text-transform: uppercase;
  letter-spacing: 0.02em;
  padding: 2px 7px;
  border-radius: var(--radius-sm);
  border: 1px solid transparent;
  white-space: nowrap;
}
.pill.ok { background: rgba(31, 157, 85, 0.12); color: var(--ok); border-color: rgba(31, 157, 85, 0.4); }
.pill.check { background: rgba(244, 0, 5, 0.10); color: var(--err); border-color: rgba(244, 0, 5, 0.32); }
.pill.notup { background: rgba(70, 83, 107, 0.12); color: var(--text-dim); border-color: var(--border-hi); }
.pill.logged { background: var(--warn-dim); color: var(--warn); border-color: rgba(184, 121, 74, 0.4); }
.pill.blank { background: transparent; color: var(--text-mute); }

.monitoring-grid td, .monitoring-grid th { text-align: left; }
.monitoring-grid td.cell-status { text-align: center; }

.upload-note {
  display: flex;
  align-items: flex-start;
  gap: 7px;
  background: rgba(244, 0, 5, 0.07);
  border: 1px solid rgba(244, 0, 5, 0.25);
  border-radius: var(--radius-sm);
  padding: 8px 11px;
  margin: 6px 0;
  font-size: calc(12px * var(--fs, 1));
  color: var(--text);
}
.upload-note.info {
  background: rgba(2, 59, 188, 0.07);
  border-color: rgba(2, 59, 188, 0.25);
}
.upload-note strong { color: var(--text); }

/* ==========================================================================
   Organizer review cards and soft popups
   ========================================================================== */
.branch-pill {
  display: inline-flex;
  margin: 2px 4px 2px 0;
}

.target-field-card {
  align-items: center;
  gap: 10px;
  padding: 8px 10px;
  border-radius: var(--radius-sm);
}
.target-field-card input[type=text] {
  flex: 1 1 220px;
  min-width: 0;
  max-width: 320px;
}

.review-card {
  position: relative;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  gap: 12px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 12px;
  box-shadow: 0 1px 2px rgba(16,24,40,0.05);
  transition: border-color .16s ease, box-shadow .16s ease, transform .16s ease;
}
.review-card::before {
  content: "";
  position: absolute;
  inset: 0 auto 0 0;
  width: 3px;
  background: var(--ok);
  opacity: .75;
}
.review-card:hover {
  border-color: var(--border-hi);
  box-shadow: 0 2px 6px rgba(16,24,40,0.08);
}
.review-card[data-review-state="needs-choice"]::before,
.merge-file-card.check::before {
  background: var(--accent);
}
.merge-file-card.issue::before {
  background: var(--err);
}

.review-card-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 12px;
}
.review-file {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  min-width: 0;
}
.review-icon {
  flex: 0 0 auto;
  display: inline-grid;
  place-items: center;
  width: 24px;
  height: 24px;
  border-radius: var(--radius-sm);
  background: rgba(31, 157, 85, 0.12);
  border: 1px solid rgba(31, 157, 85, 0.28);
  color: var(--ok);
  font-family: var(--font-mono);
  font-size: calc(12px * var(--fs, 1));
  font-weight: 700;
}
.review-card[data-review-state="needs-choice"] .review-icon,
.merge-file-card.check .review-icon {
  background: rgba(2, 59, 188, 0.12);
  border-color: rgba(2, 59, 188, 0.28);
  color: var(--accent-hi);
}
.merge-file-card.issue .review-icon {
  background: rgba(244, 0, 5, 0.10);
  border-color: rgba(244, 0, 5, 0.28);
  color: var(--err);
}
.review-file-text {
  display: flex;
  flex-direction: column;
  gap: 3px;
  min-width: 0;
}
.review-title {
  color: var(--text);
  font-size: calc(12.5px * var(--fs, 1));
  line-height: 1.35;
  overflow-wrap: anywhere;
}
.review-subtitle {
  color: var(--text-mute);
  font-size: calc(11px * var(--fs, 1));
  line-height: 1.35;
  overflow-wrap: anywhere;
}
.review-actions {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 8px;
  flex-wrap: wrap;
  flex: 0 0 auto;
}
.review-actions.compact {
  max-width: 240px;
}
.review-status-badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 24px;
  padding: 3px 8px;
  border-radius: var(--radius-sm);
  border: 1px solid var(--border-hi);
  background: var(--surface);
  color: var(--text-dim);
  font-family: var(--font-mono);
  font-size: calc(10px * var(--fs, 1));
  font-weight: 600;
  line-height: 1.2;
  white-space: nowrap;
}
.review-status-badge.ready {
  background: rgba(31, 157, 85, 0.10);
  border-color: rgba(31, 157, 85, 0.32);
  color: var(--ok);
}
.review-status-badge.needs-choice {
  background: rgba(2, 59, 188, 0.11);
  border-color: rgba(2, 59, 188, 0.32);
  color: var(--accent-hi);
}
.review-status-badge.soft {
  background: rgba(91, 100, 116, 0.10);
  border-color: rgba(91, 100, 116, 0.20);
  color: var(--text-dim);
}
.review-select {
  min-height: 32px;
  min-width: 170px;
  max-width: min(100%, 260px);
  border-radius: var(--radius-sm);
  background: var(--surface);
}
.review-note {
  margin: -2px 0 0 34px;
  padding: 8px 10px;
  border-radius: var(--radius-sm);
  background: rgba(244, 0, 5, 0.07);
  border: 1px solid rgba(244, 0, 5, 0.20);
  color: var(--err);
  font-size: calc(11.5px * var(--fs, 1));
  line-height: 1.45;
}
.review-map-list {
  display: flex;
  flex-direction: column;
  gap: 7px;
  padding: 2px 0 0 34px;
}
.review-map-row {
  display: grid;
  grid-template-columns: minmax(90px, .7fr) minmax(140px, 1.3fr);
  gap: 8px;
  align-items: center;
}
@media (max-width: 380px) {
  .review-map-row {
    grid-template-columns: 1fr;
  }
}
.review-map-target {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  min-width: 0;
  color: var(--text-dim);
  font-size: calc(11.5px * var(--fs, 1));
  line-height: 1.35;
}
.review-map-target span:last-child {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.map-arrow {
  color: var(--accent);
  font-family: var(--font-mono);
  font-weight: 700;
}
.review-empty {
  margin: 0;
  color: var(--text-mute);
  font-size: calc(11.5px * var(--fs, 1));
}

.result-download-panel {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 14px;
  margin-bottom: 14px;
  padding: 13px 14px;
  border: 1px solid rgba(31, 157, 85, 0.24);
  border-radius: var(--radius-sm);
  background: linear-gradient(180deg, rgba(31, 157, 85, 0.08), rgba(31, 157, 85, 0.03));
}
.result-download-panel > div {
  display: flex;
  flex-direction: column;
  gap: 3px;
  min-width: 0;
}
.result-download-panel strong {
  overflow-wrap: anywhere;
}
.result-eyebrow {
  color: var(--ok);
  font-family: var(--font-mono);
  font-size: calc(10px * var(--fs, 1));
  font-weight: 700;
  text-transform: uppercase;
}
.download-link {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 36px;
  padding: 8px 12px;
  border: 1px solid rgba(31, 157, 85, 0.36);
  border-radius: var(--radius-sm);
  background: var(--surface);
  color: var(--ok);
  font-size: calc(12px * var(--fs, 1));
  font-weight: 700;
  text-decoration: none;
  white-space: nowrap;
  transition: border-color .12s ease, background .12s ease, transform .08s ease;
}
.download-link:hover {
  background: rgba(31, 157, 85, 0.10);
  border-color: var(--ok);
}
.download-link:active { transform: translateY(1px); }

.review-summary-grid {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 8px;
  margin: 0 0 14px;
}
.review-stat {
  display: flex;
  flex-direction: column;
  gap: 4px;
  min-width: 0;
  padding: 10px 11px;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--surface);
}
.review-stat .n {
  color: var(--text);
  font-family: var(--font-mono);
  font-size: calc(19px * var(--fs, 1));
  font-weight: 700;
  line-height: 1;
  font-variant-numeric: tabular-nums;
  overflow-wrap: anywhere;
}
.review-stat .l {
  color: var(--text-mute);
  font-size: calc(10px * var(--fs, 1));
  font-weight: 700;
  line-height: 1.25;
  text-transform: uppercase;
}
.review-stat.ok .n { color: var(--ok); }
.review-stat.check .n { color: var(--accent-hi); }

.merge-review-list {
  display: flex;
  flex-direction: column;
  gap: 9px;
}
.merge-file-card .review-card-head {
  align-items: center;
}
.merge-flow {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 8px;
  padding-left: 34px;
}
.merge-flow span {
  min-width: 0;
  padding: 8px 10px;
  border-radius: var(--radius-sm);
  background: rgba(16, 24, 40, 0.035);
  color: var(--text-dim);
  font-size: calc(11.5px * var(--fs, 1));
  line-height: 1.4;
  overflow-wrap: anywhere;
}
.merge-flow strong {
  color: var(--text);
  font-weight: 600;
}
.merge-flow em {
  color: var(--text-mute);
  font-style: normal;
  white-space: nowrap;
}
.merge-sample {
  margin-left: 34px;
  padding: 8px 10px;
  border-radius: var(--radius-sm);
  background: rgba(2, 59, 188, 0.08);
  border: 1px solid rgba(2, 59, 188, 0.18);
}
.merge-sample span {
  color: var(--accent-hi);
  font-family: var(--font-mono);
  font-size: calc(10px * var(--fs, 1));
  font-weight: 700;
  text-transform: uppercase;
}
.merge-sample p {
  margin: 4px 0 0;
  color: var(--text-dim);
  font-size: calc(11.5px * var(--fs, 1));
  line-height: 1.45;
  overflow-wrap: anywhere;
}

.result-file {
  gap: 10px;
  border-width: 1.5px;
  border-radius: var(--radius-sm);
  padding: 9px 11px;
}
.result-file > span {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}
.result-file > span strong {
  color: var(--text);
  font-size: calc(12px * var(--fs, 1));
}
.result-file > span small {
  color: var(--text-mute);
  font-family: var(--font-mono);
  font-size: calc(10px * var(--fs, 1));
}
.result-file a {
  min-width: 0;
  overflow-wrap: anywhere;
  text-align: right;
}
.result-file-fresh {
  animation: review-pop .42s cubic-bezier(.2,.8,.2,1);
}

.toast-stack {
  position: fixed;
  right: 18px;
  bottom: 18px;
  z-index: 120;
  display: flex;
  flex-direction: column;
  gap: 8px;
  width: min(360px, calc(100vw - 36px));
  pointer-events: none;
}
.toast-card {
  padding: 11px 13px;
  border: 1px solid var(--border-hi);
  border-left: 5px solid var(--ok);
  border-radius: var(--radius-sm);
  background: rgba(255,255,255,0.97);
  color: var(--text);
  box-shadow: var(--shadow-2);
  font-size: calc(12px * var(--fs, 1));
  line-height: 1.45;
  pointer-events: auto;
  animation: toast-in .2s ease-out;
}
.toast-card.error {
  border-left-color: var(--hazard);
  background: rgba(255,250,250,0.98);
  color: var(--err);
}
.toast-card.leaving {
  animation: toast-out .22s ease-in forwards;
}

.review-pop {
  animation: review-pop .42s cubic-bezier(.2,.8,.2,1);
}
@keyframes review-pop {
  from { opacity: 0; transform: translateY(8px) scale(.992); }
  to { opacity: 1; transform: translateY(0) scale(1); }
}
@keyframes toast-in {
  from { opacity: 0; transform: translateY(8px); }
  to { opacity: 1; transform: translateY(0); }
}
@keyframes toast-out {
  to { opacity: 0; transform: translateY(6px); }
}

@media (max-width: 760px) {
  .review-card { padding: 11px; }
  .review-card-head {
    flex-direction: column;
    align-items: stretch;
  }
  .review-actions {
    width: 100%;
    display: grid;
    grid-template-columns: auto minmax(0, 1fr) auto;
    justify-content: stretch;
  }
  .review-actions.compact {
    display: flex;
    max-width: none;
  }
  .review-select {
    width: 100%;
    min-width: 0;
    max-width: none;
  }
  .review-note,
  .review-map-list,
  .merge-flow,
  .merge-sample {
    margin-left: 0;
    padding-left: 0;
  }
  .review-note,
  .merge-sample {
    padding: 8px 10px;
  }
  .merge-flow {
    grid-template-columns: 1fr;
  }
  .review-map-target span:last-child {
    white-space: normal;
  }
  .result-download-panel {
    align-items: stretch;
    flex-direction: column;
  }
  .download-link {
    width: 100%;
    white-space: normal;
    text-align: center;
  }
  .review-summary-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

@media (max-width: 560px) {
  .target-field-card {
    align-items: stretch;
    flex-direction: column;
  }
  .target-field-card input[type=text] {
    width: 100%;
    max-width: none;
  }
  .target-field-card button,
  .review-actions .remove-file {
    width: 100%;
  }
  .review-actions {
    grid-template-columns: 1fr;
  }
  .review-status-badge {
    width: 100%;
  }
  .review-summary-grid {
    grid-template-columns: 1fr;
  }
  .result-file {
    align-items: stretch;
    flex-direction: column;
  }
  .result-file a {
    text-align: left;
  }
  .toast-stack {
    right: 14px;
    bottom: 14px;
    width: calc(100vw - 28px);
  }
  .cta .primary {
    width: min(100%, 280px);
  }
}

@media (prefers-reduced-motion: reduce) {
  .review-pop,
  .result-file-fresh,
  .toast-card,
  .toast-card.leaving,
  .progress-fill,
  .app-nav-live-dot,
  .app-card-tag-live::before,
  .readiness-list li.check-ok .tick,
  .result-file {
    animation: none;
  }
}

/* ==========================================================================
   AUTH SCREENS — /account (change password) and /admin (manage accounts)
   Shared components so both pages (and any future auth screen) look and
   behave like one deliberate system instead of two one-off pages.
   ========================================================================== */

.auth-shell {
  max-width: 720px;
  margin: 0 auto;
  padding: 22px 20px 60px;
  display: flex;
  flex-direction: column;
  gap: 16px;
}
/* /admin gets much more room than /account's single change-password panel —
   it holds a form AND a whole account list, so it should use the screen
   instead of sitting in a narrow centered column on wide monitors. */
.auth-shell.auth-shell-wide { max-width: min(2200px, 100vw - 32px); }

/* Add-a-new-account and Existing-accounts panels: stacked on narrow
   viewports, side by side once there's room, so the page's width is
   actually spent on content instead of margin on either side. */
.admin-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: 16px;
  align-items: start;
}
@media (min-width: 1100px) {
  .admin-grid { grid-template-columns: minmax(400px, 480px) 1fr; }
}

/* flash / inline messages — one look everywhere a form can succeed or fail */
.auth-flash {
  padding: 10px 14px;
  border-radius: var(--radius-sm);
  font-size: calc(13px * var(--fs, 1));
  font-family: var(--font-body);
  display: flex;
  align-items: flex-start;
  gap: 8px;
}
.auth-flash-ok { background: var(--ok-dim); color: var(--ok); border: 1px solid var(--ok); }
.auth-flash-error { background: var(--err-dim); color: var(--err); border: 1px solid var(--err); }
.auth-flash-warn { background: var(--warn-dim); color: var(--warn); border: 1px solid var(--warn); }

/* panel — the one card shape every section of /account and /admin uses */
.panel {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  box-shadow: var(--shadow-1);
  overflow: hidden;
}
.panel-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 10px;
  padding: 11px 16px 0;  /* was 16px 18px 0 — shorter panels */
}
.panel-head h2 {
  font-family: var(--font-display);
  font-size: calc(14.5px * var(--fs, 1));
  text-transform: uppercase;
  letter-spacing: 0.04em;
  margin: 0;
  color: var(--text);
}
.panel-head p {
  margin: 3px 0 0;
  font-size: calc(12px * var(--fs, 1));
  color: var(--text-mute);
  font-family: var(--font-body);
}
.panel-body { padding: 10px 16px 13px; }  /* was 14px 18px 18px — shorter panels */
.panel + .panel { margin-top: 0; }

/* profile summary row at the top of /account */
.profile-row { display: flex; align-items: center; gap: 14px; }
.profile-avatar {
  flex: none;
  width: 48px;
  height: 48px;
  border-radius: 50%;
  background: var(--surface-2);
  border: 1px solid var(--border);
  color: var(--text-dim);
  font-family: var(--font-display);
  font-weight: 600;
  font-size: calc(18px * var(--fs, 1));
  display: flex;
  align-items: center;
  justify-content: center;
}
.profile-id { display: flex; flex-direction: column; gap: 4px; min-width: 0; }
.profile-id strong { font-size: calc(15.5px * var(--fs, 1)); font-family: var(--font-body); font-weight: 700; color: var(--text); }
.profile-id span { font-size: calc(12px * var(--fs, 1)); font-family: var(--font-mono); color: var(--text-mute); }
.profile-badges { margin-left: auto; display: flex; gap: 6px; flex-wrap: wrap; }

/* badges — one small set of tags used for role/status everywhere */
.badge {
  font-family: var(--font-mono);
  font-size: calc(10px * var(--fs, 1));
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  padding: 3px 8px;
  border-radius: 4px;
  white-space: nowrap;
  line-height: 1.6;
}
.badge-admin { background: var(--accent-dim); color: var(--accent-hi); }
.badge-inactive { background: var(--err-dim); color: var(--err); }
.badge-warn { background: var(--warn-dim); color: var(--warn); }
.badge-active { background: var(--ok-dim); color: var(--ok); }

/* field — label + control, the one unit every form on these pages is built from */
.field { display: flex; flex-direction: column; gap: 5px; margin-bottom: 14px; }
.field:last-child { margin-bottom: 0; }
.field label {
  font-family: var(--font-mono);
  font-size: calc(11px * var(--fs, 1));
  text-transform: uppercase;
  letter-spacing: 0.03em;
  color: var(--text-mute);
}
.field input[type=text],
.field input[type=password] {
  width: 100%;
  flex: none; /* .field is a column flex container; the site-wide
                 `input[type=text] { flex: 1 1 0% }` rule (meant for
                 inputs sitting in a row next to a button) sets
                 flex-basis, which controls *height* on the main axis
                 in a column container — that silently beat the
                 height rules below and made same-row fields render
                 at different heights. Opting these back out of flex
                 sizing lets height be driven by height/padding again. */
  box-sizing: border-box;
  background: var(--surface);
  border: 1px solid var(--border-hi);
  color: var(--text);
  padding: 9px 11px;
  border-radius: var(--radius-sm);
  font-size: calc(13px * var(--fs, 1));
  font-family: var(--font-mono);
  transition: border-color .18s var(--ease), box-shadow .18s var(--ease);
}
.field input:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-dim);
}
.field-hint { font-size: calc(11.5px * var(--fs, 1)); color: var(--text-mute); font-family: var(--font-body); }
.field-hint.ok { color: var(--ok); }
.field-hint.ok::before { content: "✓ "; }
.field-error { font-size: calc(11.5px * var(--fs, 1)); color: var(--err); font-family: var(--font-body); }
.field-row { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; }
@media (max-width: 560px) { .field-row { grid-template-columns: 1fr; } }

/* password input with an inline show/hide toggle, reused anywhere a
   password is typed blind (login excluded — see .login-form there) */
.pw-field { position: relative; }
.pw-field input { padding-right: 54px; }
.pw-toggle {
  position: absolute;
  right: 4px;
  top: 4px;
  bottom: 4px;
  background: transparent;
  border: none;
  box-shadow: none;
  padding: 0 10px;
  font-size: calc(10px * var(--fs, 1));
  color: var(--text-mute);
}
.pw-toggle:hover { background: var(--surface-2); color: var(--text); transform: none; box-shadow: none; }

/* generate + copy row for admin-set temporary passwords */
.pw-generate-row { display: flex; gap: 8px; }
.pw-generate-row input { flex: 1; min-width: 0; }
.pw-generate-row button { flex: none; }

.field-actions { display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: 10px; margin-top: 16px; }

/* quick actions (select all / clear / live count) that fill the empty
   right-hand side of a field-actions row next to the submit button */
.perm-quick { display: flex; align-items: center; gap: 8px; margin-left: auto; }
.perm-quick-count {
  font-family: var(--font-mono);
  font-size: calc(11.5px * var(--fs, 1));
  color: var(--text-mute);
  white-space: nowrap;
}
.perm-quick-sep { color: var(--border-hi); font-size: calc(11px * var(--fs, 1)); }
button.linklike {
  background: none;
  border: none;
  box-shadow: none;
  padding: 0;
  margin: 0;
  font-family: var(--font-mono);
  font-size: calc(11.5px * var(--fs, 1));
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  color: var(--accent);
  cursor: pointer;
  transition: color .12s ease;
}
button.linklike:hover { color: var(--accent-hi); background: none; box-shadow: none; transform: none; }

/* -------------------- /admin: accounts list -------------------- */
.acct-list { display: flex; flex-direction: column; gap: 10px; }
.acct-item {
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--surface);
}
.acct-summary {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 12px 14px;
  cursor: pointer;
  list-style: none;
}
.acct-summary::-webkit-details-marker { display: none; }
.acct-summary::marker { content: ""; }
.acct-avatar {
  flex: none;
  width: 34px;
  height: 34px;
  border-radius: 50%;
  background: var(--surface-2);
  border: 1px solid var(--border);
  color: var(--text-dim);
  font-family: var(--font-display);
  font-weight: 600;
  font-size: calc(13px * var(--fs, 1));
  display: flex;
  align-items: center;
  justify-content: center;
}
.acct-id { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.acct-id strong { font-size: calc(13.5px * var(--fs, 1)); font-family: var(--font-body); font-weight: 700; color: var(--text); }
.acct-id span { font-size: calc(11.5px * var(--fs, 1)); font-family: var(--font-mono); color: var(--text-mute); }
.acct-badges { display: flex; gap: 6px; flex-wrap: wrap; margin-left: auto; }
.acct-chevron {
  flex: none;
  margin-left: 4px;
  color: var(--text-mute);
  font-family: var(--font-mono);
  font-size: calc(11px * var(--fs, 1));
  transition: transform .15s var(--ease);
}
details.acct-item[open] .acct-chevron { transform: rotate(180deg); }
.acct-body {
  padding: 4px 14px 16px;
  border-top: 1px solid var(--border);
  display: flex;
  flex-direction: column;
  gap: 16px;
}
.acct-section-label {
  font-family: var(--font-mono);
  font-size: calc(10.5px * var(--fs, 1));
  text-transform: uppercase;
  letter-spacing: 0.03em;
  color: var(--text-mute);
  margin: 12px 0 8px;
}
.acct-section-label:first-child { margin-top: 12px; }
.acct-danger-row {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  align-items: center;
  padding-top: 12px;
  border-top: 1px dashed var(--border);
}

/* permission chips — checkbox + label styled as a toggle pill so a dozen
   permissions read as a scannable grid instead of a checkbox wall */
.perm-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
  gap: 8px;
}
.perm-chip {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px 10px;
  border: 1px solid var(--border-hi);
  border-radius: var(--radius-sm);
  font-size: calc(12px * var(--fs, 1));
  font-family: var(--font-body);
  color: var(--text-dim);
  cursor: pointer;
  transition: border-color .12s ease, background .12s ease, color .12s ease;
}
.perm-chip:has(input:checked) {
  border-color: var(--accent);
  background: var(--accent-dim);
  color: var(--accent-hi);
}
.perm-chip input {
  appearance: none;
  flex: none;
  width: 14px;
  height: 14px;
  border: 1px solid var(--border-hi);
  border-radius: 3px;
  background: var(--surface);
  cursor: pointer;
  display: inline-grid;
  place-content: center;
}
.perm-chip input:checked { background: var(--accent); border-color: var(--accent); }
.perm-chip input:checked::after {
  content: "";
  width: 4px; height: 7px;
  border: solid #fff;
  border-width: 0 2px 2px 0;
  transform: rotate(45deg) translate(-1px, -1px);
}

/* explicit danger button — was only a hover accent before */
button.danger { color: var(--err); border-color: var(--err); background: transparent; }
button.danger:hover { background: var(--err); border-color: var(--err); color: #fff; }

.empty-note {
  padding: 20px;
  text-align: center;
  color: var(--text-mute);
  font-size: calc(12.5px * var(--fs, 1));
  font-family: var(--font-body);
}

@media (max-width: 620px) {
  .profile-row { flex-wrap: wrap; }
  .profile-badges { margin-left: 0; }
  .acct-summary { flex-wrap: wrap; }
  .acct-badges { margin-left: 0; width: 100%; }
}

/* ==========================================================================
   Neumorphic theme override
   Soft-UI reskin: flat same-tone surfaces, depth from dual light/dark
   shadows instead of borders/flat drop-shadows. Layered on top of the
   existing rules above (kept intact) so it can be dropped by deleting
   this block. Redefining --bg/--surface/--border/--shadow-* here also
   quietly fixes every :hover/:disabled rule above that already reads
   those tokens (e.g. button:hover, .card:hover, .app-card-expand:hover).
   ========================================================================== */
:root {
  --neu-bg:      #e6ebf3;
  --neu-light:   #ffffff;
  --neu-dark:    #a9b3c7;   /* deeper + more saturated than the old #b7c1d1 —
                               low contrast against --neu-bg was the main
                               reason shadows read as haze instead of depth */
  --neu-edge:    rgba(23, 32, 48, 0.32);  /* corporate pass: was a near-invisible
                               0.07 hairline (app-style, borders you can't
                               actually see); raised to a clearly visible
                               line so every card/panel reads as a bordered
                               block, not a same-tone blob relying on shadow */

  --neu-radius-lg: 8px;   /* was 18px — rounded "app bubble" corners flattened
                              to a clean, corporate-document radius */
  --neu-radius:    6px;   /* was 12px */
  --neu-radius-sm: 4px;   /* was 8px */

  /* Corporate pass: soft-UI dual glow cut way down — short blur, low
     offset, so surfaces read as a flat bordered panel with a faint lift,
     not a glowing pillow. (was 6/6/12 and 3/3/6) */
  --neu-raised:     1px 1px 3px var(--neu-dark), -1px -1px 3px var(--neu-light);
  --neu-raised-sm:  1px 1px 2px var(--neu-dark), -1px -1px 2px var(--neu-light);
  --neu-inset:      inset 2px 2px 4px var(--neu-dark), inset -2px -2px 4px var(--neu-light);
  --neu-inset-sm:   inset 1px 1px 2px var(--neu-dark), inset -1px -1px 2px var(--neu-light);

  /* Accent (brand-blue) shadow pair — tightened further alongside the
     neutral pair above so primary buttons match the flatter language
     instead of standing out as the one still-glowing element. */
  --neu-accent-raised: 1px 1px 4px rgba(15,50,110,0.45), -1px -1px 3px rgba(150,185,240,0.22);
  --neu-accent-inset:  inset 1px 1px 3px rgba(15,50,110,0.5), inset -1px -1px 3px rgba(150,185,240,0.18);

  /* Re-point the base tokens so every existing var(--shadow-*)/var(--surface)/
     var(--border) reference in the file above resolves to the neumorphic
     values automatically. */
  --bg:         var(--neu-bg);
  --surface:    var(--neu-bg);
  --surface-2:  var(--neu-bg);
  --border:     var(--neu-edge);
  --border-hi:  var(--neu-edge);
  --shadow-1:   var(--neu-raised-sm);
  --shadow-2:   var(--neu-raised);
  --shadow-3:   var(--neu-raised);
}

body { background-image: none; background: var(--neu-bg); }

/* Raised containers — cards, panels, rails, rows, dockable groups.
   A 1px hairline (--neu-edge) rides along with the soft shadow so each
   shape has a crisp boundary even where its own tone nearly matches the
   floor — that boundary is what was missing before, not the shadow. */
.card, .panel, .app-card, .app-card-expand, .app-card-options, .rail, .rail-card,
.review-card, .review-map-row, .results-dock, .results-strip, .result-download-panel,
.progress-panel, .auth-shell, .acct-item, .month-countdown, .merge-sample,
.hint-banner, .app-nav, .log, .perm-grid, .field, .dropzone, .target-field-card,
.toast-card, .summary-modal-panel, .uploaded-row, .supplier-row,
.table-scroll, .tab-bar, .admin-grid {
  background: var(--neu-bg) !important;
  border: 1px solid var(--neu-edge) !important;
  box-shadow: var(--neu-raised) !important;
  border-radius: var(--neu-radius-lg) !important;
}

/* Recessed — text inputs, selects, search fields, progress groove, ticks */
input, select, textarea, .table-search input {
  background: var(--neu-bg) !important;
  border: none !important;
  box-shadow: var(--neu-inset-sm) !important;
  border-radius: var(--neu-radius) !important;
}
input:focus, select:focus, textarea:focus {
  box-shadow: var(--neu-inset-sm), 0 0 0 2px var(--accent) !important;
  outline: none;
}
/* .home-search-input already sits inside .home-search-box, which supplies
   its own pressed-in pill shadow -- letting the generic input rule above
   also stamp a shadow+radius on the input created a double bezel (a pill
   inside a pill). The input here is just flat text on top of its parent. */
.home-search-input {
  box-shadow: none !important;
  border-radius: 0 !important;
}
.home-search-input:focus {
  box-shadow: none !important;
}
.progress-track {
  background: var(--neu-bg) !important;
  box-shadow: var(--neu-inset-sm) !important;
  border: none !important;
}
.progress-fill { box-shadow: var(--neu-raised-sm) !important; border: none !important; }
.readiness-list .tick {
  border-radius: 50% !important;
  border: none !important;
}
.readiness-list li.check-pending .tick {
  background: var(--neu-bg) !important;
  box-shadow: var(--neu-inset-sm) !important;
}
.readiness-list li.check-ok .tick {
  box-shadow: 2px 2px 5px rgba(44,138,86,0.45), -1px -1px 3px rgba(255,255,255,0.6) !important;
}
.readiness-list li { border-left: none !important; }

/* Buttons — raised at rest, recessed while pressed or disabled */
button, .btn-icon, .cta, .back-btn, .info-btn, .pw-toggle, .tab-btn,
.summary-nav-btn, .summary-modal-close {
  background: var(--neu-bg) !important;
  border: 1px solid var(--neu-edge) !important;
  box-shadow: var(--neu-raised-sm) !important;
  border-radius: var(--neu-radius) !important;
}
button:active, .btn-icon:active, .cta:active, .tab-btn:active {
  box-shadow: var(--neu-inset-sm) !important;
}
button:disabled, button[disabled] {
  box-shadow: var(--neu-inset-sm) !important;
  opacity: 0.5;
}

/* Primary / accent actions keep brand-blue fill, colored neumorphic shadow.
   The inset top highlight is what makes a flat gradient fill read as a
   distinct, pressable object instead of a soft blue smudge. */
button.primary, .tab-btn.active {
  background: linear-gradient(145deg, var(--brand-blue-light), var(--brand-blue)) !important;
  color: #fff !important;
  border: 1px solid rgba(15,50,110,0.35) !important;
  box-shadow: var(--neu-accent-raised), inset 0 1px 0 rgba(255,255,255,0.28) !important;
}
button.primary:active, .tab-btn.active:active {
  box-shadow: var(--neu-accent-inset) !important;
}
button.primary:disabled {
  background: var(--neu-bg) !important;
  border-color: var(--neu-edge) !important;
  box-shadow: var(--neu-inset-sm) !important;
  color: var(--text-mute) !important;
  /* The muted text color above is what should read as "disabled" here --
     stacking the generic button:disabled 50% opacity on top of it faded
     the label into the background entirely (the bug being fixed). */
  opacity: 1;
}

/* .btn-icon inherits the same raised-disc treatment as real buttons from
   the shared rule above (dual light+dark shadow) -- against a flat
   colored surface like .primary's blue fill that dual shadow has no dark
   side to grab onto, so the pale highlight half smears into a foggy halo
   instead of reading as a small crisp badge. Tighten it there specifically
   (single soft shadow, tinted to the button instead of stark white) while
   leaving .btn-icon alone everywhere else it already sits on a matching
   --neu-bg surface and looks fine. */
button.primary .btn-icon, .tab-btn.active .btn-icon {
  background: rgba(255,255,255,0.18) !important;
  border: none !important;
  box-shadow: 1px 1px 3px rgba(10,25,60,0.35) !important;
}
button.primary:disabled .btn-icon {
  background: var(--neu-bg) !important;
  box-shadow: var(--neu-raised-sm) !important;
}

/* Nav tabs — active tab reads as a pressed-in button */
.app-nav-item { box-shadow: none !important; background: transparent !important; border: none !important; border-radius: var(--neu-radius) !important; }
.app-nav-item.active { box-shadow: var(--neu-inset-sm) !important; background: var(--neu-bg) !important; }

/* Badges, pills, tags — small raised chips */
.badge, .pill, .branch-pill, .perm-chip, .app-card-tag, .review-status-badge, .status {
  border: 1px solid var(--neu-edge) !important;
  box-shadow: var(--neu-raised-sm) !important;
  border-radius: 999px !important;
}

/* ---------- dark neumorphism: the topbar ----------
   Everything above assumes a light --neu-bg floor, so its shadows are
   built from white + grey. The topbar keeps its dark --ink background
   (brand identity bar) — dropping those same white-based shadows onto a
   dark surface blows out into a glowing halo instead of a soft shadow.
   These rules give topbar children their own dark-on-dark shadow pair
   (lighter ink / darker ink, no white) so they look raised, not lit up. */
.topbar {
  --topbar-hi:  #2c313a;   /* lighter tint of --ink, for the "raised" highlight edge */
  --topbar-lo:  #0f1115;   /* darker tint of --ink, for the "raised" shadow edge */
  --topbar-raised:    3px 3px 7px var(--topbar-lo), -3px -3px 7px var(--topbar-hi);
  --topbar-inset:     inset 2px 2px 5px var(--topbar-lo), inset -2px -2px 5px var(--topbar-hi);
}
.topbar .back-btn, .topbar .info-btn, .topbar .crumb, .topbar .month-countdown,
.topbar button, .topbar .cta button {
  background: var(--ink) !important;
  border: none !important;
  box-shadow: var(--topbar-raised) !important;
  color: #b9c2d6;
}
.topbar .back-btn:active, .topbar .info-btn:active, .topbar .crumb:active,
.topbar button:active, .topbar .cta button:active {
  box-shadow: var(--topbar-inset) !important;
}
.topbar .back-btn:hover, .topbar .crumb:hover { color: #fff; }
.topbar .back-btn, .topbar .info-btn { border-radius: 50% !important; }
.topbar .crumb, .topbar .month-countdown { border-radius: 999px !important; }
.topbar .month-countdown-num { color: var(--ok); }
/* Primary CTA (e.g. "Generate order form(s)") keeps its blue fill for
   emphasis, but the shadow drops the pale glow-prone rgba highlight in
   favor of the same dark-on-dark recipe. */
.topbar button.primary, .topbar .cta button.primary {
  background: linear-gradient(145deg, var(--brand-blue-light), var(--brand-blue)) !important;
  color: #fff !important;
  box-shadow: var(--topbar-raised) !important;
}
.topbar button.primary:active { box-shadow: var(--topbar-inset) !important; }

/* ==========================================================================
   PO Order Form — page-scoped redesign
   Reuses the home-* header component (fixed, shared across the app) and
   introduces a po-* namespace for the page body so nothing here leaks into
   the other tool pages that still use the older .card/.wrap system.
   ========================================================================== */
.po-shell {
  max-width: 1320px;
  margin: 0 auto;
  padding: var(--space-4) 18px var(--space-5);
}

.po-hero {
  display: flex;
  align-items: center;
  gap: var(--space-4);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow-1);
  padding: var(--space-4) var(--space-5);
  margin-bottom: var(--space-4);
}
.po-hero-badge {
  flex: none;
  width: 46px;
  height: 46px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--accent);
  color: var(--accent-ink);
  font-family: var(--font-display);
  font-weight: 600;
  font-size: calc(13px * var(--fs, 1));
  letter-spacing: 0.02em;
}
.po-hero-text h1 {
  margin: 0;
  font-family: var(--font-display);
  font-weight: 600;
  font-size: calc(19px * var(--fs, 1));
  color: var(--text);
}
.po-hero-text p {
  margin: 2px 0 0;
  font-size: calc(12.5px * var(--fs, 1));
  color: var(--text-mute);
}

.po-columns {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 340px;
  gap: var(--space-3);
  align-items: start;
}
@media (max-width: 980px) {
  .po-columns { grid-template-columns: 1fr; }
}

/* Opt-in: stretches both columns to the height of the taller one, so a
   po-card-grow card in the side rail can expand and its bottom edge lines
   up with the bottom of whatever the main column ends on. */
.po-columns-stretch { align-items: stretch; }

.po-col-main, .po-col-side {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  min-width: 0;
}

.po-card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow-1);
  padding: var(--space-3) var(--space-4) var(--space-4);
}
.po-card-compact { padding: var(--space-3) var(--space-4); }
.po-card-grow { flex: 1; min-height: 0; display: flex; flex-direction: column; }
.po-card-grow .results-dock { flex: 1; min-height: 120px; }

.po-eyebrow {
  margin: 0 0 var(--space-3);
  font-family: var(--font-mono);
  font-size: calc(10.5px * var(--fs, 1));
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-mute);
}

.po-card-title-row {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  margin-bottom: var(--space-2);
}
.po-card-title-row h2 {
  margin: 0;
  font-family: var(--font-display);
  font-weight: 600;
  font-size: calc(15px * var(--fs, 1));
  color: var(--text);
  flex: 1;
  min-width: 0;
}
.po-card-icon {
  flex: none;
  width: 28px;
  height: 28px;
  border-radius: 8px;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--accent-dim);
  color: var(--accent);
}
.po-card-icon svg { width: 17px; height: 17px; }
.po-card-icon-accent { background: var(--accent); color: var(--accent-ink); }

.po-count-chip {
  flex: none;
  font-family: var(--font-mono);
  font-size: calc(11px * var(--fs, 1));
  font-weight: 600;
  color: var(--text-dim);
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: 999px;
  padding: 2px 9px;
}

/* A second labeled section inside one modal's body -- e.g. Add Branch's
   detected-branches checklist sitting below its Workbook dropzone, the
   same way Branch Organizer's master-file summary sits below its own
   dropzone. Mirrors .po-card-title-row's icon+heading treatment, with a
   hairline divider above so it reads as a distinct section rather than
   a continuation of the dropzone above it. */
.po-modal-subhead {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  margin: var(--space-4) 0 var(--space-2);
  padding-top: var(--space-4);
  border-top: 1px solid var(--border);
}
.po-modal-subhead h3 {
  margin: 0;
  font-family: var(--font-display);
  font-weight: 600;
  font-size: calc(13.5px * var(--fs, 1));
  color: var(--text);
  flex: 1;
  min-width: 0;
}

/* Dropzone / selected files -- scoped so the shared .dropzone class used by
   other tool pages (barcode, intransit, organizer) is untouched. */
.po-shell .dropzone {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  width: 100%;
  border: 1.5px dashed var(--border-hi);
  border-radius: var(--radius);
  background: var(--surface-2);
  padding: 30px var(--space-4);
  text-align: center;
  cursor: pointer;
  transition: border-color .15s, background .15s;
}
.po-shell .dropzone:hover { border-color: var(--accent) !important; background: var(--accent-dim) !important; }
.po-shell .dropzone.drag-over { border-color: var(--accent) !important; background: var(--accent-dim) !important; }
/* Base .dropzone paints its own compact icon + "Drag files here, or" copy via
   pseudo-elements for the single-row layout used elsewhere. The PO shell
   uses real markup (.po-dropzone-icon / .po-dropzone-text) for a roomier
   card layout instead, so switch those off here to avoid double copy. */
.po-shell .dropzone::before { content: none; }
.po-shell .dropzone::after { content: none; }
.po-dropzone-icon {
  width: 30px;
  height: 30px;
  color: var(--accent);
  margin-bottom: 12px;
  padding: 10px;
  border-radius: 999px;
  background: var(--accent-dim);
  box-sizing: content-box;
}
.po-dropzone-text {
  margin: 0 0 3px;
  font-size: calc(14px * var(--fs, 1));
  font-weight: 600;
  color: var(--text);
}
.po-shell .dropzone .small {
  margin: 0;
  font-size: calc(12px * var(--fs, 1));
}
.po-link-btn {
  background: none;
  border: none;
  padding: 0;
  font: inherit;
  font-weight: 600;
  color: var(--accent);
  cursor: pointer;
}
.po-link-btn:hover { color: var(--accent-hi); text-decoration: underline; }
.po-shell .dropzone button.po-link-btn { padding: 0; font-size: inherit; }

.po-selected-row {
  margin-top: var(--space-3);
}

/* Required Files table */
.po-table-wrap { overflow-x: auto; border: 1px solid var(--border); border-radius: var(--radius-sm); }
.po-shell #requiredFilesTable { width: 100%; border-collapse: collapse; font-size: calc(12.5px * var(--fs, 1)); }
.po-shell #requiredFilesTable thead th {
  text-align: left;
  font-family: var(--font-mono);
  font-size: calc(10.5px * var(--fs, 1));
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--text-mute);
  background: var(--surface-2);
  padding: 9px 12px;
  border-bottom: 1px solid var(--border);
}
.po-shell #requiredFilesTable td {
  padding: 10px 12px;
  border-bottom: 1px solid var(--border);
  vertical-align: middle;
  color: var(--text);
}
.po-shell #requiredFilesTable tbody tr:last-child td { border-bottom: none; }
.po-shell #requiredFilesTable tbody tr:nth-child(even) { background: var(--surface-2); }
.req-index { font-family: var(--font-mono); color: var(--text-mute); width: 28px; }
.req-example {
  display: block;
  font-family: var(--font-mono);
  font-size: calc(11px * var(--fs, 1));
  color: var(--text-mute);
  margin-top: 2px;
}
.req-actions { color: var(--text-mute); text-align: center; }
.req-status-pill {
  display: inline-block;
  font-family: var(--font-mono);
  font-size: calc(11px * var(--fs, 1));
  font-weight: 600;
  padding: 3px 9px;
  border-radius: 999px;
}
.req-status-pill.pending { background: var(--err-dim); color: var(--err); }
.req-status-pill.ok { background: var(--ok-dim); color: var(--ok); }

/* Folder path card */
.po-folder-note {
  display: flex;
  gap: 10px;
  align-items: flex-start;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: var(--space-3);
}
.po-folder-note svg { width: 19px; height: 19px; flex: none; color: var(--accent); margin-top: 1px; }
.po-folder-note-text {
  /* Flex items default to a min-width based on their content, so a long
     unbroken string (a Windows folder path, all backslashes, no spaces)
     used to refuse to shrink and just poked out past the card's edge
     instead of wrapping. min-width: 0 lets it shrink to the available
     space like a normal block; overflow-wrap below then wraps the path
     itself onto multiple lines instead of overflowing. */
  min-width: 0;
  flex: 1;
}
.po-folder-note strong {
  display: block;
  font-size: calc(12.5px * var(--fs, 1));
  color: var(--text);
  overflow-wrap: anywhere;
  word-break: break-word;
}
.po-folder-note .small { display: block; margin-top: 2px; }
.po-shell .advanced { margin-top: var(--space-3); }

.po-status-line { margin: 0; font-family: var(--font-mono); font-size: calc(12.5px * var(--fs, 1)); font-weight: 600; }
.po-status-line.ok { color: var(--ok); }
.po-status-line.pending { color: var(--warn); }

/* Generation progress ring */
.po-progress-body { display: flex; flex-direction: column; gap: var(--space-3); }
.po-progress-idle-hint { margin: 0; }
.po-ring-row { display: flex; align-items: center; gap: var(--space-3); }
.po-ring { position: relative; width: 64px; height: 64px; flex: none; }
.po-ring svg { width: 100%; height: 100%; transform: rotate(-90deg); }
.po-ring-track { fill: none; stroke: var(--border); stroke-width: 8; }
.po-ring-fill {
  fill: none;
  stroke: var(--accent);
  stroke-width: 8;
  stroke-linecap: round;
  stroke-dasharray: 263.89;
  stroke-dashoffset: 263.89;
  transition: stroke-dashoffset 0.3s var(--ease);
}
.po-ring-label {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: var(--font-mono);
  font-weight: 700;
  font-size: calc(13px * var(--fs, 1));
  color: var(--text);
}
.po-ring-copy { min-width: 0; }
.po-ring-copy strong { display: block; font-size: calc(13px * var(--fs, 1)); color: var(--text); }
.po-ring-copy .small { display: block; margin-top: 2px; }
.po-progress-meta {
  display: flex;
  justify-content: space-between;
  gap: var(--space-2);
  font-size: calc(11px * var(--fs, 1));
  color: var(--text-mute);
  flex-wrap: wrap;
}
.po-progress-meta span { display: inline-flex; align-items: center; gap: 5px; }
.po-progress-meta svg { width: 15px; height: 15px; }

/* Actions card */
.po-actions-row { display: flex; flex-direction: column; gap: 8px; }
.po-actions-row button { width: 100%; }
.po-shell #actionsCard .status { display: block; margin-top: 8px; }

/* ==========================================================================
   PO Order Form — header identity badge
   Replaces the LYR logo image with this page's own circular badge, same
   size/shadow language as .home-logo-badge so the header row still reads
   as one component.
   ========================================================================== */
.home-logo-badge-po {
  background: var(--neu-blue);
  color: var(--neu-blue-ink);
  font-family: var(--font-display);
  font-weight: 700;
  font-size: calc(13px * var(--fs, 1));
  letter-spacing: 0.02em;
}

/* ==========================================================================
   PO Order Form — compact configuration cards + modal overlays
   Replaces the old always-expanded sections with small trigger cards that
   open a focused modal per section. All original controls/ids live inside
   the modal bodies untouched; only their container's visibility changes.
   ========================================================================== */
.po-section-label {
  margin: 0 0 var(--space-3);
  font-family: var(--font-body);
  font-size: calc(12px * var(--fs, 1));
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--text-mute);
}

.po-config-grid {
  display: grid;
  gap: var(--space-3);
}
.po-config-grid-2 { grid-template-columns: repeat(2, 1fr); }
.po-config-grid-3 { grid-template-columns: repeat(3, 1fr); }
.po-config-grid-4 { grid-template-columns: repeat(4, 1fr); }
@media (max-width: 860px) {
  .po-config-grid-2, .po-config-grid-3, .po-config-grid-4 { grid-template-columns: 1fr; }
}
@media (min-width: 861px) and (max-width: 1180px) {
  .po-config-grid-3, .po-config-grid-4 { grid-template-columns: repeat(2, 1fr); }
}

.po-trigger-card {
  position: relative;
  display: flex;
  align-items: center;
  gap: var(--space-3);
  width: 100%;
  text-align: left;
  font: inherit;
  color: inherit;
  cursor: pointer;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow-1);
  padding: var(--space-4);
  transition: border-color .15s var(--ease), box-shadow .15s var(--ease);
}
.po-trigger-card:hover { border-color: var(--border-hi); box-shadow: var(--shadow-2); }
.po-trigger-card:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

.po-trigger-icon {
  flex: none;
  width: 40px;
  height: 40px;
  border-radius: 50%;
  background: var(--surface-2);
  color: var(--text-dim);
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
.po-trigger-icon svg { width: 21px; height: 21px; }

.po-trigger-body { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 3px; }
.po-trigger-title-row { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.po-trigger-title {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: calc(15px * var(--fs, 1));
  color: var(--text);
}
.po-trigger-desc { margin: 0; font-size: calc(12.5px * var(--fs, 1)); color: var(--text-mute); }
.po-trigger-badge {
  flex: none;
  font-family: var(--font-mono);
  font-size: calc(11px * var(--fs, 1));
  font-weight: 600;
  color: var(--text-dim);
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: 999px;
  padding: 2px 9px;
  white-space: nowrap;
}
.po-trigger-chevron { flex: none; width: 18px; height: 18px; color: var(--text-mute); }
.po-trigger-badge-optional {
  background: transparent;
  border-style: dashed;
  color: var(--text-mute);
  font-family: var(--font-body);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  font-size: calc(10px * var(--fs, 1));
}

/* Compact variant — Branch / Working Days / Stocking Days / Suppliers */
.po-trigger-card-compact {
  flex-direction: column;
  align-items: flex-start;
  gap: 4px;
  padding: var(--space-4) 40px var(--space-4) var(--space-4);
}
.po-trigger-card-compact .po-trigger-top-row {
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 6px;
}
.po-trigger-card-compact .po-trigger-title { display: block; }
.po-trigger-card-compact .po-trigger-desc { display: block; }
.po-trigger-chevron-compact {
  position: absolute;
  right: var(--space-4);
  bottom: var(--space-4);
}

/* Readiness — full width beneath the left column only, grid of checklist
   items instead of the narrow single-column list used in the sidebar
   version of this component on other pages. Scoped to this panel so the
   shared .readiness-list class is untouched elsewhere in the suite. */
.po-readiness-panel .po-readiness-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 2px var(--space-5);
}
@media (max-width: 720px) {
  .po-readiness-panel .po-readiness-grid { grid-template-columns: 1fr; }
}
.po-readiness-panel .po-readiness-grid li {
  border-left: none;
  padding: 10px 0;
  border-bottom: 1px solid var(--border);
  font-size: calc(11.5px * var(--fs, 1));
}
.po-readiness-panel .po-readiness-grid li:nth-last-child(-n+3) { border-bottom: none; }
@media (max-width: 720px) {
  .po-readiness-panel .po-readiness-grid li:last-child { border-bottom: none; }
}
.po-readiness-panel .po-readiness-grid .tick {
  width: 18px;
  height: 18px;
  border-radius: 50%;
  background: var(--surface-2);
  border-color: var(--border-hi);
}
.po-readiness-panel .po-readiness-grid li.check-ok .tick {
  background: var(--ok-dim);
  color: var(--ok);
  border-color: transparent;
  animation: none;
}

.po-process-log { margin-top: var(--space-4); }

/* ---------- Modal overlay ----------
   Same open/close convention as .summary-modal (class toggle + aria-hidden,
   driven by static/order-form-modals.js). Every modal shares one fixed
   panel size regardless of how much content it holds -- the head stays put
   and .po-modal-body scrolls internally -- so switching between Source
   Files, Branch Settings, etc. doesn't jump the dialog to a different size. */
.po-modal {
  position: fixed;
  inset: 0;
  z-index: 120;
  display: flex;
  align-items: flex-start;
  justify-content: center;
  padding: 48px 16px;
  overflow-y: auto;
  opacity: 0;
  visibility: hidden;
  transition: opacity .18s var(--ease), visibility 0s linear .18s;
}
.po-modal.is-open {
  opacity: 1;
  visibility: visible;
  transition: opacity .18s var(--ease);
}
.po-modal-backdrop {
  position: fixed;
  inset: 0;
  background: rgba(16, 17, 18, 0.55);
  backdrop-filter: blur(2px);
}
.po-modal-panel {
  position: relative;
  display: flex;
  flex-direction: column;
  width: 100%;
  max-width: 720px;
  height: auto;
  max-height: calc(100vh - 96px);
  background: var(--surface);
  border: 1px solid var(--border-hi);
  border-top: 3px solid var(--brand-blue);
  border-radius: var(--radius);
  overflow: hidden;
  box-shadow: var(--shadow-3);
  padding: 0;
  transform: translateY(8px) scale(0.98);
  transition: transform .18s var(--ease);
}
.po-modal.is-open .po-modal-panel { transform: translateY(0) scale(1); }
.po-modal-head {
  flex: none;
  display: flex;
  align-items: center;
  gap: var(--space-3);
  margin: 0;
  padding: var(--space-4) var(--space-5);
  background: var(--surface-2);
  border-bottom: 1px solid var(--border-hi);
}
/* Tab content uses the same font treatment as the header strip/sidebar
   (font-display, 12.5px, weight 600, normal case) instead of the plain
   body font, so a tab's labels/buttons/table text read as one interface
   language with the chips and nav around it rather than switching fonts
   once you're "inside" a tab. This is a default, not a hard override --
   font-family/size/weight are inherited, so any element that already
   sets its own on purpose (mono badges/status pills/monospaced counters,
   the h2 title, .po-modal-save) keeps that instead, since a specified
   value on the element itself always wins over an inherited one. */
.po-modal-panel {
  font-family: var(--font-display);
  font-size: calc(12.5px * var(--fs, 1));
  font-weight: 600;
  letter-spacing: normal;
  text-transform: none;
}
.po-modal-body {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  padding: var(--space-4) var(--space-5) var(--space-5);
  display: flex;
  flex-direction: column;
}
/* Each modal has one "main content" element -- a list or table -- that
   should claim whatever vertical room the fixed-height panel has left,
   instead of sitting at a small capped height with empty space below it. */
.po-modal-body .supplier-list {
  flex: 1;
  min-height: 0;
  max-height: none;
}
.po-modal-body .po-selected-row {
  flex: 1;
  min-height: 0;
  max-height: none;
}
/* #outgoingList is the one .po-selected-row that never holds more than one
   row -- Outgoing Register uploads replace each other, they don't add up
   (see intransit_upload_outgoing() server-side). Letting it flex-grow like
   a genuinely long list (Receiving Files, which shares this same class and
   really can hold dozens) just stretches one row across the whole modal
   and shoves the destination-branch breakdown below it down past a wall of
   empty space. Pin it to its content height and let #outgoingSummary --
   the part that actually grows with the file (branches, month-span note)
   -- claim the room instead. */
#outgoingList.po-selected-row { flex: none; }
#outgoingSummary {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
}
.po-modal-body .po-table-wrap {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
}
.po-modal-body .card-stack {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
}
.po-modal-head h2 {
  flex: 1;
  min-width: 0;
  margin: 0;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: calc(16px * var(--fs, 1));
  letter-spacing: 0.02em;
  text-transform: uppercase;
  color: var(--text);
}
/* Save button for modals whose whole body is one scrollable list with a
   single override action (Working Days per branch, Stocking Days) -- sits
   in the head next to the close button instead of trailing at the bottom
   of the scrollable list, where it could be scrolled out of view. */
.po-modal-save {
  flex: none;
  padding: 6px 14px;
  font-size: calc(13px * var(--fs, 1));
  font-weight: 600;
  font-family: var(--font-body);
  border-radius: var(--radius-sm) !important;
  background: linear-gradient(145deg, var(--brand-blue-light), var(--brand-blue)) !important;
  color: #fff !important;
  border: 1px solid rgba(15,50,110,0.35) !important;
  box-shadow: var(--shadow-2) !important;
  cursor: pointer;
  transition: transform .1s ease, box-shadow .15s ease;
}
.po-modal-save:hover { transform: translateY(-1px); box-shadow: var(--shadow-3) !important; }
.po-modal-save:active { transform: translateY(0); }
.po-modal-close {
  flex: none;
  width: 30px;
  height: 30px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: calc(20px * var(--fs, 1));
  line-height: 1;
  color: var(--text-mute);
  background: none;
  border: none;
  box-shadow: none;
  cursor: pointer;
  border-radius: 8px;
}
.po-modal-close:hover { color: var(--text); background: var(--surface-2); }
body.po-modal-lock { overflow: hidden; }

/* ---------- Docked canvas panel ----------
   On pages with a `.po-canvas` free-space area (see the compact-canvas
   block further down), static/order-form-modals.js moves a `.po-modal`'s
   real `.po-modal-panel` into this docking slot instead of popping the
   whole page under a dark backdrop. `.po-canvas-dock` is created once and
   reused for every tab on the page. Only one tab is ever docked at a time
   -- opening a new one plays this same close transition on whichever tab
   is currently open first, then the open transition for the next one, so
   each tab gets the *entire* free space to itself rather than two panels
   overlapping. Keep the .22s here in sync with CLOSE_MS in
   order-form-modals.js so the JS hands the panel back to its original
   modal exactly when the close animation finishes. */
.po-canvas-dock {
  position: absolute;
  inset: 0;
  z-index: 6;
  display: flex;
  flex-direction: column;
  min-height: 0;
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transition: opacity .18s var(--ease), visibility 0s linear .18s;
}
.po-canvas-dock.is-open {
  opacity: 1;
  visibility: visible;
  pointer-events: auto;
  transition: opacity .18s var(--ease);
}
/* Full-bleed, no chrome of its own: the open tab's content becomes the
   canvas's content -- it fills the exact rect the idle logo just occupied,
   flush, with no surrounding scrim/gap/shadow to read as a card floating
   on top of a second background panel. */
.po-canvas-dock-slot {
  position: relative;
  z-index: 1;
  flex: 1;
  width: 100%;
  min-height: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
}
/* The panel itself keeps every rule from .po-modal-panel above (head,
   body, scroll behavior) -- its card chrome (border/radius/shadow) and
   fixed-popup sizing are stripped so it reads as the canvas's own content,
   sized to the exact space available (height: 100%, not a percentage cap)
   so the inner .po-modal-body can reliably scroll instead of the panel
   just growing taller than its box. */
.po-canvas-dock-slot .po-modal-panel {
  position: static;
  width: 100%;
  max-width: none;
  height: 100%;
  max-height: none;
  flex: 1;
  min-height: 0;
  margin: 0;
  background: transparent;
  border: none;
  border-top: none;
  border-radius: 0;
  box-shadow: none;
  transform: none !important;
  transition: none !important;
}
/* Fade the canvas's own idle/progress/done state out while a tab is
   docked on top of it, so the swap between "free space content" and
   "docked panel" reads as one continuous motion instead of a hard cut. */
.po-canvas.po-canvas-docked > .po-canvas-state {
  opacity: 0;
  transition: opacity .18s ease;
}

/* ---------- unsaved-changes guard ----------
   Shown in place of the panel (over the scrim) when the user tries to
   switch tabs, close, or hit Escape while the open panel has an edit
   sitting behind its own .po-modal-save button. See guardLeave() in
   order-form-modals.js -- panels with no such Save button never trigger
   this, since their edits are already live in the in-memory model. */
.po-canvas-dock-confirm {
  position: absolute;
  inset: 0;
  z-index: 2;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 18px;
  opacity: 0;
  transition: opacity .16s var(--ease);
}
/* `hidden` needs to actually win here: an author rule that sets `display`
   on this element (above) beats the browser's built-in `[hidden] {
   display: none }` outright, since author styles always outrank the
   user-agent stylesheet regardless of specificity or source order. Without
   this, the bar never really goes away -- it just sits there at
   opacity: 0, full-bleed over the whole tab (position: absolute; inset: 0)
   with z-index: 2, silently eating every click/scroll/drop meant for the
   panel underneath. This rule's own specificity (attribute + class) beats
   the plain-class rule above so `hidden` reliably collapses it again. */
.po-canvas-dock-confirm[hidden] { display: none; }
.po-canvas-dock-confirm.is-open { opacity: 1; }
.po-canvas-dock-confirm-card {
  width: 100%;
  max-width: 360px;
  background: var(--surface);
  border: 1px solid var(--border-hi);
  border-radius: var(--radius);
  box-shadow: var(--shadow-3);
  padding: var(--space-4);
  text-align: center;
}
.po-canvas-dock-confirm-card p {
  margin: 0 0 var(--space-3);
  font-size: calc(13.5px * var(--fs, 1));
  color: var(--text);
}
.po-canvas-dock-confirm-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  justify-content: center;
}
.po-canvas-dock-confirm-actions button {
  font-family: var(--font-display);
  text-transform: none;
  letter-spacing: normal;
  font-size: calc(12.5px * var(--fs, 1));
}

/* ==========================================================================
   PO Order Form — compact strip + stateful canvas (order-form.html only)
   All new po-strip-*/po-canvas-* class names, plus one .po-compact-canvas
   modifier added alongside the existing .po-shell class. None of the
   shared .po-columns/.po-col-*/.po-trigger-card/.po-card rules above are
   touched, so Barcode/Intransit*/Organizer (which still render the old
   trigger-card layout with those same classes) are unaffected. This block
   is the template to lift onto those pages later.
   ========================================================================== */

/* .po-shell normally sizes to its own content; here it needs to stretch so
   the canvas below the strip can claim the rest of the viewport instead of
   collapsing to the idle logo's own height. The fixed .home-topbar is out
   of normal flow (position: fixed) and .home-header-spacer already stands
   in for its height, so stretching the shared flex-column .home-wrap to
   100vh and letting .po-shell grow into the remaining space is enough --
   no page-specific pixel math needed. */
.home-wrap.home-neu:has(> .po-shell.po-compact-canvas) { min-height: 100vh; }
.po-shell.po-compact-canvas {
  display: flex;
  flex-direction: column;
  flex: 1;
  min-height: 0;
  /* .po-shell's base rule above sets `margin: 0 auto` for its old
     plain-block centering. As a flex item of .home-wrap here, auto
     margins on the cross axis (width) override stretch and shrink the
     whole shell to its content's min width instead of filling the row --
     that's what was squeezing the strip/canvas/modal down to ~226px
     regardless of viewport size. Zero it out so flex stretch applies. */
  margin: 0;
  width: 100%;
}

/* ---------- flatten the inherited neumorphic re-skin ----------
   The ancestor .home-neu wrap re-points --surface/--border/--border-hi/
   --shadow-1/2/3 to soft-UI "raised"/"pressed" double-shadow values for
   the older card+sidebar screens (see the .home-neu token block). The
   compact strip + canvas redesign was built flat on purpose -- hairline
   borders, no embossed/inset shadows -- and now that tabs dock their real
   panel *into* the canvas instead of popping up separately, that panel
   inherits the same neumorphic tokens too unless it's opted back out here.
   Re-pointing the shared tokens back to the plain :root values for
   everything inside this shell keeps the docked panel's card, buttons,
   inputs, dropzone and tables visually consistent with the strip/canvas
   around it instead of reading as a leftover embossed popup. */
.po-shell.po-compact-canvas {
  --surface: #FFFFFF;
  --surface-2: #f4f5f7;
  --border: #dde1e7;
  --border-hi: #b7bec9;
  --shadow-1: 0 1px 2px rgba(16,24,40,0.04), 0 1px 3px rgba(16,24,40,0.05);
  --shadow-2: 0 2px 8px rgba(16,24,40,0.06);
  --shadow-3: 0 16px 40px rgba(16,24,40,0.16), 0 2px 8px rgba(16,24,40,0.08);
}

/* ---------- compact configuration strip ----------
   Lives in the fixed .app-header bar rather than flowing under it: same
   fixed rect (left/right/height), centered on top of it up to .po-shell's
   own 1320px cap so the chips line up with the canvas underneath, with no
   background of its own so the header's white fill shows through. */
.po-strip {
  position: fixed;
  top: 0;
  left: var(--nav-w);
  right: 0;
  height: var(--header-h);
  max-width: 1320px;
  margin: 0 auto;
  z-index: 31;
  display: flex;
  align-items: center;
  flex-wrap: nowrap;
  overflow-x: auto;
  overflow-y: hidden;
  gap: 6px 10px;
  padding: 0 18px;
}
.po-strip-title {
  flex: none;
  font-family: var(--font-display);
  font-size: calc(15px * var(--fs, 1));
  font-weight: 700;
  color: var(--neu-ink);
  white-space: nowrap;
}
.po-strip-divider {
  flex: none;
  width: 1px;
  align-self: stretch;
  min-height: 20px;
  background: var(--neu-edge);
}
.po-strip-spacer { flex: 1 1 auto; min-width: 8px; }

/* Flat nav-item treatment -- same box-free approach as the home topbar's
   .wsnav-cat-btn tabs (plain text + icon, no border/background in any
   state, color shift on hover only). Kept as its own selector since these
   chips carry an icon + badge the plain topbar tabs don't, but the
   underlying "no box" rules below intentionally mirror .wsnav-cat-btn so
   future tool-page strips can adopt the same look.
   Font matches the sidebar's .app-side-item labels (font-display, mixed
   case) rather than the plain `button` element's mono/uppercase default,
   so the header reads as one piece with the nav beside it. */
.po-strip-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-family: var(--font-display);
  font-size: calc(12.5px * var(--fs, 1));
  font-weight: 600;
  text-transform: none;
  letter-spacing: normal;
  color: var(--text-dim);
  padding: 5px 1px;
  cursor: pointer;
  white-space: nowrap;
  transition: color .12s ease;
}
/* Overrides the shared neumorphic `button` rules (background/border/
   box-shadow, plus the button:hover lift) the same way .wsnav-cat-btn
   does above -- so this stays a plain text+icon label in every state. */
.po-strip-chip,
.po-strip-chip:hover,
.po-strip-chip:active {
  background: transparent !important;
  border: none !important;
  box-shadow: none !important;
  border-radius: 0 !important;
  transform: none !important;
}
.po-strip-chip:hover,
.po-strip-chip:active { color: var(--brand-blue); }
/* Set by order-form-modals.js while this chip's tab is docked open in the
   canvas below, so it's clear which panel the free space is showing. */
.po-strip-chip.is-active { color: var(--brand-blue); }
.po-strip-chip-icon { flex: none; width: 18px; height: 18px; display: inline-flex; color: inherit; }
.po-strip-chip-icon svg { width: 18px; height: 18px; }
.po-strip-chip-badge {
  font-family: var(--font-mono);
  font-size: calc(10px * var(--fs, 1));
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.02em;
  color: var(--text-mute);
  background: var(--surface-2);
  border-radius: 999px;
  padding: 1px 7px;
}

/* Same "optional" treatment as .po-trigger-badge-optional, sized for the
   strip's smaller pill instead of the old two-column trigger card's. */
.po-strip-chip-badge-optional {
  background: transparent;
  border: 1px dashed var(--border);
  color: var(--text-mute);
  font-family: var(--font-body);
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

.po-strip-actions { flex: none; display: flex; align-items: center; gap: 10px; margin-left: auto; }
.po-strip-status { font-size: calc(11px * var(--fs, 1)); }
/* Generate PO keeps button.primary's color/shape but drops the shared
   button element's mono/uppercase type in favor of the sidebar's
   font-display, mixed-case treatment, matching the chips beside it. */
.po-strip-generate {
  white-space: nowrap;
  font-family: var(--font-display);
  text-transform: none;
  letter-spacing: normal;
}

@media (max-width: 860px) {
  .po-strip-divider { display: none; }
}
@media (max-width: 640px) {
  .po-strip { padding: 0 12px; }
}

/* ---------- stateful canvas ----------
   min-height is a calc floor, not a fallback: with the strip now pinned
   into the fixed header instead of taking up flow space, this needs to
   actually reach the true remaining viewport height (100vh minus the
   header, minus .po-shell's own top/bottom padding) rather than the old
   360px, or the idle logo centers inside a too-short box and sits high
   on the page instead of in the middle of the free space below the
   header. Keep the 44/16/24 in sync with .app-header's height and
   .po-shell's padding above. */
.po-canvas {
  position: relative;
  flex: 1;
  min-height: calc(100vh - var(--header-h) - 16px - 24px);
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
}
.po-canvas-state { display: flex; align-items: center; justify-content: center; width: 100%; height: 100%; }
/* Idle state specifically: mascot stacked above a one-line caption
   (same pairing as the dashboard's "still building this page" note --
   see .wsnav-canvas-mascot / .wsnav-canvas-greeting), so every tool
   page's empty canvas reads as a small greeting instead of a bare
   logo, worded for whatever that page actually does. */
.po-canvas-idle { flex-direction: column; gap: var(--space-3); text-align: center; }
.po-canvas-idle .wsnav-canvas-greeting { max-width: 300px; }

/* Idle: reuses .wsnav-canvas-logo verbatim (defined above, home page's
   idle canvas) so this never drifts into a second divergent version. */

.po-canvas-progress-body {
  width: 100%;
  max-width: 380px;
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
  align-items: center;
  text-align: center;
}
.po-canvas-ring-row { flex-direction: column; gap: var(--space-3); }
.po-canvas-ring { width: 120px; height: 120px; }
.po-canvas-ring .po-ring-label { font-size: calc(22px * var(--fs, 1)); }
.po-canvas-progress-body .progress-track { width: 100%; }
.po-canvas-progress-body .po-progress-meta { width: 100%; justify-content: center; gap: var(--space-4); }

.po-canvas-done-body { display: flex; flex-direction: column; align-items: center; gap: var(--space-3); text-align: center; }
.po-canvas-done-icon {
  flex: none;
  width: 56px;
  height: 56px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--ok-dim);
  color: var(--ok);
}
.po-canvas-done-icon svg { width: 28px; height: 28px; }
.po-canvas-done-body strong { font-family: var(--font-display); font-size: calc(17px * var(--fs, 1)); color: var(--text); }
.po-canvas-done-body .small { margin: 0; }
.po-btn-download-canvas { min-width: 200px; }

/* ---------- icon-only generate/download buttons ----------
   Once a tool finishes its first run, the full-width Generate/Merge/Add
   button collapses down to a plain circular icon button (a "regenerate"
   glyph) instead of repeating text that no longer applies, and the
   Download button matches it. Applies wherever a button carries this
   class -- canvas-layout pages and the older sidebar-layout pages alike. */
.po-icon-btn {
  width: 44px;
  height: 44px;
  min-width: 0;
  padding: 0;
  border-radius: 50%;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0;
}
.po-icon-btn .btn-icon { width: 22px; height: 22px; margin: 0; }
.po-icon-btn > span { display: none; }

@media (max-width: 640px) {
  .po-canvas { min-height: calc(100vh - var(--header-h) - 16px - 24px); }
}

/* ==========================================================================
   Full-canvas generation progress + interactive preview grid
   -------------------------------------------------------------------------
   The old .po-canvas-progress / .po-canvas-done states were small centered
   cards. These two states now fill the whole canvas top-to-bottom (see
   .po-canvas-state.is-full overriding the centered default below) so the
   person watching generation sees an accurate, detailed account of what's
   happening -- a real step checklist and live log, not a bare percentage --
   and, once it's done, an actual interactive preview of the data instead
   of just a "your file is ready" card.
   ========================================================================== */

.po-canvas-state.is-full {
  align-items: flex-start;
  justify-content: flex-start;
  overflow-y: auto;
  padding: var(--space-5) clamp(16px, 3vw, 40px) 32px;
}

/* ---------- progress state ---------- */
.po-progress-full {
  width: 100%;
  max-width: 1080px;
  margin: 0 auto;
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
}
.po-progress-full-head {
  display: flex;
  align-items: center;
  gap: var(--space-4);
}
.po-progress-full-ring { width: 76px; height: 76px; flex: none; }
.po-progress-full-ring .po-ring-label { font-size: calc(16px * var(--fs, 1)); }
.po-progress-full-copy { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 6px; }
.po-progress-full-copy strong { font-family: var(--font-display); font-size: calc(16px * var(--fs, 1)); color: var(--text); }
.po-progress-full-copy .small { color: var(--text-dim); }

.po-progress-stats {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: var(--space-3);
}
.po-mini-stat {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 10px 14px;
  box-shadow: var(--shadow-1);
  display: flex;
  flex-direction: column;
  gap: 3px;
}
.po-mini-stat .k { font-family: var(--font-mono); font-size: calc(10px * var(--fs, 1)); letter-spacing: .03em; color: var(--text-mute); text-transform: uppercase; }
.po-mini-stat .v { font-family: var(--font-mono); font-size: calc(17px * var(--fs, 1)); font-weight: 700; color: var(--text); font-variant-numeric: tabular-nums; }

.po-progress-cols {
  display: grid;
  grid-template-columns: minmax(240px, 320px) 1fr;
  gap: var(--space-3);
  align-items: start;
}
@media (max-width: 760px) {
  .po-progress-cols { grid-template-columns: 1fr; }
}

.po-steps {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  box-shadow: var(--shadow-1);
  overflow: hidden;
  /* Same viewport-relative sizing as .po-grid-scroll's done-state table,
     instead of a flat 420px that leaves a growing dead zone below this
     panel on any window taller than ~900px. Still scrolls internally
     (overflow-y) once the step list itself runs past that height, so a
     long list never forces the whole page to scroll. */
  max-height: calc(100vh - 340px);
  overflow-y: auto;
}
.po-step {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  padding: 10px 12px;
  border-bottom: 1px solid var(--border);
  font-size: calc(12.5px * var(--fs, 1));
  line-height: 1.4;
}
.po-step:last-child { border-bottom: none; }
.po-step-dot {
  width: 18px; height: 18px; border-radius: 50%; flex: none; margin-top: 1px;
  display: flex; align-items: center; justify-content: center;
  border: 1.5px solid var(--border-hi);
  background: var(--surface);
}
.po-step-dot svg { width: 10px; height: 10px; }
.po-step.pending .po-step-label { color: var(--text-mute); }
.po-step.active .po-step-dot { border-color: var(--accent); }
.po-step.active .po-step-label { color: var(--text); font-weight: 600; }
.po-step.done .po-step-dot { background: var(--ok); border-color: var(--ok); }
.po-step.done .po-step-label { color: var(--text-dim); }
.po-step.error .po-step-dot { background: var(--err); border-color: var(--err); }
.po-step.error .po-step-label { color: var(--err); font-weight: 600; }
.po-spinner {
  width: 8px; height: 8px; border-radius: 50%;
  border: 1.5px solid var(--accent); border-top-color: transparent;
  animation: po-spin .7s linear infinite;
}
@keyframes po-spin { to { transform: rotate(360deg); } }

.po-log-panel {
  position: relative;
  background: var(--brand-black);
  border-radius: var(--radius-sm);
  overflow: hidden;
  display: flex;
  flex-direction: column;
  min-height: 300px;
  /* Same reasoning as .po-steps above: grow with the viewport instead of
     capping at 420px, so the console fills the same free space the done
     state's table already does, on both this page and the two other
     pages that share this component (Order Form, WH-to-Bs Intransit). */
  max-height: calc(100vh - 340px);
  box-shadow: var(--shadow-1);
}
/* The mascot tucks into whatever free space is left below the log
   lines -- pinned to the panel itself (not the scrolling #progressLog
   body), so it never scrolls with the text and never intercepts
   clicks. It's a smaller, dedicated size rather than the canonical
   .wsnav-canvas-mascot treatment: here it's a corner companion peeking
   into a busy console, not the main content of an idle canvas. */
.po-log-mascot {
  position: absolute;
  right: 10px;
  bottom: 0;
  width: 104px;
  height: auto;
  opacity: 0.92;
  pointer-events: none;
  z-index: 0;
}
@media (max-width: 640px) {
  .po-log-mascot { width: 78px; }
}
.po-log-head {
  padding: 8px 12px;
  font-family: var(--font-mono);
  font-size: calc(10px * var(--fs, 1));
  letter-spacing: .05em;
  color: #cbd3e0;
  border-bottom: 1px solid rgba(255,255,255,.08);
  display: flex;
  align-items: center;
  gap: 8px;
  flex: none;
}
.po-log-dots { display: flex; gap: 4px; }
.po-log-dots span { width: 6px; height: 6px; border-radius: 50%; background: rgba(255,255,255,.25); }
.po-log-body {
  flex: 1;
  overflow-y: auto;
  padding: 10px 12px;
  font-family: var(--font-mono);
  font-size: calc(11px * var(--fs, 1));
  line-height: 1.75;
}
.po-log-line { color: #d7dce5; white-space: pre-wrap; word-break: break-word; }
.po-log-line.warn { color: #eec27a; }
.po-log-time { color: #7a8699; margin-right: 8px; }

/* ---------- done state ---------- */
.po-done-full {
  width: 100%;
  max-width: 1200px;
  margin: 0 auto;
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}
.po-done-banner {
  display: flex;
  align-items: center;
  gap: 12px;
  background: var(--ok-dim);
  border: 1px solid rgba(44, 138, 86, 0.28);
  border-radius: var(--radius-sm);
  padding: 12px 16px;
}
.po-done-banner .po-canvas-done-icon { width: 36px; height: 36px; }
.po-done-banner .po-canvas-done-icon svg { width: 18px; height: 18px; }
.po-done-banner-copy { display: flex; flex-direction: column; gap: 2px; }
.po-done-banner-copy strong { font-family: var(--font-display); font-size: calc(14.5px * var(--fs, 1)); color: var(--text); }
.po-done-banner-copy .small { color: var(--text-dim); }

.po-grid-toolbar {
  display: flex;
  align-items: center;
  gap: 14px;
  flex-wrap: wrap;
}
.po-grid-hint {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: calc(11.5px * var(--fs, 1));
  color: var(--text-mute);
}
.po-grid-hint svg { width: 15px; height: 15px; flex: none; }
.po-grid-search {
  margin-left: auto;
  display: flex;
  align-items: center;
  gap: 6px;
  background: var(--surface);
  border: 1px solid var(--border-hi);
  border-radius: var(--radius-sm);
  padding: 0 10px;
  height: 30px;
}
.po-grid-search svg { width: 15px; height: 15px; color: var(--text-mute); flex: none; }
.po-grid-search input {
  border: none; outline: none; background: transparent;
  font-size: calc(12px * var(--fs, 1)); font-family: var(--font-mono); width: 160px; color: var(--text);
}

.po-grid-wrap {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  box-shadow: var(--shadow-1);
  overflow: hidden;
}
.po-grid-scroll { overflow: auto; max-height: calc(100vh - 320px); }
.po-grid-empty { padding: 40px 16px; text-align: center; color: var(--text-mute); font-size: calc(12.5px * var(--fs, 1)); }

table.po-grid {
  border-collapse: separate;
  border-spacing: 0;
  table-layout: fixed;
  width: 100%;
}
table.po-grid th {
  position: sticky;
  top: 0;
  z-index: 2;
  background: var(--surface-2);
  text-align: left;
  font-family: var(--font-mono);
  font-size: calc(10px * var(--fs, 1));
  letter-spacing: .02em;
  color: var(--text-dim);
  padding: 0 10px;
  border-bottom: 1px solid var(--border-hi);
  border-right: 1px solid var(--border);
  height: 30px;
  user-select: none;
  overflow: hidden;
  position: relative;
}
table.po-grid .po-th-label {
  display: block;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
  line-height: 30px;
}
table.po-grid .po-col-resizer {
  position: absolute; right: 0; top: 0; width: 6px; height: 100%; cursor: col-resize; z-index: 3;
}
table.po-grid .po-col-resizer:hover,
table.po-grid .po-col-resizer.dragging { background: var(--accent); opacity: .35; }
table.po-grid td {
  padding: 0 10px;
  font-size: calc(12px * var(--fs, 1));
  border-bottom: 1px solid var(--border);
  border-right: 1px solid var(--border);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  color: var(--text-dim);
}
table.po-grid td.num { text-align: right; font-family: var(--font-mono); font-variant-numeric: tabular-nums; color: var(--text); }
table.po-grid tr.po-grid-row:hover td { background: var(--surface-2); }
table.po-grid tr.po-grid-row:nth-child(even) td:not(.po-grid-grip) { background: var(--surface); }
table.po-grid tr.po-grid-row:nth-child(odd) td:not(.po-grid-grip) { background: var(--surface-2); }
.po-grid-gripcol { width: 22px; }
table.po-grid td.po-grid-grip {
  width: 22px; padding: 0; text-align: center; position: relative; background: var(--surface-2);
  border-right: 1px solid var(--border);
}
.po-grip-handle {
  width: 100%; height: 100%; display: flex; align-items: center; justify-content: center;
  cursor: row-resize; color: var(--text-mute);
}
.po-grip-handle:hover { color: var(--accent); }
.po-grip-handle svg { width: 11px; height: 11px; }

.po-grid-foot {
  display: flex; align-items: center; justify-content: space-between;
  padding: 8px 14px; border-top: 1px solid var(--border);
  font-family: var(--font-mono); font-size: calc(10.5px * var(--fs, 1)); color: var(--text-mute);
}

.po-col-resizing, .po-col-resizing * { cursor: col-resize !important; user-select: none !important; }
.po-row-resizing, .po-row-resizing * { cursor: row-resize !important; user-select: none !important; }

/* ---------- status-colored cells (Intransit monitoring/summary grids) ----------
   Same green/amber/red vocabulary as everywhere else in the app (--ok /
   --warn / --err), applied per-cell so a grid reads like a conditionally-
   formatted Excel sheet: OK cells stay quiet, TO CHECK and NOT UPLOADED
   jump out. */
table.po-grid td.pg-ok       { background: var(--ok-dim);   color: var(--ok);   font-weight: 600; }
table.po-grid td.pg-check    { background: var(--warn-dim); color: var(--warn); font-weight: 600; }
table.po-grid td.pg-notup    { background: var(--err-dim);  color: var(--err);  font-weight: 600; }
table.po-grid td.pg-blank    { color: var(--text-mute); }

/* ---------- grid tab switcher (Monitoring / Summary / Discrepancy) ----------
   Reuses the app's existing .tabs / .tab-btn look (see reimbursement.html)
   so a page with more than one result table can switch between them
   without introducing a new visual language. */
.po-grid-tabs { margin-bottom: var(--space-2); }
.po-grid-tabs .tab-btn .tab-count {
  margin-left: 6px;
  font-family: var(--font-mono);
  font-size: calc(10px * var(--fs, 1));
  opacity: .75;
}

/* ---------- strip Download button ---------- */
/* Full, labeled button (not the icon-only treatment .po-icon-btn gives the
   regenerate control next to it) so "the job is done, here's your file" is
   always legible at a glance instead of collapsing to a bare glyph. */
.po-strip-download {
  white-space: nowrap;
  font-family: var(--font-display);
  text-transform: none;
  letter-spacing: normal;
}
.po-strip-download.hidden { display: none; }

@media (max-width: 640px) {
  .po-progress-stats { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .po-log-panel { min-height: 220px; }
}

/* ---------- Add Branch modal (shared by both Intransit pages) ----------
   Lives in #modal-add-branch, included from _add_branch_modal.html. The
   modal chrome itself is the app's standard .po-modal treatment; these
   are only the few pieces that modal adds on top of it -- the branch
   checklist, the collapsed source-file section, and the inline result.
   Kept here rather than in a file of its own since both host pages
   already load style.css and nothing else would ever want them. */
.ab-intro { margin: 0 0 12px; color: var(--neu-dim, var(--text-dim)); }

.ab-branches { margin-bottom: 14px; }
.ab-branch {
  display: flex;
  gap: 8px;
  align-items: flex-start;
  padding: 7px 8px;
  border-radius: var(--radius-sm);
  cursor: pointer;
}
.ab-branch:hover { background: var(--surface-2); }
.ab-branch input { margin-top: 3px; flex: none; }

/* Collapsed by default: the page's own libraries are the normal source,
   so these pickers are an override rather than a step. */
.ab-sources { margin-bottom: 14px; }
.ab-sources > summary {
  cursor: pointer;
  padding: 7px 0;
  font-family: var(--font-display);
  font-weight: 600;
}
.ab-sources > summary > .small { color: var(--text-mute); font-weight: 400; }
.ab-sources .dropzone { padding: 14px; }

.ab-file {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  padding: 6px 8px;
  font-size: calc(13px * var(--fs, 1));
}
.ab-file > span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* Footer bar, sibling of .po-modal-body (see _add_branch_modal.html) --
   flex:none like .po-modal-head, so it keeps its own place at the bottom
   of the fixed-height panel while the body scrolls independently above
   it. No sticky positioning needed: it was never inside the scrolling
   area to begin with. */
.ab-actions {
  display: flex;
  flex: none;
  align-items: center;
  justify-content: flex-end;
  gap: 12px;
  padding: 12px var(--space-5);
  border-top: 1px solid var(--border);
  background: var(--surface);
}

/* The result renders inside the modal on purpose -- patching a workbook
   must never disturb whatever the host page's own Generate flow has in
   its done-canvas. */
.ab-result {
  margin-top: 14px;
  padding: 14px;
  border-radius: var(--radius);
  background: var(--ok-dim);
  border: 1px solid var(--border);
}
.ab-result p { margin: 6px 0 10px; }
.ab-result button { margin-top: 6px; }

/* ==========================================================================
   First-run boot screen (templates/_boot.html + static/boot.js)
   Shown once per machine while the app shell downloads. Deliberately quiet:
   this is a wait, not a feature, so it borrows the rail's own surface and
   type rather than introducing a splash-screen look of its own. The only
   moving parts are the meter and the single exit animation.
   ========================================================================== */
.boot {
  position: fixed;
  inset: 0;
  z-index: 200;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
  background: var(--neu-bg, #eceff4);
}
.boot[hidden] { display: none; }

/* Stops the home screen scrolling underneath the overlay. */
.boot-locked, .boot-locked body { overflow: hidden; }

.boot-panel {
  width: 100%;
  max-width: 440px;   /* keeps the copy under ~70 characters a line */
  display: flex;
  flex-direction: column;
  gap: 20px;
}

.boot-chibi { width: 56px; height: 84px; object-fit: contain; align-self: center; animation: bootBob 1.8s ease-in-out infinite; }
@keyframes bootBob { 50% { transform: translateY(-4px); } }
.boot-logo { height: 30px; width: auto; object-fit: contain; align-self: flex-start; }

.boot-headline strong {
  display: block;
  font-family: var(--font-display);
  font-size: calc(19px * var(--fs, 1));
  font-weight: 600;
  color: var(--neu-ink, var(--text));
}
.boot-sub {
  margin: 6px 0 0;
  font-size: calc(13.5px * var(--fs, 1));
  line-height: 1.55;
  color: var(--neu-dim, var(--text-dim));
}

/* ---- meter ---- */
.boot-meter-track {
  height: 6px;
  border-radius: 999px;
  background: rgba(23, 32, 48, 0.09);
  overflow: hidden;
}
.boot-meter-fill {
  height: 100%;
  width: 0;
  border-radius: 999px;
  background: var(--neu-blue, var(--accent));
  transition: width .18s linear;   /* linear: it's tracking real bytes */
}
.boot-meter-row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  margin-top: 9px;
}
.boot-pct {
  font-family: var(--font-mono);
  font-size: calc(13px * var(--fs, 1));
  font-weight: 600;
  color: var(--neu-ink, var(--text));
  font-variant-numeric: tabular-nums;
}
.boot-detail {
  font-size: calc(12.5px * var(--fs, 1));
  color: var(--neu-dim, var(--text-dim));
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* ---- per-stage rows ---- */
.boot-stages { list-style: none; margin: 0; padding: 0; display: grid; gap: 7px; }
.boot-stage {
  display: flex;
  align-items: center;
  gap: 9px;
  font-size: calc(12.5px * var(--fs, 1));
  color: var(--neu-mute, var(--text-mute));
}
.boot-stage-dot {
  flex: none;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: rgba(23, 32, 48, 0.18);
  transition: background .2s var(--ease);
}
.boot-stage.is-active { color: var(--neu-ink, var(--text)); }
.boot-stage.is-active .boot-stage-dot { background: var(--neu-blue, var(--accent)); }
.boot-stage.is-done .boot-stage-dot { background: var(--ok); }
.boot-stage-name { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.boot-stage-meta {
  flex: none;
  font-family: var(--font-mono);
  font-size: calc(11px * var(--fs, 1));
  font-variant-numeric: tabular-nums;
}

.boot-foot {
  display: flex;
  justify-content: space-between;
  gap: 12px;
  font-family: var(--font-mono);
  font-size: calc(11px * var(--fs, 1));
  color: var(--neu-mute, var(--text-mute));
  font-variant-numeric: tabular-nums;
}

.boot-error { border-top: 1px solid var(--border); padding-top: 14px; }
.boot-error p { margin: 0 0 10px; font-size: calc(13px * var(--fs, 1)); color: var(--neu-dim, var(--text-dim)); }

/* ---- the reveal ----
   One orchestrated moment: the panel lifts away while the home screen
   behind it settles in. Nothing else in the app animates on load. */
@keyframes boot-leave {
  to { opacity: 0; transform: translateY(-10px); }
}
@keyframes boot-enter {
  from { opacity: 0; transform: scale(0.985); }
  to { opacity: 1; transform: scale(1); }
}
.boot.is-leaving {
  animation: boot-leave .42s var(--ease) forwards;
  pointer-events: none;
}
body.boot-revealing { animation: boot-enter .5s var(--ease) 60ms both; }

@media (prefers-reduced-motion: reduce) {
  .boot-meter-fill { transition: none; }
  .boot.is-leaving { animation: boot-leave .2s linear forwards; }
  @keyframes boot-leave { to { opacity: 0; transform: none; } }
  body.boot-revealing { animation: none; }
}

@media (max-width: 520px) {
  .boot { align-items: flex-start; padding-top: 18vh; }
}

/* ==========================================================================
   Skeletons
   --------------------------------------------------------------------------
   The tool pages used to render their shell and then sit there with empty
   lists and "0" badges until /api/.../state came back -- which reads as
   "there are no files" rather than "we're still asking". These give the
   page an honest shape while it waits.

   Deliberately narrow in scope: only the regions whose content genuinely
   arrives over the network get one. Anything the server already rendered
   (the sidebar, the toolbar labels, the warehouse floor map) is real on
   first paint and must never be faked behind a shimmer.

   Driven by a single `.is-booting` class on the page shell, removed by the
   page's own script the moment state lands, so there's no timer deciding
   when the UI is "ready".
   ========================================================================== */
.skel,
.is-booting .skel-target {
  position: relative;
  color: transparent !important;
  background: linear-gradient(
    90deg,
    rgba(23, 32, 48, 0.07) 25%,
    rgba(23, 32, 48, 0.13) 37%,
    rgba(23, 32, 48, 0.07) 63%
  );
  background-size: 400% 100%;
  border-radius: var(--radius-sm);
  animation: skel-sweep 1.4s ease-in-out infinite;
  user-select: none;
}
.skel > *,
.is-booting .skel-target > * { visibility: hidden; }

@keyframes skel-sweep {
  0%   { background-position: 100% 50%; }
  100% { background-position: 0 50%; }
}

/* Placeholder rows for a list that hasn't arrived. Widths are staggered so
   it reads as text rather than as three identical bars. */
.skel-rows { display: grid; gap: 8px; padding: 4px 0; }
.skel-row { height: 13px; }
.skel-row:nth-child(1) { width: 72%; }
.skel-row:nth-child(2) { width: 55%; }
.skel-row:nth-child(3) { width: 63%; }

/* A badge holds a single character, so without a floor the shimmer would
   be an ~8px speck. */
.is-booting .skel-target {
  display: inline-block;
  min-width: 14px;
}

/* Once real content exists, any skeleton still sitting in the same
   container is redundant -- hidden rather than removed so a re-fetch can
   bring it straight back without rebuilding the markup. */
.is-booting .skel-hide-when-loading { display: none !important; }
.skel-block { display: none; }
.is-booting .skel-block { display: block; }
/* .skel-rows wants grid for its row gap, and the .skel-block rule above
   would otherwise flatten it to block and drop the gaps. */
.is-booting .skel-rows.skel-block { display: grid; }

/* The chip badges carry a live count, so a literal "0" while loading is an
   actively wrong number. Shrunk to a small pill instead. */
.is-booting .po-strip-chip-badge { min-width: 22px; }

@media (prefers-reduced-motion: reduce) {
  .skel,
  .is-booting .skel-target { animation: none; background-position: 0 50%; }
}

/* ==========================================================================
   Responsive passes
   ========================================================================== */

/* The fixed toolbar scrolls sideways when the chips outgrow the viewport
   (one more chip was added for Add branch). The native scrollbar inside a
   36px-tall bar ate a third of its height, so it's hidden and the overflow
   is signalled with an edge fade instead. */
.po-strip {
  scrollbar-width: none;
  -webkit-overflow-scrolling: touch;
  overscroll-behavior-x: contain;
  -webkit-mask-image: linear-gradient(90deg, #000 calc(100% - 28px), transparent 100%);
          mask-image: linear-gradient(90deg, #000 calc(100% - 28px), transparent 100%);
}
.po-strip::-webkit-scrollbar { display: none; }

/* The generate button must stay reachable no matter how many chips are in
   front of it, so it stops being part of the scrolling run and pins to the
   right edge of the bar. */
@media (max-width: 860px) {
  .po-strip { padding: 0 10px; gap: 6px; }
  .po-strip-divider { display: none; }
  .po-strip-chip { flex: none; }
  .po-strip-spacer { min-width: 8px; }
}

/* Below this the chip labels are the first thing to go -- the icon plus
   its count is enough to identify each one, and it buys back the room the
   Generate button needs. */
@media (max-width: 620px) {
  .po-strip-chip > span:not(.po-strip-chip-icon):not(.po-strip-chip-badge) { display: none; }
  .po-strip-chip-badge-optional { display: none; }
  .po-strip-status { display: none; }
}

/* Modals: full-height sheets on a phone rather than a floating panel with
   dead space above and below it. */
@media (max-width: 620px) {
  .po-modal { padding: 0; align-items: stretch; }
  .po-modal-panel {
    width: 100%;
    max-width: none;
    min-height: 100%;
    border-radius: 0;
  }
  .po-modal-head { position: sticky; top: 0; z-index: 2; }
}

/* Add Branch modal: the action row stacks so the button gets full width
   and the status message isn't squeezed to two characters. */
@media (max-width: 620px) {
  .ab-actions { flex-direction: column; align-items: stretch; gap: 8px; }
  .ab-actions button { width: 100%; }
  .ab-file { flex-wrap: wrap; }
}

/* ---------------------------------------------------------------------
   PWA install gate -- fullscreen overlay shown once per browser on
   first open, blocking the home screen until Workspace is installed
   (or the person explicitly opts to use the browser for now). Same
   panel language as .boot above, existing tokens only. Uses its own
   .pwa-gate-locked scroll-lock class (not .boot-locked) so the boot
   screen and this gate can't clear each other's lock on a very first
   visit where both could theoretically be present.
   --------------------------------------------------------------------- */
.pwa-gate {
  position: fixed;
  inset: 0;
  z-index: 210;   /* above .boot (200) -- install gate wins if both ever show */
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
  background: var(--neu-bg, #eceff4);
}
.pwa-gate-locked, .pwa-gate-locked body { overflow: hidden; }

.pwa-gate-panel {
  width: 100%;
  max-width: 440px;
  display: flex;
  flex-direction: column;
  gap: 18px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--neu-radius, 8px);
  box-shadow: var(--shadow-3);
  padding: 28px;
}

.pwa-gate-logo { height: 30px; width: auto; object-fit: contain; align-self: flex-start; }

.pwa-gate-headline strong {
  display: block;
  font-family: var(--font-display);
  font-size: calc(19px * var(--fs, 1));
  font-weight: 600;
  color: var(--neu-ink, var(--text));
}
.pwa-gate-headline p {
  margin: 6px 0 0;
  font-size: calc(13.5px * var(--fs, 1));
  line-height: 1.55;
  color: var(--neu-dim, var(--text-dim));
}

.pwa-gate-steps {
  margin: 0;
  padding: 0;
  list-style: none;
  display: grid;
  gap: 10px;
}
.pwa-gate-steps li {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  font-size: calc(13px * var(--fs, 1));
  line-height: 1.5;
  color: var(--neu-ink, var(--text));
}
.pwa-gate-step-num {
  flex: none;
  width: 20px;
  height: 20px;
  border-radius: 50%;
  background: var(--neu-blue, var(--accent));
  color: #fff;
  font-family: var(--font-mono);
  font-size: calc(11px * var(--fs, 1));
  font-weight: 700;
  display: flex;
  align-items: center;
  justify-content: center;
}

.pwa-gate-install-btn { width: 100%; }
.pwa-gate-waiting {
  margin: 0;
  font-size: calc(12.5px * var(--fs, 1));
  line-height: 1.5;
  color: var(--neu-mute, var(--text-mute));
  text-align: center;
}

.pwa-gate-skip {
  align-self: center;
  font-size: calc(12.5px * var(--fs, 1));
  color: var(--neu-mute, var(--text-mute));
}

/* ==========================================================================
   Login, My Account, Manage Accounts — brought onto the same sidebar/
   neu-canvas language as every module page instead of the old dark
   .topbar + .role-picker one-offs. /login has no session yet so it keeps
   its own full-screen centered card (no sidebar to show), but now uses
   the same tokens, panel/field/button parts, and mascot as everywhere
   else. /account and /admin gained the real sidebar (_topnav.html) and
   sit inside .home-wrap.home-neu like every other tool page.
   ========================================================================== */

/* ---------- /login : full-screen neu card ---------- */
.login-neu-body { background: var(--neu-bg); }
.login-neu-wrap {
  min-height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
  box-sizing: border-box;
}
.login-card {
  width: 100%;
  max-width: 380px;
  background: var(--neu-light);
  border: 1px solid var(--neu-edge);
  border-radius: var(--neu-radius-lg);
  box-shadow: var(--neu-raised);
  padding: 30px 28px 26px;
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  animation: role-picker-in 0.4s var(--ease) both;
}
.login-card-logo { height: 26px; width: auto; object-fit: contain; margin-bottom: 14px; }
.login-card-mascot {
  width: 132px;
  max-width: 60%;
  height: auto;
  margin-bottom: 6px;
}
.login-card h1 {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: calc(20px * var(--fs, 1));
  letter-spacing: 0.01em;
  color: var(--neu-ink, var(--text));
  margin: 0 0 4px;
}
.login-card h1 span { color: var(--neu-blue); }
.login-card-sub {
  font-family: var(--font-body);
  font-size: calc(13px * var(--fs, 1));
  color: var(--neu-dim, var(--text-dim));
  margin: 0 0 20px;
}
.login-card .auth-flash { width: 100%; box-sizing: border-box; margin-bottom: 16px; text-align: left; }
.login-card form.field-form {
  width: 100%;
  display: flex;
  flex-direction: column;
  gap: 14px;
  text-align: left;
}
.login-card .primary { width: 100%; justify-content: center; margin-top: 4px; }
.login-card .primary .login-form-btn-spinner {
  width: 0; height: 14px; border-radius: 50%; margin-right: 0;
  border: 2px solid transparent; flex: none; opacity: 0; transform: scale(0.4);
  overflow: hidden;
  transition: opacity 0.2s ease, transform 0.2s var(--spring, ease), width 0.2s ease, margin-right 0.2s ease;
}
.login-card .primary.btn-loading { cursor: default; }
.login-card .primary.btn-loading .login-form-btn-spinner {
  width: 14px; margin-right: 8px; opacity: 1; transform: scale(1);
  border-color: rgba(255,255,255,0.35); border-top-color: #fff;
  animation: login-btn-spin 0.7s linear infinite;
}
@keyframes login-btn-spin { to { transform: rotate(360deg); } }
.login-footnote {
  font-family: var(--font-body);
  font-size: calc(11.5px * var(--fs, 1));
  color: var(--neu-mute, var(--text-mute));
  text-align: center;
  margin: 18px 0 0;
}
@media (prefers-reduced-motion: reduce) {
  .login-card { animation: none; }
  .login-card .login-form-btn-spinner { transition: opacity 0.2s ease; }
}

/* ---------- /account and /admin : content canvas under the sidebar ---------- */
/* Same padding rhythm as .po-shell (every other module's content
   wrapper) but no max-width cap of its own -- .auth-shell / .auth-shell-wide
   already set the width that's right for each page, and doubling up a
   cap here just fights that on wide monitors. */
.auth-canvas { padding: var(--space-4) 18px var(--space-5); }

.acct-hero {
  display: flex;
  align-items: center;
  gap: 16px;
  margin-bottom: 4px;
}
.acct-hero-mascot { width: 64px; height: auto; flex: none; }
.acct-hero-text h1 {
  font-family: var(--font-display);
  font-size: calc(19px * var(--fs, 1));
  font-weight: 600;
  color: var(--neu-ink, var(--text));
  margin: 0;
}
.acct-hero-text p {
  font-family: var(--font-body);
  font-size: calc(12.5px * var(--fs, 1));
  color: var(--neu-dim, var(--text-dim));
  margin: 3px 0 0;
}
@media (max-width: 480px) {
  .acct-hero { gap: 10px; }
  .acct-hero-mascot { width: 46px; }
}


/* ==========================================================================
   READABILITY / CONTRAST (app-wide) -- text must be easy to read, everywhere.
   Secondary text tokens were darkened above (--text-dim/--text-mute/--neu-*);
   these rules catch what tokens can't: placeholders, disabled text, faded rows.
   ========================================================================== */
body { color: var(--text); -webkit-font-smoothing: auto; -moz-osx-font-smoothing: auto; text-rendering: optimizeLegibility; }
::placeholder { color: #566070 !important; opacity: 1 !important; }
:disabled, [disabled], [aria-disabled="true"] { opacity: 1; }
button:disabled, input:disabled, select:disabled, textarea:disabled { color: #566070; }
label, th, .po-ident, .po-strip-chip { color: var(--text); }
small, .muted, .hint, .help, figcaption, caption { color: var(--text-dim); }

/* Admin (admin only): a single button pinned to the bottom of the nav. */
.app-side-admin { margin-top: auto; }
a.po-strip-chip { text-decoration: none; }

/* Online list: avatar first, status dot pushed to the right */
.app-side-presence-row .av { flex: none; }
.app-side-presence-row .app-side-presence-dot { order: 3; margin-left: auto; }
.app-side-presence-row.is-away .av { opacity: .65; }

/* ---------- "About this app" modal: compact, card-based layout (replaces the 60px headline + giant bullets) ---------- */
.summary-modal .summary-modal-panel {
  max-width: min(920px, calc(100vw - 32px));
  height: auto; min-height: 0;
  max-height: calc(100vh - 48px);
  padding: 40px 44px 24px;
}
.summary-modal .summary-slide { justify-content: flex-start; gap: 14px; min-height: 360px; overflow-y: auto; }
.summary-modal .summary-slide-eyebrow { font-size: calc(12px * var(--fs, 1)); }
.summary-modal .summary-slide-title { font-size: calc(30px * var(--fs, 1)); line-height: 1.2; }
.summary-modal .summary-slide-lede { margin: 0; font-size: calc(15px * var(--fs, 1)); line-height: 1.55; color: var(--text-dim); max-width: 62ch; }
.summary-card-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; margin-top: 6px; }
.summary-card {
  display: flex; flex-direction: column; gap: 4px;
  padding: 14px 16px; background: var(--surface-2, #f4f5f7);
  border: 1px solid var(--border, #e2e5ea); border-left: 3px solid var(--accent);
  border-radius: var(--radius-sm, 8px);
}
.summary-card strong { font-size: calc(15px * var(--fs, 1)); color: var(--ink); }
.summary-card span { font-size: calc(13.5px * var(--fs, 1)); line-height: 1.5; color: var(--text-dim); }
.summary-split { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; margin-top: 6px; }
.summary-split-col { padding: 14px 16px; border: 1px solid var(--border, #e2e5ea); border-radius: var(--radius-sm, 8px); background: var(--surface-2, #f4f5f7); }
.summary-split-col h3 { margin: 0 0 8px; font-size: calc(12px * var(--fs, 1)); letter-spacing: .06em; text-transform: uppercase; font-family: var(--font-mono); }
.summary-split-col.is-ok { border-left: 3px solid #2e9e5b; }
.summary-split-col.is-ok h3 { color: #2e7d4f; }
.summary-split-col.is-gap { border-left: 3px solid #d98a1b; }
.summary-split-col.is-gap h3 { color: #a8670d; }
.summary-split-col ul { margin: 0; padding: 0; list-style: none; display: flex; flex-direction: column; gap: 8px; }
.summary-split-col li { position: relative; padding-left: 20px; font-size: calc(13.5px * var(--fs, 1)); line-height: 1.5; color: var(--text-dim); }
.summary-split-col li::before { position: absolute; left: 0; top: 0; font-weight: 700; }
.summary-split-col.is-ok li::before { content: "\2713"; color: #2e9e5b; }
.summary-split-col.is-gap li::before { content: "\2013"; color: #d98a1b; }
@media (max-width: 640px) {
  .summary-modal .summary-modal-panel { padding: 34px 18px 16px; }
  .summary-card-grid, .summary-split { grid-template-columns: 1fr; }
  .summary-modal .summary-slide-title { font-size: calc(22px * var(--fs, 1)); }
}

/* Hub header shown on the per-type add form (see hub-strip.js) */
.hub-strip-chips { display: none; }
.po-strip.hub-mode .hub-strip-chips,
html.hub-new .hub-strip-chips,
html.hub-routing .hub-strip-chips { display: contents; }
.po-strip.hub-mode > .po-strip-chip[data-tab],
html.hub-new .po-strip > .po-strip-chip[data-tab],
html.hub-routing .po-strip > .po-strip-chip[data-tab] { display: none !important; }
/* Routing is a tab: only its own panel shows, never the add form underneath */
html.hub-routing #panel-list { display: none !important; }
/* arriving via #new: show the form panel immediately, not the list for a split second */
html.hub-new #panel-list { display: none !important; }
html.hub-new #panel-new[hidden] { display: block !important; }
.hub-strip-chips a.po-strip-chip { text-decoration: none; }
