/* ═══════════════════════════════════════════════════════════════════════
   Dyeing Manager — Design System
   ═══════════════════════════════════════════════════════════════════════ */

/* ── Fonts: matched to dyeing (Google Fonts CDN) ────────────────────────
   2026-05-03: previously self-hosted under ./fonts/, but the dyeing app at
   /Volumes/Box/erp/dyeing/ loads DM Sans + JetBrains Mono via the Google
   Fonts CDN (<link href="https://fonts.googleapis.com/css2?family=DM+Sans
   …&family=JetBrains+Mono:wght@400;500;600&display=swap"> in
   dyeing/index.html). To match dyeing's rendering exactly (per user's
   "MATCH OUR DYEING FONTS IN OUR APP" directive), orders now also pulls
   from the same CDN — see the <link> tags in index.html. The @font-face
   blocks that used to live here have been removed; the .woff2 files in
   ./fonts/ are now unused (flagged for deletion, kept for now in case we
   need to revert). Instrument Serif is still referenced in many rules
   below but — matching dyeing — we do NOT preload it; it falls back to
   Georgia, which is what dyeing renders. */

/* ── Design Tokens ──────────────────────────────────────────────────── */
/*
   Two-tier hierarchy:
   1. PRIMITIVE tokens (--color-*)  — raw palette, referenced by semantic tokens only.
   2. SEMANTIC tokens (--bg, --primary, --danger, …) — used throughout CSS + JS.
   To add dark mode later, redefine ONLY the semantic tokens inside a [data-theme="dark"]
   (or `@media (prefers-color-scheme: dark)`) block. Primitives never change.
*/
:root {
  /* ── PRIMITIVES — Warm neutrals (chrome, app background, lines) ─────── */
  --color-white: #ffffff;
  --color-stone-50:  #f4f3f0;
  --color-stone-75:  #fafafa;
  --color-stone-100: #f5f5f4;
  --color-stone-120: #f5f3ef;
  --color-stone-140: #e7e0d8;
  --color-stone-150: #f0eeea;
  --color-stone-200: #e5e2dc;
  --color-stone-250: #d6d3d1;
  --color-stone-300: #d6cfc6;
  --color-stone-500: #9c958d;
  --color-stone-700: #504a44;
  --color-stone-800: #44403c;
  --color-stone-950: #1a1816;

  /* ── PRIMITIVES — Cool grays (form inputs, admin UI) ────────────────── */
  --color-gray-300: #d1d5db;
  --color-gray-500: #666666;
  --color-gray-800: #1f2937;

  /* ── PRIMITIVES — Cool neutrals (tables, secondary UI, admin surfaces) ─ */
  --color-slate-50:  #f8fafc;
  --color-slate-100: #f1f5f9;
  --color-slate-200: #e2e8f0;
  --color-slate-300: #e5e7eb;
  --color-slate-400: #94a3b8;
  --color-slate-500: #64748b;
  --color-slate-600: #475569;
  --color-slate-700: #334155;
  --color-slate-800: #1e293b;

  /* ── PRIMITIVES — Indigo (brand) ────────────────────────────────────── */
  --color-indigo-50:  #eef2ff;
  --color-indigo-300: #a5b4fc;
  --color-indigo-500: #6366f1;
  --color-indigo-600: #4f46e5;
  --color-indigo-700: #4338ca;
  --color-indigo-950: #1e1b4b;

  /* ── PRIMITIVES — Blue (links, info pills) ──────────────────────────── */
  --color-blue-50:  #eff6ff;
  --color-blue-100: #dbeafe;
  --color-blue-200: #bfdbfe;
  --color-blue-300: #93c5fd;
  --color-blue-500: #3b82f6;
  --color-blue-600: #2563eb;
  --color-blue-700: #1d4ed8;
  --color-blue-800: #1e40af;

  /* ── PRIMITIVES — Red (danger) ──────────────────────────────────────── */
  --color-red-50:  #fef2f2;
  --color-red-100: #fee2e2;
  --color-red-300: #fca5a5;
  --color-red-400: #f87171;
  --color-red-500: #ef4444;
  --color-red-600: #dc2626;
  --color-red-700: #b91c1c;
  --color-red-800: #991b1b;

  /* ── PRIMITIVES — Green / Emerald (success) ─────────────────────────── */
  --color-green-50:  #f0fdf4;
  --color-green-100: #dcfce7;
  --color-green-300: #86efac;
  --color-green-400: #4ade80;
  --color-green-600: #16a34a;
  --color-green-700: #166534;
  --color-emerald-50:  #ecfdf5;
  --color-emerald-600: #059669;
  --color-sky-50: #f0f9ff;

  /* ── PRIMITIVES — Amber / Orange (warning) ──────────────────────────── */
  --color-amber-50:  #fffbeb;
  --color-amber-100: #fef3c7;
  --color-amber-150: #fef3cd;
  --color-amber-200: #fde68a;
  --color-amber-300: #fcd34d;
  --color-amber-500: #f59e0b;
  --color-amber-700: #b45309;
  --color-amber-800: #92400e;
  --color-amber-900: #856404;
  --color-amber-950: #78350f;
  --color-orange-50:  #fff7ed;
  --color-orange-200: #fed7aa;
  --color-orange-600: #d97706;
  --color-orange-700: #ea580c;

  /* ── PRIMITIVES — Violet (misc status pills) ────────────────────────── */
  --color-violet-50:  #f5f3ff;
  --color-violet-200: #ddd6fe;
  --color-violet-600: #7c3aed;
  --color-violet-700: #6d28d9;
  --color-violet-900: #5b21b6;

  /* ── PRIMITIVES — Cyan / Teal (invoice accents) ─────────────────────── */
  --color-cyan-50:  #ecfeff;
  --color-teal-200: #99f6e4;

  /* ── SEMANTIC — Surfaces ────────────────────────────────────────────── */
  --bg:              var(--color-stone-50);
  --surface:         var(--color-white);
  --surface-sunken:  var(--color-stone-150);
  --surface-muted:   var(--color-stone-100);  /* read-only input fills */
  /* Floating-UI surfaces — light mode collapses to plain white so the
     palette / dialog visuals are unchanged. Dark mode lifts these to
     distinct elevation layers (Linear / Spotify pattern). */
  --surface-popover: var(--color-white);
  --surface-overlay: var(--color-white);
  --line:            var(--color-stone-200);
  --border:          var(--line);

  /* ── SEMANTIC — Text ────────────────────────────────────────────────── */
  --text:            var(--color-stone-950);
  --text-secondary:  var(--color-stone-700);
  --muted:           var(--color-stone-500);

  /* ── SEMANTIC — Brand ───────────────────────────────────────────────── */
  --primary:         var(--color-indigo-600);
  --primary-hover:   var(--color-indigo-700);
  --primary-light:   var(--color-indigo-50);
  --primary-glow:    rgba(79,70,229,.15);

  /* ── SEMANTIC — Status (danger / success / amber) ───────────────────── */
  --danger:          var(--color-red-600);
  --danger-strong:   var(--color-red-700);   /* stronger red for emphasis text */
  --danger-darker:   var(--color-red-800);   /* darkest red for "Mismatch" label */
  --danger-light:    var(--color-red-50);
  --danger-border:   var(--color-red-300);
  --success:         var(--color-green-600);
  --success-strong:  var(--color-emerald-600);
  --success-dark:    var(--color-green-700);
  --success-light:   var(--color-green-50);
  --success-tint:    var(--color-emerald-50);
  --success-border:  var(--color-green-300);
  --amber:           var(--color-amber-700);
  --amber-dark:      var(--color-amber-800);
  --amber-light:     var(--color-amber-50);
  --amber-tint:      var(--color-amber-100);
  --amber-tint-warm: var(--color-amber-150);
  --amber-border:    var(--color-amber-300);
  --amber-text-warm: var(--color-amber-900);

  /* ── SEMANTIC — Info (blue) ─────────────────────────────────────────── */
  --info:            var(--color-blue-600);
  --info-strong:     var(--color-blue-700);
  --info-dark:       var(--color-blue-800);
  --info-accent:     var(--color-blue-500);
  --info-light:      var(--color-blue-50);
  --info-tint:       var(--color-blue-100);
  --info-border:     var(--color-blue-200);

  /* ── SEMANTIC — Admin / secondary tables (slate) ────────────────────── */
  --admin-bg:         var(--color-slate-50);
  --admin-surface-sunken: var(--color-slate-100);
  --admin-line:       var(--color-slate-200);
  --admin-line-soft:  var(--color-slate-300);
  --admin-muted:      var(--color-slate-400);
  --admin-text-muted: var(--color-slate-500);
  --admin-text-secondary: var(--color-slate-600);
  --admin-text:       var(--color-slate-700);
  --admin-text-strong: var(--color-slate-800);

  /* ── SEMANTIC — Accents ─────────────────────────────────────────────── */
  --accent-violet:   var(--color-violet-600);

  /* Shadows */
  --shadow-xs: 0 1px 2px rgba(0,0,0,.04);
  --shadow-sm: 0 1px 3px rgba(0,0,0,.06), 0 1px 2px rgba(0,0,0,.04);
  --shadow-md: 0 4px 12px rgba(0,0,0,.07), 0 2px 4px rgba(0,0,0,.04);
  --shadow-lg: 0 10px 28px rgba(0,0,0,.10), 0 4px 10px rgba(0,0,0,.05);

  /* Spacing scale (4px base) */
  --sp-1: 4px;
  --sp-2: 8px;
  --sp-3: 12px;
  --sp-4: 16px;
  --sp-5: 20px;
  --sp-6: 24px;
  --sp-8: 32px;

  /* Type scale */
  --text-xs: 0.75rem;   /* 12px — minimum readable */
  --text-sm: 0.8125rem;  /* 13px — labels, captions */
  --text-base: 0.9rem;  /* 14.4px — body */
  --text-md: 0.95rem;   /* 15.2px — emphasis */
  --text-lg: 1.1rem;    /* 17.6px — subheadings */
  --text-xl: 1.3rem;    /* 20.8px — headings */
  --text-2xl: 1.6rem;   /* 25.6px — display */

  /* Radii */
  --radius-sm: 6px;
  --radius: 10px;
  --radius-lg: 14px;
  --radius-full: 9999px;

  /* Motion — primitive timing + easing tokens (v2-motion-polish).
     Scoped intentionally at :root so v1 and v2 both consume the same
     vocabulary. --transition kept for back-compat with legacy rules. */
  --ease-out:    cubic-bezier(0.22, 1, 0.36, 1);    /* Apple-sheet spring */
  --ease-in-out: cubic-bezier(0.65, 0, 0.35, 1);    /* bidirectional */
  --ease-spring: cubic-bezier(0.34, 1.56, 0.64, 1); /* bouncy — :active etc. */
  --dur-fast:    120ms;
  --dur-base:    180ms;
  --dur-slow:    260ms;
  --dur-drawer:  320ms;
  --transition:  var(--dur-base) var(--ease-out);

  /* Layout */
  --sidebar-bg: var(--color-indigo-950);
  --sidebar-w: 240px;
  --header-h: 60px;
}

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

body {
  margin: 0;
  font-family: "DM Sans", system-ui, -apple-system, sans-serif;
  font-size: var(--text-base);
  color: var(--text);
  background: var(--bg);
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

/* ── Layout ─────────────────────────────────────────────────────────── */
.layout {
  min-height: 100vh;
  min-height: 100dvh;
  display: grid;
  grid-template-columns: var(--sidebar-w) 1fr;
}

/* ── Sidebar ────────────────────────────────────────────────────────── */
.sidebar {
  background: var(--sidebar-bg);
  color: #c7d2de;
  padding: var(--sp-5) var(--sp-3);
  display: flex;
  flex-direction: column;
  gap: var(--sp-1);
  border-right: 1px solid rgba(255,255,255,.06);
  /* Sidebar scrolls independently from the main content. position:sticky
     pins it to the viewport so the nav stays visible while the workspace
     scrolls; max-height + overflow-y:auto turns the sidebar into its own
     scroll container. overscroll-behavior:contain keeps wheel/trackpad
     scroll inside the sidebar instead of bubbling to the page when the
     user reaches the top/bottom of the nav. */
  position: sticky;
  top: 0;
  align-self: start;
  max-height: 100vh;
  max-height: 100dvh;
  overflow-y: auto;
  overscroll-behavior: contain;
}

.sidebar h1 {
  margin: 0;
  font-family: "Instrument Serif", Georgia, serif;
  font-size: var(--text-lg);
  font-weight: 400;
  font-style: italic;
  letter-spacing: 0.01em;
  padding: 0 var(--sp-2) var(--sp-4);
  border-bottom: 1px solid rgba(255,255,255,.08);
  margin-bottom: var(--sp-2);
  color: #f8fafc;
}

.subtitle {
  margin: var(--sp-1) 0 var(--sp-4);
  color: #64748b;
  font-size: var(--text-xs);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  padding: 0 var(--sp-2);
}

#master-nav { display: flex; flex-direction: column; gap: 2px; flex: 1; min-height: 0; }

.nav-item {
  width: 100%;
  text-align: left;
  border: none;
  background: transparent;
  color: rgba(255,255,255,.85);
  border-radius: var(--radius-sm);
  padding: 10px var(--sp-3);
  cursor: pointer;
  font-family: inherit;
  font-size: var(--text-base);
  font-weight: 500;
  transition: background var(--transition), color var(--transition);
}

.nav-item:hover:not(.active) {
  background: rgba(255,255,255,.07);
  color: #e2e8f0;
}

.nav-item.active {
  background: rgba(255,255,255,.12);
  color: #ffffff;
  border-left: 3px solid #a5b4fc;
  padding-left: 9px;
  font-weight: 600;
}

/* ── Hamburger (mobile) ─────────────────────────────────────────────── */
#hamburger-btn { display: none; }
#hamburger-btn {
  flex-direction: column;
  justify-content: center;
  gap: 5px;
  width: 36px;
  height: 36px;
  padding: 6px;
  background: transparent;
  border: none;
  cursor: pointer;
  flex-shrink: 0;
}
#hamburger-btn span {
  display: block;
  width: 100%;
  height: 2px;
  background: var(--text-secondary);
  border-radius: 2px;
  transition: background var(--dur-fast) var(--ease-out);
}
#hamburger-btn:hover span { background: var(--text); }

#sidebar-overlay {
  display: none;
  position: fixed;
  inset: 0;
  background: rgba(0,0,0,.45);
  z-index: 999;
}
#sidebar-overlay.active { display: block; }

/* ── Main area ──────────────────────────────────────────────────────── */
.main {
  display: flex;
  flex-direction: column;
  min-width: 0;
}

.main-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: var(--sp-4);
  padding: var(--sp-3) var(--sp-6);
  min-height: var(--header-h);
  border-bottom: 1px solid var(--line);
  background: var(--surface);
  position: sticky;
  top: 0;
  z-index: 20;
}

.main-header h2 { margin: 0; }
.main-header p { margin: 3px 0 0; color: var(--muted); }

#module-title {
  font-size: var(--text-xl);
  font-weight: 700;
  color: var(--text);
  letter-spacing: -0.01em;
}

#module-description {
  font-size: var(--text-sm);
  color: var(--muted);
  margin-top: 2px;
}

.toolbar {
  display: flex;
  gap: var(--sp-2);
  align-items: center;
}

/* Search bar — wraps the input + left icon + ⌘K kbd + in-bar status badge.
   Badge lives INSIDE the bar (no longer clips behind the notification bell).
   Mockup Section 07b reference. */
.search-bar {
  position: relative;
  display: flex;
  align-items: center;
  flex: 0 1 360px;
  min-width: 0;
}
.search-bar__icon {
  position: absolute;
  left: 12px;
  top: 50%;
  transform: translateY(-50%);
  color: var(--muted, #78716c);
  pointer-events: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
.search-bar__kbd {
  position: absolute;
  right: 10px;
  top: 50%;
  transform: translateY(-50%);
  font-family: var(--font-mono, ui-monospace, SFMono-Regular, monospace);
  font-size: 0.72rem;
  font-weight: 500;
  color: var(--muted, #78716c);
  background: var(--surface-sunken, #f4f2ed);
  border: 1px solid var(--line, #e5e5e5);
  border-radius: 4px;
  padding: 2px 6px;
  pointer-events: none;
  white-space: nowrap;
}
.search-bar__badge {
  display: none;
  position: absolute;
  right: 48px;
  top: 50%;
  transform: translateY(-50%);
  font-size: 0.7rem;
  font-weight: 600;
  padding: 2px 8px;
  border-radius: 99px;
  white-space: nowrap;
  pointer-events: none;
}

#search-input {
  width: 100%;
  border-radius: var(--radius-lg);
  background: var(--surface);
  border: 1px solid var(--line);
  padding: 10px 86px 10px 36px;
  font-size: var(--text-base);
  box-shadow: var(--shadow-xs);
}

#search-input:focus {
  background: var(--surface);
  border-color: var(--primary);
  box-shadow: 0 0 0 3px var(--primary-glow);
}

#quick-open-input { width: min(260px, 100%); }

#session-user-wrap { display: flex; align-items: center; }
.session-user {
  display: grid;
  gap: var(--sp-1);
  font-size: var(--text-xs);
  color: var(--muted);
}
.session-user select { min-width: 170px; }

#module-workspace {
  padding: var(--sp-6);
  flex: 1;
}

/* ── Workspace grid ─────────────────────────────────────────────────── */
.workspace-grid {
  display: grid;
  gap: var(--sp-4);
  grid-template-columns: 1.3fr 1fr;
}

/* ── Panels & Cards ─────────────────────────────────────────────────── */
.panel {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-sm);
  overflow: hidden;
}

.panel-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: var(--sp-3) var(--sp-5);
  border-bottom: 1px solid var(--line);
  background: var(--surface-sunken);
}

.panel-header h3 {
  font-size: var(--text-sm);
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--text-secondary);
  margin: 0;
}

.table-wrap {
  overflow: auto;
  max-height: calc(100vh - var(--header-h) - var(--sp-6) * 2 - 56px);
  max-height: calc(100dvh - var(--header-h) - var(--sp-6) * 2 - 56px);
}

.detail-card {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  padding: var(--sp-6);
  box-shadow: var(--shadow-sm);
}

/* ── Tables ─────────────────────────────────────────────────────────── */
table {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--text-base);
  font-variant-numeric: tabular-nums;
}

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

thead th {
  position: sticky;
  top: 0;
  z-index: 5;
  background: var(--surface);
  color: var(--text-secondary);
  font-size: var(--text-xs);
  letter-spacing: 0.04em;
  text-transform: uppercase;
  font-weight: 600;
  padding: var(--sp-3) var(--sp-4);
  border-bottom: 2px solid var(--line);
}

tbody td {
  padding: var(--sp-3) var(--sp-4);
  font-size: var(--text-base);
  border-bottom: 1px solid rgba(0,0,0,.05);
}

tbody tr:hover td {
  background: var(--primary-light) !important;
  transition: background var(--transition);
}

.mono, td.mono {
  font-family: "JetBrains Mono", "Fira Code", monospace;
  font-size: var(--text-sm);
}

.actions { display: flex; gap: var(--sp-2); }

/* Detail table */
.detail-table { width: 100%; border-collapse: collapse; table-layout: auto; }
.detail-card .detail-table {
  margin: 0 calc(var(--sp-6) * -1) calc(var(--sp-6) * -1);
  width: calc(100% + var(--sp-6) * 2);
  border-radius: 0 0 var(--radius-lg) var(--radius-lg);
  overflow: hidden;
}

.detail-table thead th {
  background: var(--surface-sunken);
  color: var(--text-secondary);
  font-size: var(--text-xs);
  letter-spacing: 0.04em;
  text-transform: uppercase;
  font-weight: 600;
  padding: var(--sp-3) var(--sp-4);
  border-bottom: 2px solid var(--line);
  text-align: left;
  white-space: nowrap;
}

.detail-table tbody td {
  padding: var(--sp-3) var(--sp-4);
  font-size: var(--text-base);
  border-bottom: 1px solid var(--line);
  vertical-align: middle;
}

.detail-table tbody tr:last-child td { border-bottom: none; }
.detail-table tbody tr:hover { background: var(--surface-sunken); }

.meters-cell {
  text-align: right;
  font-family: "JetBrains Mono", monospace;
  font-variant-numeric: tabular-nums;
  font-weight: 500;
}

.row-num { color: var(--muted); width: 36px; font-size: var(--text-xs); }
.row-delete { width: 40px; text-align: center; }

.btn-icon-delete {
  background: none; border: none;
  font-size: 1.1rem; color: var(--muted);
  cursor: pointer; padding: 2px 6px;
  border-radius: var(--radius-sm);
  transition: all var(--transition);
  opacity: 0.35;
}
.btn-icon-delete:hover { color: var(--danger); opacity: 1; background: var(--danger-light); }
.detail-table tbody tr:hover .btn-icon-delete { opacity: 1; }

.total-row td {
  font-weight: 700;
  border-top: 2px solid var(--text-secondary);
  border-bottom: none !important;
  padding-top: var(--sp-3);
}

.empty-msg {
  text-align: center;
  color: var(--muted);
  font-style: italic;
  padding: var(--sp-8) var(--sp-3);
}

/* ── Forms ──────────────────────────────────────────────────────────── */
form, .form-grid { display: grid; gap: var(--sp-4); }

label {
  display: grid;
  gap: var(--sp-1);
  font-size: var(--text-sm);
  font-weight: 500;
  color: var(--text-secondary);
}

input, textarea, select {
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 10px var(--sp-4);
  font: inherit;
  font-size: var(--text-base);
  background: var(--surface);
  color: var(--text);
  transition: border-color var(--transition), box-shadow var(--transition);
}

input:not([type="checkbox"]):not([type="radio"]):not([type="file"]),
select, textarea {
  padding: 9px var(--sp-3);
}

input:focus, select:focus, textarea:focus {
  outline: none;
  border-color: var(--primary);
  box-shadow: 0 0 0 3px var(--primary-glow);
}

input[type="number"] { -moz-appearance: textfield; }
input[type="number"]::-webkit-inner-spin-button,
input[type="number"]::-webkit-outer-spin-button { -webkit-appearance: none; margin: 0; }

input::placeholder, textarea::placeholder { color: var(--muted); }
textarea { min-height: 66px; }

.form-row {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
  gap: var(--sp-3);
}

.form-actions { display: flex; gap: var(--sp-2); }

.issue-lookup { display: flex; gap: var(--sp-3); align-items: center; }
.issue-lookup input { flex: 1; }

.issue-meta {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--sp-2) var(--sp-4);
  padding: var(--sp-3);
  border-radius: var(--radius);
  background: var(--surface-sunken);
  border: 1px solid var(--line);
  font-size: var(--text-base);
}

.issue-warning { color: var(--danger); font-size: var(--text-sm); }
.issue-form input[type="number"] { width: 110px; }

/* ── Buttons ────────────────────────────────────────────────────────── */
.btn {
  border: 1px solid var(--line);
  background: var(--surface);
  color: var(--text);
  border-radius: var(--radius);
  padding: 9px var(--sp-4);
  cursor: pointer;
  font-family: inherit;
  font-size: var(--text-sm);
  font-weight: 500;
  letter-spacing: 0.01em;
  transition: background var(--transition), border-color var(--transition),
              color var(--transition), box-shadow var(--transition);
}

.btn:hover { background: var(--surface-sunken); border-color: var(--muted); }
.btn:active { transform: translateY(1px); }
.btn:focus-visible { outline: 2px solid var(--primary); outline-offset: 2px; }

.btn.primary {
  border-color: var(--primary);
  background: var(--primary);
  color: #ffffff;
  box-shadow: 0 2px 8px var(--primary-glow);
}
.btn.primary:hover {
  background: var(--primary-hover);
  border-color: var(--primary-hover);
  box-shadow: 0 4px 14px var(--primary-glow);
}

#new-record-btn {
  font-weight: 600;
  padding: 9px var(--sp-5);
  font-size: var(--text-base);
  white-space: nowrap;
}

.btn.success {
  border-color: var(--success-border);
  background: var(--success-light);
  color: var(--success);
  font-weight: 600;
}
.btn.success:hover { background: #dcfce7; border-color: #4ade80; }

.btn.danger {
  border-color: var(--danger-border);
  background: var(--danger-light);
  color: var(--danger);
  font-weight: 600;
}
.btn.danger:hover { background: #fee2e2; border-color: #f87171; }

.btn.small { font-size: var(--text-xs); padding: 5px 10px; }

/* ── Badges ─────────────────────────────────────────────────────────── */
.badge {
  display: inline-block;
  border-radius: var(--radius-full);
  padding: 3px 10px;
  font-size: var(--text-xs);
  font-weight: 600;
  letter-spacing: 0.03em;
  border: 1px solid #d6cfc6;
  background: var(--surface-sunken);
}

.badge.approved, .badge.done, .badge.completed, .badge.running {
  background: var(--success-light); border-color: var(--success-border); color: #166534;
}
.badge.draft, .badge.in_progress, .badge.ok {
  background: #eff6ff; border-color: #93c5fd; color: #1d4ed8;
}
.badge.pending, .badge.balance {
  background: #fff7ed; border-color: #fdba74; color: #9a3412;
}
.badge.rejected, .badge.failed, .badge.blocked {
  background: var(--danger-light); border-color: var(--danger-border); color: #991b1b;
}
.badge.uniform, .badge.repeat, .badge.export,
.badge.gh_ready, .badge.ok_not_ready_for_exp, .badge.brief_entry_check {
  background: #f5f3ff; border-color: #ddd6fe; color: #5b21b6;
}
.badge.priority {
  background: var(--amber-light); border-color: var(--amber-border); color: #92400e; margin-left: 6px;
}

/* ── Design image ───────────────────────────────────────────────────── */
.design-image-preview {
  width: 100%;
  max-height: 200px;
  object-fit: contain;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: var(--sp-2);
  background: var(--surface-sunken);
}

.grid-inline {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--sp-3);
}

.fast-note { font-size: var(--text-sm); color: var(--muted); margin: 0 0 var(--sp-2); }
.help-line { font-size: var(--text-sm); color: var(--muted); }

/* ── Fullscreen editor ──────────────────────────────────────────────── */
.editor-fullscreen {
  position: fixed;
  inset: var(--sp-4);
  background: rgba(255,255,255,.97);
  backdrop-filter: blur(12px);
  border: 1px solid var(--line);
  border-radius: 18px;
  box-shadow: var(--shadow-lg);
  padding: var(--sp-5);
  overflow: auto;
  z-index: 100;
}

.editor-fullscreen-header {
  position: sticky;
  top: 0;
  display: flex;
  justify-content: space-between;
  align-items: center;
  background: rgba(255,255,255,.94);
  padding: var(--sp-2) 0 var(--sp-3);
  margin-bottom: var(--sp-3);
  border-bottom: 1px solid var(--line);
}
.editor-fullscreen-header h3 { margin: 0; font-size: var(--text-md); }

/* ── Quick select ───────────────────────────────────────────────────── */
.quick-select-row {
  display: grid;
  gap: var(--sp-2);
  grid-template-columns: 1.4fr 0.7fr auto auto;
  margin: var(--sp-3) 0;
  align-items: center;
}

/* ── Invoice cards ──────────────────────────────────────────────────── */
.invoice-showcase { border-top: 1px solid var(--line); padding-top: var(--sp-3); }
.invoice-showcase-header h4 { margin: 0; font-size: var(--text-md); }
.invoice-showcase-header p { margin: 4px 0 var(--sp-3); color: var(--muted); font-size: var(--text-sm); }

.invoice-card-grid {
  display: grid;
  gap: var(--sp-3);
  grid-template-columns: repeat(2, minmax(0, 1fr));
}

.invoice-card {
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  padding: var(--sp-3);
  background: linear-gradient(165deg, #ffffff, var(--surface-sunken));
}
.invoice-card.customer { border-color: #99f6e4; background: linear-gradient(160deg, #ecfeff, var(--surface-sunken)); }
.invoice-card.internal { border-color: #bfdbfe; background: linear-gradient(160deg, #eff6ff, var(--surface-sunken)); }

.invoice-chip {
  display: inline-block;
  border-radius: var(--radius-full);
  padding: 3px 10px;
  font-size: var(--text-xs);
  letter-spacing: 0.04em;
  text-transform: uppercase;
  border: 1px solid var(--line);
  color: var(--text-secondary);
}

.invoice-card h5 { margin: var(--sp-2) 0 var(--sp-1); font-size: var(--text-md); }
.invoice-muted { margin: 0 0 var(--sp-3); font-size: var(--text-sm); color: var(--muted); }

.invoice-meta {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--sp-2);
  margin-bottom: var(--sp-3);
}
.invoice-meta div { display: grid; gap: 2px; }
.invoice-meta strong {
  font-size: var(--text-xs);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--text-secondary);
}

/* ── Workflow cards ──────────────────────────────────────────────────── */
.workflow-card {
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--surface-sunken);
  padding: var(--sp-3);
  display: grid;
  gap: var(--sp-3);
}

.workflow-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
}

.workflow-steps {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--sp-2);
}

.workflow-step {
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--surface);
  padding: var(--sp-2);
  font-size: var(--text-sm);
  color: var(--text-secondary);
  display: grid;
  gap: var(--sp-1);
}

/* ── Tracking / color matching ──────────────────────────────────────── */
.tracking-tabs { display: flex; flex-wrap: wrap; gap: var(--sp-2); }

.tracking-tab {
  border: 0;
  border-radius: var(--radius-sm);
  padding: var(--sp-2) var(--sp-3);
  color: #fff;
  font-family: inherit;
  font-size: var(--text-xs);
  cursor: pointer;
  text-transform: uppercase;
  font-weight: 500;
  transition: background var(--transition), box-shadow var(--transition);
}
.tracking-tab:hover:not(.active-tab) { opacity: 0.85; }
.tracking-tab.active-tab, .tracking-tab.active {
  box-shadow: 0 2px 8px rgba(0,0,0,.22); font-weight: 600;
}

.tracking-tab:nth-child(1)  { background: #6d28d9; }
.tracking-tab:nth-child(2)  { background: #15803d; }
.tracking-tab:nth-child(3)  { background: #2563eb; }
.tracking-tab:nth-child(4)  { background: #d97706; }
.tracking-tab:nth-child(5)  { background: #ea580c; }
.tracking-tab:nth-child(6)  { background: #1d4ed8; }
.tracking-tab:nth-child(7)  { background: #166534; }
.tracking-tab:nth-child(8)  { background: #7c3aed; }
.tracking-tab:nth-child(9)  { background: #b45309; }
.tracking-tab:nth-child(10) { background: #92400e; }
.tracking-tab:nth-child(11) { background: #4338ca; }
.tracking-tab:nth-child(12) { background: #0e7490; }
.tracking-tab:nth-child(13) { background: #0f766e; }

.tracking-table-wrap { border: 1px solid var(--line); border-radius: var(--radius); }
.tracking-table { font-size: var(--text-sm); }
.tracking-table thead th { background: var(--surface-sunken); color: var(--text-secondary); white-space: nowrap; }
.tracking-table td { white-space: nowrap; }

.tracking-pager {
  display: flex; align-items: center; gap: var(--sp-3);
  flex-wrap: wrap; font-size: var(--text-sm);
}
.tracking-searchbar input { width: min(420px, 100%); }
.tracking-check-col { width: 34px; }
.tracking-row-selected { background: #fef9f0; }
.tracking-flag-uniform { background: var(--amber-light); color: #92400e; font-weight: 700; }

.tracking-bulk-fab {
  position: fixed; right: 28px; bottom: 28px;
  border: 0; border-radius: var(--radius-full);
  padding: 12px var(--sp-5);
  background: var(--primary); color: #fff;
  font-family: inherit; font-weight: 600;
  cursor: pointer;
  box-shadow: 0 10px 28px rgba(79,70,229,.35);
  z-index: 35;
}

.tracking-bulk-panel {
  position: fixed; right: 28px; bottom: 82px;
  width: min(360px, calc(100vw - 24px));
  border: 1px solid var(--line); border-radius: var(--radius-lg);
  background: var(--surface);
  box-shadow: var(--shadow-lg);
  padding: var(--sp-3);
  display: grid; gap: var(--sp-2);
  z-index: 36;
}

.tracking-summary-card {
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--surface-sunken);
  padding: var(--sp-3);
}

.tracking-meta-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--sp-2) var(--sp-3);
  font-size: var(--text-sm);
}
.tracking-meta-grid div { display: grid; gap: 2px; }
.tracking-meta-grid strong {
  color: var(--text-secondary);
  font-size: var(--text-xs);
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

/* ── Final job card ─────────────────────────────────────────────────── */
.finaljob-fullscreen {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  padding: var(--sp-4);
}
.finaljob-fullscreen-header {
  display: flex; justify-content: space-between; align-items: center;
  gap: var(--sp-3); border-bottom: 1px solid var(--line);
  padding-bottom: var(--sp-3); margin-bottom: var(--sp-3);
}
.finaljob-fullscreen-header h3 { margin: 0; }
.finaljob-fullscreen-content { max-height: calc(100vh - 230px); max-height: calc(100dvh - 230px); overflow: auto; }

/* ── Scrollbar ──────────────────────────────────────────────────────── */
::-webkit-scrollbar        { width: 6px; height: 6px; }
::-webkit-scrollbar-track  { background: transparent; }
::-webkit-scrollbar-thumb  { background: #d6cfc6; border-radius: var(--radius-full); }
::-webkit-scrollbar-thumb:hover { background: var(--muted); }

/* ═══════════════════════════════════════════════════════════════════════
   Responsive
   ═══════════════════════════════════════════════════════════════════════ */
@media (max-width: 1080px) {
  .sidebar {
    position: fixed; top: 0; left: 0;
    height: 100vh; height: 100dvh; width: 260px;
    z-index: 1000;
    transform: translateX(-100%);
    transition: transform var(--dur-slow) var(--ease-out);
    overflow-y: auto;
  }
  .sidebar.sidebar-open { transform: translateX(0); }
  .layout { grid-template-columns: 1fr; }
  #hamburger-btn { display: flex; }
  .workspace-grid { grid-template-columns: 1fr; }
  .quick-select-row { grid-template-columns: 1fr; }
  .workflow-steps { grid-template-columns: 1fr; }
  .tracking-meta-grid { grid-template-columns: 1fr; }
  .invoice-card-grid { grid-template-columns: 1fr; }
  .main-header { flex-direction: column; align-items: flex-start; }
}

/* ═══════════════════════════════════════════════════════════════════════
   Fabric stock accordion
   ═══════════════════════════════════════════════════════════════════════ */
.fabric-group-header {
  cursor: pointer; display: flex; align-items: center;
  gap: 1rem; padding: .5rem .75rem;
  border-bottom: 1px solid var(--border);
}
.fabric-group-header:hover { background: var(--surface-sunken); }
.fabric-group-header.is-open { font-weight: 600; background: var(--primary-light); }
.fabric-group-body td, .fabric-group-body th { padding: .35rem .6rem; font-size: var(--text-base); }

/* ── Scan issue UI ──────────────────────────────────────────────────── */
.scan-input-wrap { display: flex; gap: .5rem; margin: .75rem 0; }
.scan-input-wrap input { flex: 1; font-size: var(--text-lg); padding: .4rem .6rem; }
.issue-queue-table td, .issue-queue-table th { padding: .35rem .5rem; font-size: var(--text-base); }
.scan-error-banner {
  background: var(--danger-light); color: #991b1b; padding: .4rem .75rem;
  border-radius: var(--radius-sm); margin: .4rem 0; font-weight: 500;
}
.issue-running-total { font-size: var(--text-md); font-weight: 600; margin: .5rem 0; }
.issue-running-total.fulfilled { color: var(--success); }

/* ── QR print labels ────────────────────────────────────────────────── */
.qr-label-grid { display: flex; flex-wrap: wrap; gap: 1rem; padding: 1rem; }
.qr-label-card {
  border: 1px solid var(--line); border-radius: var(--radius-sm); padding: .75rem;
  text-align: center; width: 160px; font-size: var(--text-sm);
}
.qr-label-card .take-no {
  font-family: "JetBrains Mono", monospace;
  font-weight: 600; font-size: var(--text-md); margin: .4rem 0 .2rem;
}

/* ── Checkbox group ─────────────────────────────────────────────────── */
.checkbox-group { margin: .5rem 0; }
.checkbox-group-label { font-weight: 600; font-size: var(--text-base); color: var(--text-secondary); margin-bottom: .5rem; }
.checkbox-list { display: flex; flex-wrap: wrap; gap: .5rem 1.5rem; }
.checkbox-item { display: flex; align-items: center; gap: .35rem; font-size: var(--text-base); cursor: pointer; }
.checkbox-item input[type="checkbox"] { width: 16px; height: 16px; accent-color: var(--primary); }

/* ═══════════════════════════════════════════════════════════════════════
   Program Detail Page
   ═══════════════════════════════════════════════════════════════════════ */
.program-detail {
  max-width: 900px; margin: 0 auto;
  display: flex; flex-direction: column;
  gap: var(--sp-4); width: 100%;
}
.detail-container { display: flex; flex-direction: column; gap: var(--sp-4); max-width: 900px; margin: 0 auto; width: 100%; }
.detail-topbar { display: flex; align-items: center; }

.btn-back {
  display: inline-flex; align-items: center; gap: var(--sp-2);
  padding: var(--sp-2) var(--sp-4);
  font-size: var(--text-sm); font-weight: 500;
  color: var(--text-secondary);
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius); cursor: pointer;
  font-family: inherit;
  transition: all var(--transition);
  box-shadow: var(--shadow-xs);
}
.btn-back:hover { background: var(--surface-sunken); color: var(--text); box-shadow: var(--shadow-sm); }
.btn-back::before { content: "\2190"; font-size: var(--text-md); }

.detail-title-row {
  display: flex; align-items: center;
  justify-content: space-between; margin-bottom: var(--sp-4);
}

.detail-lot-no {
  font-family: "JetBrains Mono", monospace;
  font-size: var(--text-2xl); font-weight: 700; color: var(--text);
}

.detail-status {
  display: inline-flex; align-items: center; gap: var(--sp-2);
  padding: var(--sp-1) var(--sp-3);
  font-size: var(--text-xs); font-weight: 500;
  border-radius: var(--radius-full);
  background: var(--amber-light); color: #92400e;
}
.detail-status::before {
  content: ""; width: 6px; height: 6px;
  border-radius: 50%; background: #f59e0b;
}

.detail-meta-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: var(--sp-4) var(--sp-6);
}
.detail-meta-item { display: flex; flex-direction: column; gap: 2px; }
.detail-meta-label {
  font-size: var(--text-xs); font-weight: 500;
  text-transform: uppercase; letter-spacing: 0.04em;
  color: var(--muted);
}
.detail-meta-value { font-size: var(--text-base); font-weight: 500; color: var(--text); }

.detail-summary-strip {
  display: flex; gap: var(--sp-8); flex-wrap: wrap;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  padding: var(--sp-5) var(--sp-6);
  box-shadow: var(--shadow-sm);
  margin-top: var(--sp-3);
}
.detail-summary-item { display: flex; flex-direction: column; gap: 2px; }
.detail-summary-num {
  font-family: "JetBrains Mono", monospace;
  font-size: var(--text-2xl); font-weight: 700;
  color: var(--text); line-height: 1.2;
}
.detail-summary-label {
  font-size: var(--text-xs); font-weight: 500;
  text-transform: uppercase; letter-spacing: 0.04em;
  color: var(--muted);
}

/* Inline add shade row */
.detail-add-row {
  display: flex; align-items: center; gap: var(--sp-2);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  padding: var(--sp-3);
}
.detail-add-select { flex: 2; min-width: 0; }
.detail-add-input { flex: 1; min-width: 100px; }

@media (max-width: 640px) {
  .detail-meta-grid { grid-template-columns: repeat(2, 1fr); gap: var(--sp-3) var(--sp-4); }
  .detail-add-row { flex-wrap: wrap; }
  .detail-add-select { flex: 1 1 100%; }
  .detail-summary-strip { gap: var(--sp-5); }
  .detail-card .detail-table { margin: 0 calc(var(--sp-3) * -1) calc(var(--sp-4) * -1); width: calc(100% + var(--sp-3) * 2); }
  .detail-card { padding: var(--sp-4) var(--sp-3); }
  .btn-icon-delete { opacity: 1; }
}

/* ═══════════════════════════════════════════════════════════════════════
   Receiving Module
   ═══════════════════════════════════════════════════════════════════════ */
.recv-container {
  display: flex; flex-direction: column;
  gap: var(--sp-4);
  max-width: 1100px; margin: 0 auto; width: 100%;
}

.recv-form-card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  padding: var(--sp-4) var(--sp-5);
  box-shadow: var(--shadow-sm);
}

.recv-lot-row { display: flex; gap: var(--sp-3); align-items: center; }
.recv-lot-group { flex: 0 0 200px; position: relative; }

.recv-lot-input {
  font-size: var(--text-lg) !important;
  font-family: "JetBrains Mono", monospace !important;
  font-weight: 600 !important;
  padding: 8px var(--sp-3) !important;
  letter-spacing: 0.02em;
}

.recv-lot-status { display: inline-flex; align-items: center; }

.recv-badge {
  display: inline-block;
  font-size: var(--text-xs); font-weight: 600;
  padding: 3px 10px; border-radius: var(--radius-sm);
}
.recv-badge-ok { background: #dbeafe; color: #1d4ed8; }
.recv-badge-err { background: #fee2e2; color: #991b1b; }

/* ── Context strip (replaces cards) ──────────────────────────────── */
.recv-ctx-strip {
  display: flex; flex-wrap: wrap; gap: 2px 14px;
  font-size: 0.82rem; line-height: 1.5;
  padding: 8px 14px;
  background: var(--surface-sunken);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  margin-top: var(--sp-2);
}
.recv-ctx-strip .cl { color: var(--muted); font-weight: 500; }
.recv-ctx-strip .cv { font-weight: 600; color: var(--text); }

/* ── Progress bar ─────────────────────────────────────────────────── */
.recv-progress-wrap {
  margin-top: 6px;
  display: flex; align-items: center; gap: 10px;
}
.recv-progress-bar {
  flex: 1; height: 6px;
  background: var(--border); border-radius: 3px;
  overflow: hidden;
}
.recv-progress-fill {
  height: 100%; border-radius: 3px;
  background: var(--primary);
  transition: width var(--dur-slow) var(--ease-out);
}
.recv-progress-label {
  font-size: 0.78rem; font-weight: 600;
  color: var(--text-secondary);
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}

/* ── Fast entry row ───────────────────────────────────────────────── */
.recv-entry-row {
  display: flex; gap: 8px; align-items: flex-end;
  margin-top: var(--sp-3);
}
.recv-entry-row .recv-field { min-width: 0; }
.recv-entry-row .recv-field-shade { flex: 2.5; min-width: 160px; }
.recv-entry-row .recv-field-bale { flex: 1.5; min-width: 100px; }
.recv-entry-row .recv-field-num { flex: 1; min-width: 70px; }
.recv-entry-row .recv-field-btn { flex: 0 0 auto; }

.recv-label {
  display: block;
  font-size: 0.68rem; font-weight: 600;
  text-transform: uppercase; letter-spacing: 0.04em;
  color: var(--muted); margin-bottom: 3px;
}

.recv-field input, .recv-field select { width: 100%; }

.recv-add-btn { height: 36px; white-space: nowrap; font-weight: 600; min-width: 90px; padding: 0 14px; }

/* ── Collapsible secondary fields ─────────────────────────────────── */
.recv-secondary {
  margin-top: 6px;
}
.recv-secondary summary {
  font-size: 0.78rem; font-weight: 500; color: var(--muted);
  cursor: pointer; padding: 4px 0;
  user-select: none;
}
.recv-secondary summary:hover { color: var(--text); }
.recv-secondary-fields {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(100px, 1fr));
  gap: 8px; margin-top: 6px;
}

/* ── Running lot bale list ────────────────────────────────────────── */
.recv-lot-bales {
  margin-top: var(--sp-3);
  max-height: 320px; overflow-y: auto;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface);
}
.recv-lot-bales-header {
  font-size: 0.78rem; font-weight: 600; color: var(--text-secondary);
  padding: 6px 0 4px;
}
.recv-lot-bales table {
  width: 100%; border-collapse: collapse; font-size: 0.82rem;
}
.recv-lot-bales thead th {
  background: var(--surface-sunken);
  color: var(--muted); font-size: 0.68rem;
  text-transform: uppercase; letter-spacing: 0.04em;
  font-weight: 600; padding: 5px 8px;
  border-bottom: 1px solid var(--border);
  text-align: left; white-space: nowrap;
  position: sticky; top: 0;
}
.recv-lot-bales tbody td {
  padding: 4px 8px;
  border-bottom: 1px solid var(--line);
  vertical-align: middle;
}
.recv-lot-bales tbody tr:hover td { background: var(--surface-sunken); }

/* ── Summary stats ──────────────────────────────────────────────────── */
.recv-summary {
  display: flex; gap: 0;
  border-radius: var(--radius-lg); overflow: hidden;
  border: 1px solid var(--line);
  background: var(--surface);
  box-shadow: var(--shadow-sm);
}

.recv-stat {
  flex: 1;
  padding: var(--sp-4) var(--sp-5);
  display: flex; flex-direction: column; gap: 3px;
  border-right: 1px solid var(--line);
}
.recv-stat:last-child { border-right: none; }

.recv-stat-num {
  font-family: "JetBrains Mono", monospace;
  font-size: var(--text-xl); font-weight: 700;
  color: var(--text); line-height: 1.2;
  font-variant-numeric: tabular-nums;
}

.recv-stat-label {
  font-size: var(--text-xs); font-weight: 500;
  text-transform: uppercase; letter-spacing: 0.04em;
  color: var(--muted);
}

/* ── Recv table ─────────────────────────────────────────────────────── */
.recv-table-section { display: flex; flex-direction: column; gap: var(--sp-4); }

.recv-table {
  width: 100%; border-collapse: collapse;
  font-size: var(--text-base);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  overflow: hidden;
  box-shadow: var(--shadow-sm);
}

.recv-table thead th {
  background: var(--surface-sunken);
  color: var(--text-secondary);
  font-size: var(--text-xs); letter-spacing: 0.04em;
  text-transform: uppercase; font-weight: 600;
  padding: var(--sp-3) var(--sp-4);
  border-bottom: 2px solid var(--line);
  text-align: left; white-space: nowrap;
}

.recv-table tbody td {
  padding: var(--sp-3) var(--sp-4);
  border-bottom: 1px solid var(--border);
  vertical-align: middle;
}

.recv-table tbody tr:last-child td { border-bottom: none; }
.recv-table tbody tr:nth-child(even) td { background: rgba(0,0,0,.015); }
.recv-table tbody tr:hover td { background: var(--surface-sunken); }
.recv-table tbody tr:hover .btn-icon-delete { opacity: 1; }

@media (max-width: 800px) {
  .recv-lot-row { flex-wrap: wrap; }
  .recv-lot-group { flex: 1 1 auto; width: 100%; }
  .recv-entry-row { flex-wrap: wrap; }
  .recv-entry-row .recv-field-shade { flex: 1 1 100%; }
  .recv-summary { flex-direction: column; }
  .recv-stat { border-right: none; border-bottom: 1px solid var(--border); }
  .recv-stat:last-child { border-bottom: none; }
}

/* ═══════════════════════════════════════════════════════════════════════
   Lot Report
   ═══════════════════════════════════════════════════════════════════════ */
.lot-rpt-filter { display: flex; gap: var(--sp-2); flex-wrap: wrap; margin-bottom: var(--sp-1); }

.lot-rpt-tab {
  border: 1px solid var(--border);
  background: var(--surface);
  color: var(--text-secondary);
  border-radius: var(--radius);
  padding: var(--sp-2) var(--sp-4);
  cursor: pointer;
  font-family: inherit;
  font-size: var(--text-sm);
  font-weight: 500;
  transition: all var(--transition);
}
.lot-rpt-tab:hover:not(.active) { background: var(--surface-sunken); border-color: var(--muted); }
.lot-rpt-tab.active {
  background: var(--primary); border-color: var(--primary);
  color: #fff; font-weight: 600;
}

.lot-rpt-area { display: flex; flex-direction: column; gap: var(--sp-4); }
.lot-rpt-section { display: flex; flex-direction: column; gap: var(--sp-2); }

.lot-rpt-section-title {
  font-size: var(--text-sm); font-weight: 600;
  text-transform: uppercase; letter-spacing: 0.04em;
  color: var(--text-secondary);
  padding-bottom: var(--sp-1);
  border-bottom: 1px solid var(--border);
}

.lot-rpt-alerts-cell { display: flex; flex-wrap: wrap; gap: var(--sp-1); max-width: 220px; }

.lot-rpt-tag {
  display: inline-block;
  font-size: var(--text-xs); font-weight: 600;
  padding: 2px var(--sp-2);
  border-radius: var(--radius-sm);
  white-space: nowrap;
}
.lot-rpt-tag-miss  { background: #fee2e2; color: #991b1b; }
.lot-rpt-tag-short { background: var(--amber-light); color: #92400e; }
.lot-rpt-tag-ok    { background: #dcfce7; color: #166534; }
.lot-rpt-tag-partial { background: #dbeafe; color: #1d4ed8; }

.lot-rpt-row-expanded { background: var(--primary-light) !important; }
.lot-rpt-row-expanded td { border-bottom-color: transparent; }
.lot-rpt-detail-cell { padding: 0 var(--sp-3) var(--sp-4) var(--sp-3) !important; background: var(--surface-sunken); }

.lot-rpt-shade-table {
  width: 100%; border-collapse: collapse;
  font-size: var(--text-sm); margin: 0;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm); overflow: hidden;
}
.lot-rpt-shade-table thead th {
  background: #eae5df;
  font-size: var(--text-xs); text-transform: uppercase;
  letter-spacing: 0.04em; font-weight: 600;
  color: var(--text-secondary);
  padding: var(--sp-2) var(--sp-3);
  border-bottom: 1px solid var(--border);
}
.lot-rpt-shade-table tbody td {
  padding: var(--sp-2) var(--sp-3);
  border-bottom: 1px solid var(--border);
}
.lot-rpt-shade-table tbody tr:last-child td { border-bottom: none; }
.lot-rpt-shade-table tbody tr:hover { background: var(--surface-sunken); }

@media (max-width: 800px) {
  .lot-rpt-filter { gap: var(--sp-1); }
  .lot-rpt-tab { padding: 5px var(--sp-3); font-size: var(--text-xs); }
  .lot-rpt-alerts-cell { max-width: 140px; }
}

/* ═══════════════════════════════════════════════════════════════════════
   Collapsible section cards
   ═══════════════════════════════════════════════════════════════════════ */
.section-card {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-sm);
  margin-bottom: var(--sp-4);
  /* WebKit bug 98538/72619 workaround: an `overflow: hidden` rounded
   * parent breaks horizontal touch-scroll capture in descendant flex
   * scroll containers (the chip row) on iOS. Mask-image clips to the
   * same border-radius without creating the broken composited layer. */
  -webkit-mask-image: -webkit-radial-gradient(white, black);
  mask-image: radial-gradient(white, black);
}
.section-header {
  display: flex; align-items: center; gap: var(--sp-3);
  padding: var(--sp-4) var(--sp-5);
  cursor: pointer; user-select: none;
  transition: background var(--transition);
}
.section-header:hover { background: var(--surface-sunken); }
.section-icon {
  width: 34px; height: 34px; border-radius: var(--radius);
  display: flex; align-items: center; justify-content: center;
  font-size: var(--text-md); flex-shrink: 0;
}
.section-icon.shades  { background: var(--primary-light); color: var(--primary); }
.section-icon.bills   { background: var(--amber-light); color: var(--amber); }
.section-icon.history { background: #fce7f3; color: #be185d; }
.section-icon.bales   { background: var(--success-light); color: var(--success); }

.section-title { font-size: var(--text-md); font-weight: 600; color: var(--text); flex: 1; }
.section-badge { font-size: var(--text-sm); color: var(--muted); font-weight: 400; }
.section-chevron { font-size: var(--text-sm); color: var(--muted); transition: transform var(--dur-slow) var(--ease-out); }
.section-card.collapsed .section-chevron { transform: rotate(-90deg); }
.section-body { padding: 0 var(--sp-5) var(--sp-5); }
.section-card.collapsed .section-body { display: none; }

/* ── Shade progress bar ─────────────────────────────────────────────── */
.shade-progress {
  width: 60px; height: 5px;
  background: var(--line); border-radius: 3px;
  overflow: hidden; display: inline-block;
  vertical-align: middle; margin-left: var(--sp-2);
}

/* ── Print ──────────────────────────────────────────────────────────── */
@media print {
  body > *:not(.print-qr-view) { display: none !important; }
  .print-qr-view { display: block !important; }
  .no-print { display: none !important; }
}

/* ═══════════════════════════════════════════════════════════════════════
   PWA P0 Foundation — mobile native-feel (desktop-safe via gating)
   Appended 2026-04-13 as part of PWA Phase 1. Every rule below is either
   (a) universally safe (env() returns 0 on desktop, touch-action only
   affects touch input), or (b) gated to standalone/mobile so desktop is
   pixel-identical.
   ═══════════════════════════════════════════════════════════════════════ */
/* Removed: html { touch-action: manipulation }.
 * Per the W3C spec, touch-action intersects up the ancestor chain.
 * On iOS Safari, having `manipulation` on <html> caused diagonal swipes
 * inside descendants like `#lst-chips` (a horizontal scroll container)
 * to trigger pointercancel — horizontal chip scroll became unreliable.
 * The 300ms-tap-delay reason for adding this globally was obsoleted in
 * iOS 9.3 (WebKit blog 5610). Tap targets that need it can declare it
 * locally. */
body {
  padding-top: env(safe-area-inset-top);
  padding-left: env(safe-area-inset-left);
  padding-right: env(safe-area-inset-right);
}
button, .nav-item, .sidebar, .main-header, .bottom-nav {
  -webkit-user-select: none;
  user-select: none;
  -webkit-tap-highlight-color: transparent;
  /* Prevent iOS long-press context menu (Copy/Save Image) on UI chrome.
     Long-press on data values still works in non-button content. */
  -webkit-touch-callout: none;
}

/* PWA native-feel polish — prevents accidental clicks + tactile feedback. */
@media (hover: none) and (pointer: coarse) {
  /* Tactile press: every interactive element scales subtly on touch
     so the user gets immediate visual confirmation BEFORE the click
     resolves. Combines with iOS's own press dim. Cards / chips / nav
     items / buttons all participate. */
  button:active,
  .chip:active,
  .nav-item:active,
  .kb-card:active,
  .od-line-row:active,
  .be-card:active {
    transform: scale(0.97);
    transition: transform 120ms cubic-bezier(.4, 0, .2, 1);
  }
  /* Tap targets: every clickable surface enforces ≥44x44 hit area
     (Apple HIG minimum) via min-height. The visible chrome can be
     smaller — invisible padding extends the target. */
  button, .chip, .nav-fav, .od-line-edit, .od-line-del {
    min-height: 36px;
    min-width: 36px;
  }
  /* Disable text selection on entire interactive ROWS so a long-press
     during scroll doesn't pop the selection cursor + native menu. */
  .od-line-row, .kb-card, #module-workspace table tbody tr {
    -webkit-user-select: none;
    user-select: none;
    -webkit-touch-callout: none;
  }
  /* Native momentum scrolling on iOS for any scrollable container. */
  .sidebar, #module-workspace, #lst-table-wrap, .modal-dialog {
    -webkit-overflow-scrolling: touch;
  }
  /* Prevent the 50ms "ghost click" that iOS Safari sometimes fires
     after a scroll — pointer-events:none briefly during scroll is
     usually solved automatically on modern WebKit, but we add the
     defense for older PWA wrappers. */
  *[data-no-row-click="true"], .no-tap-passthrough {
    pointer-events: none;
  }
}

/* Block Android pull-to-refresh + scroll-chaining from any inner scroller
   to the document. Without this, a tap-with-tiny-drift near the top of a
   list can be interpreted by the OS as the start of a vertical pan, which
   shifts the document under the finger between pointerdown and pointerup
   and re-targets `click` to a different element. We saw this on Order
   Items: pointerdown lands on the first row TR; pointerup retargets to
   the chip-row above; click bubbles to .section-card. The same retargeting
   broke chip taps. `overscroll-behavior: none` blocks both the default
   browser action (pull-to-refresh) AND scroll-chaining, in all browsing
   modes (PWA standalone already had this — now it applies in regular tabs
   too). */
html, body { overscroll-behavior: none; }
#lst-table-wrap, .chip-row, #lst-chips { overscroll-behavior: contain; }

@media (display-mode: standalone) {
  html, body { overscroll-behavior: none; }
  /* Bottom-nav respects the iOS home-indicator inset so the labels
     don't sit underneath the swipe handle. */
  .bottom-nav {
    padding-bottom: max(8px, env(safe-area-inset-bottom));
  }
}

/* Mobile-gated tap-target enlargement — desktop pixel-identical */
@media (hover: none) and (pointer: coarse), (max-width: 1080px) {
  .nav-item { padding: 14px var(--sp-3); }
  .recv-add-btn { height: 44px; }
}

/* iOS zoom fix: inputs must be ≥16px on coarse-pointer devices so Safari
   doesn't auto-zoom on focus. Mobile-gated so desktop forms unchanged. */
@media (hover: none) and (pointer: coarse) {
  input, select, textarea { font-size: 16px; }
}

/* Online/offline banner — slim strip at top of viewport */
.net-banner {
  position: fixed; left: 0; right: 0; top: 0; z-index: 10000;
  padding: 6px 12px;
  padding-top: calc(6px + env(safe-area-inset-top));
  font-size: 0.85rem; font-weight: 600; text-align: center;
  background: #dc2626; color: #fff;
  transform: translateY(-100%); transition: transform var(--dur-base) var(--ease-out);
  pointer-events: none;
}
.net-banner.show { transform: translateY(0); }
.net-banner.online { background: #059669; }

/* Install prompt banner — docked at bottom, safe-area aware */
.install-banner {
  position: fixed; left: 12px; right: 12px; z-index: 9999;
  bottom: calc(12px + env(safe-area-inset-bottom));
  padding: 14px 16px; border-radius: 12px;
  background: #1c1917; color: #fff;
  box-shadow: 0 12px 32px rgba(0,0,0,0.25);
  display: none; align-items: center; gap: 12px;
}
.install-banner.show { display: flex; }
.install-banner .msg { flex: 1; font-size: 0.9rem; line-height: 1.35; }
.install-banner button {
  background: #fff; color: #1c1917; border: none;
  padding: 8px 14px; border-radius: 8px; font-weight: 600;
  font-size: 0.85rem; cursor: pointer; min-height: 36px;
}
.install-banner button.secondary {
  background: transparent; color: #d6d3d1;
  padding: 8px 10px;
}

/* ═══════════════════════════════════════════════════════════════════════
   Dyeing Mobile Design System (PWA) — 2026 refresh
   All rules below are mobile-gated. Desktop pixel-identical to prior.
   See /Users/neekunjshah/.claude/plans/peppy-shimmying-thunder.md
   ═══════════════════════════════════════════════════════════════════════ */
@media (hover: none) and (pointer: coarse), (max-width: 768px) {
  /* ── Stage A: design tokens + typography + root layout ───────── */
  :root {
    --radius-card: 14px;
    --radius-btn: 10px;
    --radius-pill: 999px;
    --shadow-sm: 0 1px 2px rgba(0,0,0,0.04);
    --shadow-md: 0 4px 12px rgba(0,0,0,0.08);
    --shadow-lg: 0 12px 32px rgba(0,0,0,0.15);
    --success: #059669;
    --warning: #d97706;
    --danger: #dc2626;
    --info: #2563eb;
    /* --ease-out + --ease-in-out inherit from :root (v2-motion-polish).
       --t-press / --t-reveal remain as shorthand aliases used by mobile
       rules below; they now resolve to the primitive easing tokens. */
    --t-press: 150ms var(--ease-out);
    --t-reveal: 220ms var(--ease-in-out);
    --fs-body: 17px;
    --fs-h1: 26px;
    --fs-h2: 20px;
    --fs-caption: 13px;
    --fs-label: 11px;
  }

  html, body { overflow-x: clip; max-width: 100vw; font-size: var(--fs-body); }
  body { -webkit-text-size-adjust: 100%; }
  .layout, .main, #module-workspace { min-width: 0; max-width: 100vw; }

  #module-workspace { padding: 12px 14px 96px 14px; }
  .main-header h2 { font-size: var(--fs-h2); line-height: 1.2; }
  .main-header p  { font-size: var(--fs-caption); }

  .detail-table thead th,
  .tracking-table td, .tracking-table th,
  .recv-table td, .recv-table th { white-space: normal; }

  .recv-field-date, .recv-field-main, .recv-field-qty,
  .recv-field-net, .recv-field-gross, .recv-add-btn { min-width: 0; }
  .recv-lot-row { flex-wrap: wrap; }

  /* ── Stage B: card transform + progressive disclosure ─────────── */
  .main-header {
    position: sticky; top: 0; z-index: 50;
    background: rgba(250, 250, 249, 0.92);
    -webkit-backdrop-filter: saturate(180%) blur(8px);
    backdrop-filter: saturate(180%) blur(8px);
    border-bottom: 1px solid var(--line);
    padding: 10px 14px;
  }
  #search-bar-wrap { width: 100%; max-width: none; flex: 1 1 100%; }
  #search-input {
    width: 100%;
    font-size: 16px;
    padding: 10px 14px;
    border-radius: var(--radius-pill);
  }

  #module-workspace table {
    display: block;
    width: 100%;
    border-collapse: separate;
    border-spacing: 0;
  }
  #module-workspace table thead { display: none; }
  #module-workspace table tbody,
  #module-workspace table tr { display: block; width: 100%; }

  #module-workspace table tbody tr {
    position: relative;
    padding: 14px 16px;
    margin-bottom: 12px;
    border: 1px solid var(--line);
    border-radius: var(--radius-card);
    background: var(--surface, #fff);
    box-shadow: var(--shadow-sm);
    cursor: pointer;
    transition: transform var(--t-press), box-shadow var(--t-press);
  }
  #module-workspace table tbody tr:active { transform: scale(0.985); }
  #module-workspace table tbody tr.card-open { box-shadow: var(--shadow-md); }

  #module-workspace table tbody td {
    display: none;
    padding: 4px 0;
    border: none;
    text-align: left;
    white-space: normal;
  }

  #module-workspace table tbody td:nth-child(1) {
    display: block;
    font-weight: 700;
    font-size: 17px;
    line-height: 1.3;
    color: var(--text, #1c1917);
    padding-right: 30px;
  }
  #module-workspace table tbody td:nth-child(2) {
    display: block;
    font-size: var(--fs-caption);
    color: var(--muted, #78716c);
    margin-top: 3px;
  }
  #module-workspace table tbody td:nth-child(1)::before,
  #module-workspace table tbody td:nth-child(2)::before { content: none; }

  #module-workspace table tbody tr.card-open > td:nth-child(n+3) {
    display: grid;
    grid-template-columns: minmax(100px, 40%) 1fr;
    gap: 6px 12px;
    padding: 8px 0;
    margin-top: 6px;
    border-top: 1px dashed var(--line);
    font-size: var(--fs-caption);
  }
  #module-workspace table tbody tr.card-open > td:nth-child(n+3)::before {
    content: attr(data-label);
    font-size: var(--fs-label);
    font-weight: 600;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--muted, #78716c);
    align-self: center;
  }
  #module-workspace table tbody tr.card-open > td:not([data-label]),
  #module-workspace table tbody tr.card-open > td[data-label=""] {
    grid-template-columns: 1fr;
  }
  #module-workspace table tbody tr.card-open > td:not([data-label])::before,
  #module-workspace table tbody tr.card-open > td[data-label=""]::before { content: none; }

  #module-workspace table tbody td[data-label*="Status" i] { font-weight: 600; }

  #module-workspace table tbody tr::after {
    content: "";
    position: absolute;
    right: 18px;
    top: 20px;
    width: 9px;
    height: 9px;
    border-right: 2px solid var(--muted, #78716c);
    border-bottom: 2px solid var(--muted, #78716c);
    transform: rotate(45deg);
    transition: transform var(--t-press);
  }
  #module-workspace table tbody tr.card-open::after { transform: rotate(225deg); top: 24px; }

  .card-expand-toolbar {
    display: flex;
    justify-content: flex-end;
    gap: 8px;
    margin: 0 0 10px 0;
  }
  .card-expand-toolbar button {
    background: var(--surface, #fff);
    border: 1px solid var(--line);
    border-radius: var(--radius-pill);
    padding: 8px 16px;
    font-size: var(--fs-caption);
    font-weight: 600;
    color: var(--muted);
    cursor: pointer;
    transition: transform var(--t-press), background var(--t-press);
  }
  .card-expand-toolbar button:active { transform: scale(0.96); background: #f5f5f4; }

  /* ── Stage C: bottom nav + FAB + bottom sheets + utilities ───── */
  .bottom-nav {
    position: fixed;
    left: 0; right: 0; bottom: 0;
    z-index: 100;
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    background: var(--surface, #fff);
    border-top: 1px solid var(--line);
    padding-bottom: env(safe-area-inset-bottom);
    box-shadow: 0 -4px 16px rgba(0,0,0,0.04);
  }
  .bottom-nav button {
    background: none;
    border: none;
    cursor: pointer;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    padding: 10px 4px 8px 4px;
    min-height: 56px;
    font-size: 11px;
    font-weight: 600;
    color: var(--muted);
    gap: 2px;
    transition: color var(--t-press), transform var(--t-press);
  }
  .bottom-nav button .icon { font-size: 20px; line-height: 1; }
  .bottom-nav button.active { color: var(--info, #2563eb); }
  .bottom-nav button:active { transform: scale(0.94); }

  .fab {
    position: fixed;
    z-index: 90;
    right: 18px;
    bottom: calc(76px + env(safe-area-inset-bottom));
    width: 56px; height: 56px;
    border-radius: 50%;
    background: var(--info, #2563eb);
    color: #fff;
    border: none;
    font-size: 30px;
    font-weight: 300;
    line-height: 1;
    box-shadow: var(--shadow-md);
    cursor: pointer;
    transition: transform var(--t-press), box-shadow var(--t-press);
  }
  .fab:active { transform: scale(0.92); box-shadow: var(--shadow-sm); }

  .sheet-mobile {
    position: fixed !important;
    inset: 0 !important;
    background: rgba(0,0,0,0.4) !important;
    display: flex !important;
    flex-direction: column;
    justify-content: flex-end !important;
    z-index: 1000;
  }
  .sheet-mobile > div:not(.sheet-backdrop) {
    background: var(--surface, #fff);
    border-radius: 18px 18px 0 0;
    max-height: 92dvh;
    overflow-y: auto;
    padding: 20px 18px calc(20px + env(safe-area-inset-bottom)) 18px;
    box-shadow: var(--shadow-lg);
    animation: sheetSlideUp var(--dur-drawer) var(--ease-out);
    position: relative;
  }
  @keyframes sheetSlideUp {
    from { transform: translateY(100%); }
    to   { transform: translateY(0); }
  }
  .sheet-mobile > div:not(.sheet-backdrop)::before {
    content: "";
    display: block;
    width: 40px;
    height: 4px;
    margin: -6px auto 14px auto;
    background: #d6d3d1;
    border-radius: 999px;
  }

  .toast, #toast, .toast-stack {
    bottom: calc(80px + env(safe-area-inset-bottom)) !important;
  }

  .skeleton {
    background: linear-gradient(90deg, #f5f5f4 25%, #e7e5e4 50%, #f5f5f4 75%);
    background-size: 200% 100%;
    animation: shimmer 1.4s linear infinite;
    border-radius: 8px;
  }
  @keyframes shimmer {
    from { background-position: 200% 0; }
    to   { background-position: -200% 0; }
  }
  .skeleton-row {
    display: flex;
    flex-direction: column;
    gap: 8px;
    padding: 14px 16px;
    margin-bottom: 12px;
    border: 1px solid var(--line);
    border-radius: var(--radius-card);
    background: var(--surface, #fff);
  }
  .skeleton-row .skeleton.bar-lg { height: 18px; width: 60%; }
  .skeleton-row .skeleton.bar-sm { height: 12px; width: 40%; }

  .badge-pill {
    display: inline-flex;
    align-items: center;
    padding: 3px 10px;
    border-radius: var(--radius-pill);
    font-size: 11px;
    font-weight: 700;
    letter-spacing: 0.02em;
    text-transform: uppercase;
  }
  .badge-pill.success { background: #d1fae5; color: var(--success); }
  .badge-pill.warning { background: #fed7aa; color: var(--warning); }
  .badge-pill.danger  { background: #fee2e2; color: var(--danger); }
  .badge-pill.info    { background: #dbeafe; color: var(--info); }

  #hamburger-btn { display: none !important; }

  /* ── Refinement 1 (v12.10): post-v12.9 alignment fixes ────────── */

  /* Fix A: stack existing chat FAB above the new + FAB with a gap.
     !important needed because #chat-fab carries inline cssText from
     app.js that would otherwise win. */
  #chat-fab {
    bottom: calc(150px + env(safe-area-inset-bottom)) !important;
    right: 18px !important;
    width: 52px !important;
    height: 52px !important;
    z-index: 91 !important;
  }
  #chat-panel {
    bottom: calc(72px + env(safe-area-inset-bottom)) !important;
    right: 12px !important;
    left: 12px !important;
    max-height: calc(100dvh - 180px) !important;
    z-index: 1001 !important;
  }

  /* Fix B: declutter the mobile header toolbar.
     FAB replaces "+New". User info + Logout demoted to sidebar drawer
     (reachable via bottom-nav More tab). */
  .main-header #new-record-btn { display: none !important; }
  #user-wrap { display: none !important; }
  .main-header .toolbar { gap: 8px; }

  /* Fix C: hide the "Records" panel-header ghost above list tables.
     The sticky main-header already shows the module title on mobile. */
  #module-workspace .panel-header { display: none !important; }

  /* Row card transition. The legacy `touch-action: pan-y` (carry-over from a
     swipe-to-reveal JS handler that no longer exists) was suppressing tap
     dispatch on Android Chrome PWA — clicks weren't registering on Export
     Entries cards. iOS WebKit shrugged it off; Android required removal. */
  #module-workspace table tbody tr {
    transition: transform var(--dur-slow) var(--ease-out);
    overflow: visible;
  }
  .swipe-actions {
    position: absolute;
    top: 0; right: -120px;
    height: 100%;
    width: 120px;
    display: flex;
    align-items: stretch;
    justify-content: center;
    padding: 8px;
    gap: 6px;
  }
  .swipe-actions .swipe-action {
    flex: 1;
    border: none;
    border-radius: 10px;
    color: #fff;
    font-weight: 700;
    font-size: 12px;
    cursor: pointer;
    transition: transform var(--t-press);
  }
  .swipe-actions .swipe-action.primary { background: var(--info, #2563eb); }
  .swipe-actions .swipe-action.danger  { background: var(--danger, #dc2626); }
  .swipe-actions .swipe-action:active  { transform: scale(0.94); }
}

/* ============================================================
   UX Polish — Apple HIG + Things 3 inspiration
   Added 2026-04-23. Purely additive; existing classes untouched.
   ============================================================ */

html { color-scheme: light; }

/* Delivery-status badge variants (migrated from inline styles in
   DELIVERY_STATUS_STYLES). Pair with .badge base class. */
.badge.delivered { background: var(--surface-sunken); color: var(--text-secondary); border-color: var(--line); }
.badge.lost      { background: var(--danger-light);   color: #991b1b;               border-color: var(--danger); }
.badge.damaged   { background: var(--amber-light);    color: var(--amber);          border-color: var(--amber-border); }
.badge.cancelled { background: var(--surface-sunken); color: var(--muted);          border-color: var(--line); text-decoration: line-through; }
.badge.outline   { background: transparent; }
.badge.lost,
.badge.damaged,
.badge.cancelled,
.badge.delivered { font-weight: 500; }

/* Numbers align like a spreadsheet (Things 3 signature on task counts). */
.detail-table td,
.detail-meta-value,
.badge { font-variant-numeric: tabular-nums; }

/* Apple-style label tracking for meta-grid definition labels. */
.detail-meta-label { letter-spacing: 0.06em; font-size: 11px; }

/* Subtle display-text tightening at large sizes. */
.section-title,
.detail-title,
.modal-title { letter-spacing: -0.01em; }

.detail-title {
  font-size: var(--text-2xl);
  font-weight: 600;
  letter-spacing: -0.02em;
  color: var(--text);
  margin: 0;
}

/* ── Row states on data tables ───────────────────────────────── */
.detail-table tbody tr { transition: background var(--transition); }
.detail-table tbody tr:hover { background: var(--surface-sunken); }
.detail-table tbody tr.row--dimmed { opacity: 0.72; background: #fafafa; }
.detail-table tbody tr.row--dimmed:hover { opacity: 0.92; background: var(--surface-sunken); }
.detail-table tbody tr:focus-within { outline: 2px solid var(--primary); outline-offset: -2px; }

/* ── Section card lift on hover ──────────────────────────────── */
.section-card { transition: box-shadow var(--transition), transform var(--transition); }
.section-card:hover { box-shadow: var(--shadow-sm); }

/* ── Meta-grid item: hover hairline ──────────────────────────── */
.detail-meta-item {
  padding-bottom: 2px;
  border-bottom: 1px solid transparent;
  transition: border-color var(--transition);
}
.detail-meta-item:hover { border-bottom-color: var(--line); }

/* ── Modal primitive (Apple sheet-style) ─────────────────────── */
.modal-backdrop {
  position: fixed; inset: 0; z-index: 9999;
  background: rgba(15, 14, 12, 0.42);
  backdrop-filter: blur(6px) saturate(120%);
  -webkit-backdrop-filter: blur(6px) saturate(120%);
  display: flex; align-items: center; justify-content: center;
  padding: var(--sp-4);
  animation: modal-backdrop-in var(--dur-base) var(--ease-out);
}
.modal-dialog {
  background: var(--surface-overlay);
  border-radius: 16px;
  box-shadow: var(--shadow-lg);
  max-width: 460px; width: 100%;
  padding: var(--sp-5) var(--sp-5) var(--sp-4);
  animation: modal-dialog-in var(--dur-slow) var(--ease-out);
  transform-origin: center center;
}
.modal-title {
  font-weight: 600;
  font-size: var(--text-md);
  color: var(--text);
  margin: 0 0 var(--sp-3) 0;
}
.modal-subtle { font-size: var(--text-xs); color: var(--text-secondary); margin-top: var(--sp-1); }
.modal-actions {
  display: flex; justify-content: flex-end; gap: var(--sp-2);
  margin-top: var(--sp-4);
  padding-top: var(--sp-3);
  border-top: 1px solid var(--line);
}
@keyframes modal-backdrop-in { from { opacity: 0 } to { opacity: 1 } }
@keyframes modal-dialog-in   { from { opacity: 0; transform: translateY(16px) scale(0.96) } to { opacity: 1; transform: none } }

@media (max-width: 640px) {
  .modal-backdrop { align-items: flex-end; padding: 0; }
  .modal-dialog {
    border-radius: 20px 20px 0 0;
    max-width: 100%;
    padding-bottom: calc(var(--sp-5) + env(safe-area-inset-bottom));
    animation-name: modal-sheet-in;
  }
  @keyframes modal-sheet-in { from { transform: translateY(100%) } to { transform: none } }
}

/* ── Form field (semantic label + input pair) ────────────────── */
.form-field { display: flex; flex-direction: column; gap: var(--sp-1); margin-top: var(--sp-3); }
.form-field .form-label {
  font-size: var(--text-xs);
  font-weight: 500;
  color: var(--text-secondary);
  letter-spacing: 0.02em;
}

/* ── Inline warning block (amber notice inside cards/panels) ─── */
.inline-warning {
  background: var(--amber-light);
  border: 1px solid var(--amber-border);
  color: var(--amber);
  border-radius: var(--radius);
  padding: var(--sp-2) var(--sp-3);
  font-size: var(--text-xs);
  margin-top: var(--sp-3);
  display: flex; gap: var(--sp-2); align-items: flex-start;
  line-height: 1.5;
}
.inline-warning::before { content: "⚠"; flex-shrink: 0; }

/* ── Delivery panel (inside Bill Edit form) ──────────────────── */
.delivery-panel {
  margin-top: var(--sp-5);
  padding: var(--sp-4) var(--sp-5);
  background: var(--surface-sunken);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
}
.delivery-panel__header {
  display: flex; align-items: center; gap: var(--sp-3);
  margin-bottom: var(--sp-3);
}
.delivery-panel__title {
  font-weight: 600;
  font-size: var(--text-md);
  color: var(--text);
  letter-spacing: -0.01em;
}
.delivery-panel__meta {
  margin-left: auto;
  font-size: var(--text-xs);
  color: var(--text-secondary);
}

/* ── Button loading state (spinner) ──────────────────────────── */
.btn.is-loading { position: relative; color: transparent !important; pointer-events: none; }
.btn.is-loading::after {
  content: "";
  position: absolute; inset: 0; margin: auto;
  width: 14px; height: 14px;
  border: 2px solid currentColor;
  border-top-color: transparent;
  border-radius: 50%;
  animation: spin 0.7s linear infinite;
  color: #fff;
}
.btn.small.is-loading::after { width: 11px; height: 11px; border-width: 1.5px; }
@keyframes spin { to { transform: rotate(360deg) } }

/* ── Toast stack (multi-toast, typed variants) ───────────────── */
.toast-stack {
  position: fixed;
  bottom: 24px;
  left: 50%; transform: translateX(-50%);
  display: flex; flex-direction: column; gap: var(--sp-2);
  z-index: 10000;
  pointer-events: none;
  max-width: calc(100vw - var(--sp-4) * 2);
}
.toast-item {
  background: var(--text);
  color: var(--surface);
  padding: 10px 16px;
  border-radius: var(--radius);
  box-shadow: var(--shadow-lg);
  display: inline-flex; align-items: center; gap: var(--sp-2);
  font-size: var(--text-sm);
  font-weight: 500;
  animation: toast-in var(--dur-base) var(--ease-out);
  pointer-events: auto;
  line-height: 1.4;
}
.toast-item.success { background: var(--success); }
.toast-item.error   { background: var(--danger); }
.toast-item.warning { background: var(--amber); color: #fff; }
.toast-item.info    { background: var(--primary); }
.toast-item.fade-out { animation: toast-out var(--dur-fast) var(--ease-in-out) forwards; }
@keyframes toast-in  { from { opacity: 0; transform: translateY(8px) } to { opacity: 1; transform: none } }
@keyframes toast-out { to   { opacity: 0; transform: translateY(8px) } }

/* Mobile: lift above FAB + bottom nav (PWA layer takes over at ≤768px). */
@media (max-width: 768px) {
  .toast-stack { bottom: calc(80px + env(safe-area-inset-bottom)); }
}

/* ── Skeleton rows ───────────────────────────────────────────── */
.skeleton-row {
  height: 44px;
  display: flex; align-items: center; gap: var(--sp-4);
  padding: 0 var(--sp-4);
  border-bottom: 1px solid var(--line);
}
.skeleton-cell {
  height: 14px;
  border-radius: var(--radius-sm);
  flex: 1;
  background: linear-gradient(90deg, var(--surface-sunken) 0%, #f6f4f0 40%, var(--surface-sunken) 80%);
  background-size: 200% 100%;
  animation: skeleton-shimmer 1.4s infinite;
}
.skeleton-cell.w-20 { flex: 0 0 20% }
.skeleton-cell.w-12 { flex: 0 0 12% }
.skeleton-cell.w-8  { flex: 0 0 8% }
@keyframes skeleton-shimmer {
  0%   { background-position:  200% 0 }
  100% { background-position: -200% 0 }
}

/* ── Empty-state block ───────────────────────────────────────── */
.empty-state {
  display: flex; flex-direction: column; align-items: center;
  gap: var(--sp-3);
  padding: var(--sp-8) var(--sp-4);
  color: var(--muted);
  text-align: center;
}
.empty-state__icon  { font-size: 32px; opacity: 0.65; }
.empty-state__title { font-size: var(--text-md); color: var(--text); font-weight: 600; letter-spacing: -0.01em; }
.empty-state__hint  { font-size: var(--text-sm); color: var(--text-secondary); max-width: 320px; line-height: 1.5; }
.empty-state__cta   { margin-top: var(--sp-2); }

/* ── Lot-report tabs: animated underline ─────────────────────── */
.lot-rpt-tab { position: relative; transition: color var(--transition); }
.lot-rpt-tab::after {
  content: "";
  position: absolute;
  left: 12%; right: 12%; bottom: -2px;
  height: 2px;
  background: var(--primary);
  transform: scaleX(0);
  transform-origin: center;
  transition: transform var(--dur-slow) var(--ease-out);
  border-radius: 2px;
}
.lot-rpt-tab.active::after { transform: scaleX(1); }

/* ── Shade progress bar (Lot Report) ─────────────────────────── */
.shade-progress-track {
  height: 3px;
  width: 100%;
  background: var(--surface-sunken);
  border-radius: var(--radius-full);
  overflow: hidden;
  margin-top: 3px;
}
.shade-progress-fill {
  height: 100%;
  background: var(--success);
  border-radius: var(--radius-full);
  transition: width var(--dur-slow) var(--ease-out);
}
.shade-progress-fill.warn { background: var(--amber); }
.shade-progress-fill.danger { background: var(--danger); }

/* ── Respect reduced motion ──────────────────────────────────── */
@media (prefers-reduced-motion: reduce) {
  .modal-backdrop, .modal-dialog, .toast-item, .toast-item.fade-out,
  .skeleton-cell, .lot-rpt-tab::after, .shade-progress-fill,
  .section-card, .detail-meta-item, .detail-table tbody tr,
  .btn.is-loading::after {
    animation: none !important;
    transition: none !important;
  }
}

/* ═══ v2 UI hook (Stage 5 of audit wave) ══════════════════════════════
   Phase 2 commits append rules scoped to html[data-ui-version="v2"] so
   Phase-2 styling lives alongside v1 (no separate stylesheet fork).

   v2-mockup-tokens · 2026-04-24 — ported the `:root` token block from
   `ui-mockup.html` (Option B design system) into this v2-scoped override.
   Only SEMANTIC tokens that already exist in styles.css are re-declared;
   v1 users (`?v2=0`) still resolve tokens to their v17 primitives and
   see the unchanged stone/white palette.

   Map (mockup → styles.css semantic):
     --c-surface-1        → --surface
     --c-surface-2        → --surface-raised (new alias, unused in v1)
     --c-surface-3        → --surface-sunken
     --c-content-1        → --text
     --c-content-2        → --text-secondary
     --c-content-3        → --muted
     --c-line-1           → --line / --border
     --c-line-2           → --line-strong (new alias)
     --c-primary          → --primary
     --c-primary-soft     → --primary-light
     --c-primary-ring     → --primary-glow
     --c-success          → --success
     --c-success-soft     → --success-light
     --c-danger           → --danger
     --c-danger-soft      → --danger-light
     --c-amber            → --amber
     --c-amber-soft       → --amber-light
     --f-display  (28px)  → --text-2xl
     --f-heading  (20px)  → --text-xl
     --f-title    (16px)  → --text-lg   (mockup "title" size)
     --f-body     (14px)  → --text-base
     --f-caption  (12px)  → --text-xs
     --r-sm / --r-md / --r-lg / --r-pill → --radius-sm / --radius / --radius-lg / --radius-full
     --shadow-sm / --shadow-md / --shadow-lg / --shadow-dialog

   Mockup tokens with NO styles.css counterpart (skipped — belong to
   per-module accents already shipped in v2-p2-u5c):
     --c-surface-ink (full-bleed dark surface — no usage yet)
     --c-content-4   (fourth-tier dim grey — no usage yet)
     --s-1 … --s-9  (mockup spacing; styles.css uses --sp-1…--sp-8,
                      values already aligned 4/8/12/16/20/24/32/40)

   Literal `#8b5cf6` in v2-p2-u5c line ~2466 is intentional — the
   mockup's violet "bills" module accent is a one-off outside the
   semantic token set. Left as-is.
   ═══════════════════════════════════════════════════════════════════════ */
html[data-ui-version="v2"] {
  /* ── Surfaces (warm off-white + ivory sunken) ─────────────────────── */
  --surface:         #fbfaf8;               /* mockup --c-surface-1 */
  --surface-raised:  #ffffff;               /* mockup --c-surface-2 */
  --surface-sunken:  #f4f2ed;               /* mockup --c-surface-3 */
  --surface-muted:   #f4f2ed;               /* read-only input fills — match sunken */
  --bg:              #fbfaf8;               /* app background = surface-1 */

  /* ── Text hierarchy (warm near-black → beige-grey) ────────────────── */
  --text:            #0f0e0c;               /* mockup --c-content-1 */
  --text-secondary:  #4b4843;               /* mockup --c-content-2 */
  --muted:           #8a857d;               /* mockup --c-content-3 */

  /* ── Hairlines (warm taupe instead of cool slate) ─────────────────── */
  --line:            #ebe7df;               /* mockup --c-line-1 */
  --border:          #ebe7df;               /* alias (used by reset) */
  --line-strong:     #d9d3c7;               /* mockup --c-line-2 */

  /* ── Brand (indigo — same as v1 but declared for a clean map) ─────── */
  --primary:         #4f46e5;               /* mockup --c-primary */
  --primary-hover:   #4338ca;
  --primary-light:   #eef2ff;               /* mockup --c-primary-soft */
  --primary-glow:    rgba(79,70,229,0.18);  /* mockup --c-primary-ring */

  /* ── Semantic status — warmer reds/greens/ambers ──────────────────── */
  --success:         #16a34a;               /* mockup --c-success */
  --success-light:   #dcfce7;               /* mockup --c-success-soft */
  --danger:          #dc2626;               /* mockup --c-danger */
  --danger-light:    #fee2e2;               /* mockup --c-danger-soft */
  --amber:           #d97706;               /* mockup --c-amber (orange-700) */
  --amber-light:     #fef3c7;               /* mockup --c-amber-soft */

  /* ── Typography scale (px, matching mockup's tighter ramp) ────────── */
  --text-2xl:        28px;                  /* mockup --f-display */
  --text-xl:         22px;                  /* between heading/title */
  --text-lg:         20px;                  /* mockup --f-heading */
  --text-md:         16px;                  /* mockup --f-title */
  --text-base:       14px;                  /* mockup --f-body */
  --text-sm:         13px;                  /* labels / captions */
  --text-xs:         12px;                  /* mockup --f-caption */

  /* ── Radii (slightly softer than v1) ──────────────────────────────── */
  --radius-sm:       8px;                   /* mockup --r-sm */
  --radius:          12px;                  /* mockup --r-md */
  --radius-lg:       16px;                  /* mockup --r-lg */
  --radius-full:     999px;                 /* mockup --r-pill */

  /* ── Shadows (tinted warm black 15,14,12) ─────────────────────────── */
  --shadow-xs:       0 1px 2px rgba(15,14,12,0.04);
  --shadow-sm:       0 1px 2px rgba(15,14,12,0.06);
  --shadow-md:       0 2px 8px rgba(15,14,12,0.07);
  --shadow-lg:       0 12px 30px rgba(15,14,12,0.10);
  --shadow-dialog:   0 24px 60px rgba(15,14,12,0.22);

  /* ── Sidebar (mockup: light ivory matching main, not dark navy) ────── */
  --sidebar-bg:      #fbfaf8;               /* match --surface */
  --sidebar-text:    #0f0e0c;               /* match --text */
  --sidebar-muted:   #8a857d;               /* match --muted */
  --sidebar-active-bg:  #eef2ff;            /* --primary-light */
  --sidebar-active-fg:  #4f46e5;            /* --primary */
  --sidebar-border: #ebe7df;                /* match --line */
}

/* ════════════════════════════════════════════════════════════════════════
   DARK THEME — opt-in via <html data-theme="dark">
   Overrides ONLY semantic tokens (primitives stay untouched).
   Designed for indigo brand: brand shifts to indigo-400 on dark for
   WCAG-AA contrast. Surface stack follows Spotify's elevation model
   (sunken → base → surface → surface-2). Toggle persists in localStorage
   under key "orders_theme".
   ════════════════════════════════════════════════════════════════════════ */
html[data-theme="dark"] {
  /* Surfaces — 4 elevation levels, warm-tinted not pure black.
     Elevation chart (low → high):
       sunken (#0e0e0e) → bg (#121212) → surface (#1c1c1e)
       → surface-raised / surface-popover (#242427)
       → surface-overlay (#2a2a2e — modals, biggest overlays) */
  --bg:              #121212;
  --surface:         #1c1c1e;
  --surface-raised:  #242427;
  --surface-sunken:  #0e0e0e;
  --surface-muted:   #1c1c1e;
  --surface-popover: #242427;   /* popovers, palette, tooltips */
  --surface-overlay: #2a2a2e;   /* modals, drawers (top-most layer) */

  /* Hairlines */
  --line:            #2a2a2e;
  --border:          #2a2a2e;
  --line-strong:     #3f3f46;

  /* Text — never pure white, gentler on the eyes */
  --text:            #ededed;
  --text-secondary:  #a1a1aa;
  --muted:           #71717a;

  /* Brand — indigo-400 for contrast on dark; indigo-500 for hover; pure
     white on the brand swatch itself for filled buttons */
  --primary:         #818cf8;
  --primary-hover:   #6366f1;
  --primary-light:   #1e1b4b;       /* very dark indigo for "tint" backgrounds */
  --primary-glow:    rgba(129,140,248,0.22);

  /* Status colours — desaturated for dark backgrounds */
  --success:         #4ade80;
  --success-strong:  #4ade80;
  --success-dark:    #22c55e;
  --success-light:   #052e16;
  --success-tint:    #052e16;
  --success-border:  #166534;
  --danger:          #f87171;
  --danger-strong:   #ef4444;
  --danger-darker:   #ef4444;
  --danger-light:    #2a0e0e;
  --danger-border:   #7f1d1d;
  --amber:           #fbbf24;
  --amber-dark:      #f59e0b;
  --amber-light:     #2a1c0a;
  --amber-tint:      #2a1c0a;
  --amber-tint-warm: #2a1c0a;
  --amber-border:    #92400e;
  --amber-text-warm: #fcd34d;
  --info:            #60a5fa;
  --info-strong:     #3b82f6;
  --info-dark:       #2563eb;
  --info-accent:     #93c5fd;
  --info-light:      #0c1f3d;
  --info-tint:       #0c1f3d;
  --info-border:     #1e40af;
  --accent-violet:   #a78bfa;

  /* Shadows — taller / softer for dark mode (depth comes from elevation, not blur) */
  --shadow-xs:       0 1px 2px rgba(0,0,0,0.45);
  --shadow-sm:       0 1px 3px rgba(0,0,0,0.55);
  --shadow-md:       0 4px 12px rgba(0,0,0,0.55);
  --shadow-lg:       0 16px 40px rgba(0,0,0,0.55);
  --shadow-dialog:   0 24px 60px rgba(0,0,0,0.65);

  /* Sidebar — keep dark surface (Spotify rhythm) */
  --sidebar-bg:      #0a0a0a;
  --sidebar-text:    #ededed;
  --sidebar-muted:   #71717a;
  --sidebar-active-bg:  #1e1b4b;
  --sidebar-active-fg:  #a5b4fc;
  --sidebar-border:  #1c1c1e;

  color-scheme: dark;
}

/* Image / preview tweaks for dark mode */
html[data-theme="dark"] img.row-thumb,
html[data-theme="dark"] .modal-dialog img {
  background: #2a2a2e !important;
}

/* Mobile design # chip: light-mode purple text is invisible on dark surfaces.
   Use a lifted lavender + denser tint so the chip reads cleanly in dark. */
html[data-theme="dark"] .oi-mobile-sub .oi-sub-design {
  color: #c4b5fd;
  background: rgba(167,139,250,.18);
  border-color: rgba(167,139,250,.40);
}
html[data-theme="dark"] .oi-mobile-sub .oi-sub-pill--repeat {
  background: rgba(129,140,248,.20);
  color: #c7d2fe;
  border-color: rgba(129,140,248,.40);
}

/* Order-item detail card — design # badge. Bigger, purple, readable on
   both themes. Mobile: shrinks gracefully via flex-wrap on the parent. */
.oi-design-badge {
  font-family: "JetBrains Mono", ui-monospace, monospace;
  font-size: 1.05rem;
  font-weight: 700;
  letter-spacing: .01em;
  color: #6b21a8;
  background: rgba(168,85,247,.12);
  border: 1px solid rgba(168,85,247,.32);
  padding: 5px 12px;
  border-radius: 7px;
  white-space: nowrap;
}
html[data-theme="dark"] .oi-design-badge {
  color: #c4b5fd;
  background: rgba(167,139,250,.18);
  border-color: rgba(167,139,250,.40);
}
@media (max-width: 480px) {
  .oi-design-badge { font-size: .95rem; padding: 4px 10px; }
}

/* Dark-mode close (×) on the order-item detail modal: light-mode bg
   (--surface #1c1c1e) is darker than the dialog itself (--surface-overlay
   #2a2a2e), and the muted X (--muted #71717a) disappears against it. Lift
   the chip onto a brighter elevation and brighten the glyph. */
html[data-theme="dark"] .oi-detail__close {
  background: var(--line-strong);
  border-color: var(--line-strong);
  color: var(--text);
}
html[data-theme="dark"] .oi-detail__close:hover {
  background: #52525b;
  color: #ffffff;
}

/* ── v2 Sidebar: light theme + right hairline divider ────────────────── */
html[data-ui-version="v2"] .sidebar {
  background: var(--sidebar-bg);
  color: var(--sidebar-text);
  border-right: 1px solid var(--sidebar-border);
}
html[data-ui-version="v2"] .sidebar h1,
html[data-ui-version="v2"] .sidebar .subtitle {
  color: var(--sidebar-text);
}
html[data-ui-version="v2"] .sidebar nav a,
html[data-ui-version="v2"] .sidebar nav button {
  color: var(--sidebar-text);
  background: transparent;
  transition: background 120ms ease, color 120ms ease, box-shadow 120ms ease;
}
/* Hover: soft indigo tint is much more visible on the ivory sidebar than
   surface-sunken, which is almost the same colour as the sidebar bg.
   Deeper text + a 2px left-border indicator make the cursor position
   unambiguous. Active state still wins via the !important rule below. */
html[data-ui-version="v2"] .sidebar nav a:hover,
html[data-ui-version="v2"] .sidebar nav button:hover,
html[data-ui-version="v2"] .sidebar .nav-item:hover:not(.active) {
  background: color-mix(in srgb, var(--primary, #4f46e5) 10%, transparent);
  color: var(--text, #0f0e0c);
  box-shadow: inset 2px 0 0 var(--primary, #4f46e5);
}
html[data-ui-version="v2"] .sidebar nav a.active,
html[data-ui-version="v2"] .sidebar nav button.active,
html[data-ui-version="v2"] .sidebar nav .nav-item.active,
html[data-ui-version="v2"] .sidebar nav .active {
  background: var(--sidebar-active-bg) !important;
  color: var(--sidebar-active-fg) !important;
}

/* ── v2-p2-u1 · Bills list · Spotify pill-chip filters + polish ─────────
   Agent P2-U1 · Workstreams 2 + 6a · scope: bills list only.
   Chip rules are unconditional (chips are a shared primitive that future
   screens will reuse); all visual gating is done in JS via the
   `window.__uiVersion === "v2"` branch inside renderBillsModule. The v1
   tab-bar path is untouched — v1 users see no change.
   Tokens map: --chip-c (state colour) / --chip-soft (soft tint bg).
   ─────────────────────────────────────────────────────────────────────── */
.chip-row {
  display: flex;
  gap: var(--sp-2);
  overflow-x: auto;
  padding: var(--sp-2) 0 var(--sp-4);
  scrollbar-width: thin;
  scroll-snap-type: x proximity;
}
.chip-row::-webkit-scrollbar { height: 4px; }
.chip-row::-webkit-scrollbar-thumb { background: var(--line); border-radius: var(--radius-full); }

/* low-stock alert badge inside fabric chips on the Shades module */
.chip-badge--alert {
  background: var(--danger-soft, #fde2e1) !important;
  color: var(--danger, #c0392b);
  font-weight: 600;
}

/* Wide list tables (Shades, Order Items): allow horizontal scrolling
   when the table is wider than its container instead of squashing columns.
   Adds momentum scroll on iOS, contains overscroll so the horizontal scroll
   doesn't trigger iOS back-swipe gesture (gotcha noted by web research). */
#lst-table-wrap {
  overflow-x: auto;
  overflow-y: hidden;
  -webkit-overflow-scrolling: touch;
  overscroll-behavior-x: contain;
  touch-action: pan-x pan-y;
  position: relative;
}
#lst-table-wrap > table {
  width: auto;
  min-width: 100%;
  border-collapse: separate;  /* required for sticky cells to show their bg */
  border-spacing: 0;
}
#lst-table-wrap > table th { white-space: nowrap; }

/* Sticky first column for wide tables. Opt-in via class on the table-wrap.
   z-index stack: corner (sticky col + thead) wins, then sticky col, then
   thead, then body cells. Sticky cells must have an opaque background.
   Phones just scroll without sticky — keeping the sticky col on a 360px
   viewport ate ~40% of the width. */
@media (min-width: 769px) {
  #lst-table-wrap.sticky-first > table th:first-child,
  #lst-table-wrap.sticky-first > table td:first-child {
    position: sticky;
    left: 0;
    background: var(--surface);
    z-index: 5;
    box-shadow: 1px 0 0 var(--line);  /* subtle right edge */
  }
  #lst-table-wrap.sticky-first > table thead th {
    position: sticky;
    top: 0;
    background: var(--surface-sunken, var(--surface));
    z-index: 4;
  }
  #lst-table-wrap.sticky-first > table thead th:first-child { z-index: 6; }
  /* Right-edge fade hint that the table scrolls horizontally. */
  #lst-table-wrap.sticky-first::after {
    content: "";
    position: sticky;
    display: block;
    right: 0;
    top: 0;
    width: 24px;
    height: 100%;
    pointer-events: none;
    background: linear-gradient(to right, transparent, rgba(0,0,0,0.04));
  }
}

/* Skeleton loading bars (replaces "Loading…" text loaders). */
.skeleton-bar {
  background: linear-gradient(90deg,
    var(--surface-sunken) 0%,
    var(--surface-muted) 50%,
    var(--surface-sunken) 100%);
  background-size: 200% 100%;
  border-radius: 4px;
  animation: skeleton-shimmer 1.4s ease-in-out infinite;
  opacity: .6;
}
@keyframes skeleton-shimmer {
  0%   { background-position: 100% 0; }
  100% { background-position: -100% 0; }
}

/* Empty state (replaces inline "No X found" divs). */
.empty-state {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  padding: 80px 24px;
  color: var(--muted);
  animation: row-fade-in 240ms cubic-bezier(0.2, 0, 0, 1);
}
.empty-state__icon {
  width: 72px; height: 72px;
  display: flex; align-items: center; justify-content: center;
  border-radius: 50%;
  background: var(--surface-sunken);
  color: var(--muted);
  margin-bottom: 16px;
  position: relative;
}
.empty-state__icon::before {
  content: "";
  position: absolute;
  inset: -10px;
  border-radius: 50%;
  border: 1px dashed var(--line);
}
.empty-state__title {
  font-family: "Instrument Serif", Georgia, serif;
  font-size: 1.6rem;
  color: var(--text-secondary);
  margin-bottom: 6px;
}
.empty-state__hint {
  font-size: .9rem;
  color: var(--muted);
  max-width: 420px;
  line-height: 1.5;
}

/* Toast stack (was missing — caused alert() fallback in app.js).
   Bottom-left desktop, above bottom-nav on mobile. */
.toast-stack {
  position: fixed;
  left: 16px;
  bottom: 16px;
  /* Reset the translateX(-50%) inherited from the earlier centered-bottom
     rule (line ~2197). Without this, the toast renders at left:16px but
     shifted -50% of its width → most of it lands off-screen on desktop. */
  transform: none;
  display: flex;
  flex-direction: column;
  gap: 8px;
  z-index: 10000;
  pointer-events: none;
  max-width: min(420px, calc(100vw - 32px));
}
.toast-item {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 12px 16px;
  background: var(--surface-popover);
  color: var(--text);
  border: 1px solid var(--line);
  border-radius: 10px;
  box-shadow: var(--shadow-md);
  font-size: .9rem;
  pointer-events: auto;
  animation: toast-in .18s ease-out;
  transition: opacity .18s ease;
}
/* These typed-variant rules win over the earlier .toast-item.error rule
   (line ~2220) that set background: var(--danger). Explicitly reset
   background+color to prevent "red text on red background = invisible"
   that left the user staring at a red box with no readable message. */
.toast-item.success { border-left: 4px solid var(--success); background: var(--surface-popover); color: var(--text); }
.toast-item.error   { border-left: 4px solid var(--danger);  background: var(--surface-popover); color: var(--danger); font-weight: 600; }
.toast-item.warning { border-left: 4px solid var(--amber);   background: var(--surface-popover); color: var(--text); }
.toast-item.info    { border-left: 4px solid var(--info);    background: var(--surface-popover); color: var(--text); }
.toast-item.fade-out { opacity: 0; }
@keyframes toast-in { from { transform: translateY(8px); opacity: 0; } to { transform: none; opacity: 1; } }
@media (max-width: 768px) {
  .toast-stack {
    left: 12px; right: 12px;
    bottom: calc(16px + env(safe-area-inset-bottom));
    max-width: none;
  }
}

/* Theme toggle in topbar */
#theme-toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
#theme-toggle .icon { display: inline-flex; }

.chip {
  --chip-c: var(--text-secondary);
  --chip-soft: var(--surface-sunken);
  flex-shrink: 0;
  padding: 6px 14px;
  border-radius: var(--radius-full);
  background: var(--chip-soft);
  color: var(--chip-c);
  border: 1px solid transparent;
  font: inherit;
  font-size: var(--text-sm, 13px);
  font-weight: 500;
  cursor: pointer;
  transition: background var(--dur-fast) var(--ease-out),
              color var(--dur-fast) var(--ease-out),
              box-shadow var(--dur-base) var(--ease-out),
              transform var(--dur-fast) var(--ease-out),
              border-color var(--dur-fast) var(--ease-out);
  scroll-snap-align: start;
  white-space: nowrap;
  line-height: 1.3;
}
.chip:hover {
  background: color-mix(in srgb, var(--chip-c) 12%, var(--surface));
  border-color: color-mix(in srgb, var(--chip-c) 28%, transparent);
  color: var(--chip-c);
}
.chip:active { transform: scale(0.97); }
.chip.active {
  background: var(--chip-c);
  color: #ffffff;
  border-color: var(--chip-c);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--chip-c) 22%, transparent);
}
.chip.active:hover {
  background: color-mix(in srgb, var(--chip-c) 88%, #000);
  color: #fff;
}
.chip__count { margin-left: 6px; opacity: 0.75; font-variant-numeric: tabular-nums; }

/* Semantic variants — each chip carries its state colour. */
.chip--all      { --chip-c: var(--text);            --chip-soft: var(--surface-sunken); }
.chip--success  { --chip-c: var(--success);         --chip-soft: var(--success-light); }
.chip--primary  { --chip-c: var(--primary);         --chip-soft: var(--primary-light); }
.chip--amber    { --chip-c: var(--amber);           --chip-soft: var(--amber-light); }
.chip--danger   { --chip-c: var(--danger);          --chip-soft: var(--danger-light); }
.chip--muted    { --chip-c: var(--text-secondary);  --chip-soft: var(--surface-sunken); }

/* Bills-table row dim for Lost/Damaged/Cancelled bills. A sibling rule at
   line ~1959 already handles `.detail-table tr.row--dimmed`; this one
   covers the main .table-wrap tables used in renderBillsModule. */
html[data-ui-version="v2"] .row--dimmed { opacity: 0.72; }
html[data-ui-version="v2"] .row--dimmed:hover { opacity: 0.92; }

@media (prefers-reduced-motion: reduce) {
  .chip, .chip:active { transition: none; transform: none; }
}

/* ───────────────────────────────────────────────────────────────────────
   v2-p2-u2 · Program Detail polish (Workstream 3)
   Appended 2026-04-24. Additive only — scoped to [data-ui-version="v2"].
   ─────────────────────────────────────────────────────────────────────── */

/* 1. Program number promotion — display heading style. */
html[data-ui-version="v2"] .detail-title {
  font-size: var(--text-2xl, 28px);
  font-weight: 600;
  letter-spacing: -0.02em;
}

/* 2. Section-card hover elevation. */
html[data-ui-version="v2"] .section-card {
  transition: box-shadow var(--dur-base) var(--ease-out),
              transform var(--dur-base) var(--ease-out);
}
html[data-ui-version="v2"] .section-card:hover {
  box-shadow: var(--shadow-sm);
}

/* 3. Meta-grid item hover hairline. */
html[data-ui-version="v2"] .detail-meta-item {
  border-bottom: 1px solid transparent;
  transition: border-color var(--transition);
}
html[data-ui-version="v2"] .detail-meta-item:hover {
  border-bottom-color: var(--line);
}

/* 4. Lost-bill row dim in the inner bills table. */
html[data-ui-version="v2"] .detail-table tbody tr.row--dimmed {
  opacity: 0.72;
  background: var(--surface-sunken, #fafafa);
}
html[data-ui-version="v2"] .detail-table tbody tr.row--dimmed:hover {
  opacity: 0.92;
}

/* 5. Back-button: SVG + label alignment. */
html[data-ui-version="v2"] .btn-back {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2, 6px);
}
html[data-ui-version="v2"] .btn-back svg {
  flex: 0 0 auto;
}
/* v19.6: suppress the Unicode ← pseudo — in v2 the Lucide SVG is the only
   arrow (the pseudo-element was doubling the glyph). */
html[data-ui-version="v2"] .btn-back::before {
  content: none;
}

/* v19.6 · in-detail progress strip — replaces the retired bottom context bar.
   Two-row layout: uppercase label + inline value on top, 4px rounded track
   below. Sits inside the detail page's header area so the completion number
   is adjacent to the stats it summarises. */
.detail-progress-strip {
  display: flex;
  flex-direction: column;
  gap: 6px;
  margin: var(--sp-3, 10px) 0 var(--sp-4, 14px);
  padding: 0;
}
.detail-progress-strip__row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--sp-4, 14px);
  font-size: var(--text-xs, 0.72rem);
}
.detail-progress-strip__label {
  text-transform: uppercase;
  letter-spacing: 0.06em;
  font-weight: 600;
  color: var(--muted, #78716c);
}
.detail-progress-strip__value {
  color: var(--text, #0f0e0c);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}
.detail-progress-strip__track {
  position: relative;
  height: 4px;
  background: var(--surface-sunken, #f4f2ed);
  border-radius: 999px;
  overflow: hidden;
}
.detail-progress-strip__fill {
  position: absolute;
  inset: 0 auto 0 0;
  height: 100%;
  background: var(--primary, #4f46e5);
  border-radius: 999px;
  transition: width 320ms cubic-bezier(0.22, 1, 0.36, 1);
}
.detail-progress-strip[data-variant="success"] .detail-progress-strip__fill { background: var(--success, #16a34a); }
.detail-progress-strip[data-variant="danger"]  .detail-progress-strip__fill { background: var(--danger, #dc2626); }
.detail-progress-strip[data-variant="idle"]    .detail-progress-strip__fill { background: var(--line-strong, #d9d3c7); }

/* v20.0 · Recently viewed + Pinned rails (Spotify-inspired). Per-user,
   rendered on Dashboard above "Programs in progress". Chips scroll
   horizontally on overflow; modular accent colour strips the left edge. */
.personal-rail {
  margin: var(--sp-4, 16px) 0 var(--sp-5, 20px);
}
.personal-rail__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-3, 12px);
  margin-bottom: 10px;
}
.personal-rail__eyebrow {
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--muted, #78716c);
}
.personal-rail__action {
  font-size: 12px;
  font-weight: 500;
  color: var(--muted, #78716c);
  background: transparent;
  border: 0;
  cursor: pointer;
  padding: 4px 8px;
  border-radius: 6px;
  transition: color 120ms ease, background 120ms ease;
}
.personal-rail__action:hover {
  color: var(--text, #0f0e0c);
  background: var(--surface-sunken, #f4f2ed);
}
.personal-rail__count {
  font-family: var(--font-mono, ui-monospace, SFMono-Regular, monospace);
  font-size: 11px;
  font-weight: 600;
  color: var(--muted, #78716c);
  padding: 2px 8px;
  background: var(--surface-sunken, #f4f2ed);
  border-radius: 999px;
}
.personal-rail__row {
  display: flex;
  gap: 10px;
  overflow-x: auto;
  scroll-snap-type: x proximity;
  scrollbar-width: none;
  -ms-overflow-style: none;
  padding: 2px 2px 6px;
}
.personal-rail__row::-webkit-scrollbar { display: none; }

.personal-chip {
  flex: 0 0 auto;
  scroll-snap-align: start;
  display: flex;
  flex-direction: column;
  gap: 3px;
  min-width: 170px;
  max-width: 240px;
  padding: 10px 14px 10px 16px;
  background: var(--surface-raised, #ffffff);
  border: 1px solid var(--line, #ebe7df);
  border-left: 3px solid var(--line-strong, #d9d3c7);
  border-radius: var(--radius-lg, 12px);
  box-shadow: var(--shadow-xs, 0 1px 2px rgba(15,14,12,0.04));
  cursor: pointer;
  text-align: left;
  font-family: inherit;
  color: var(--text, #0f0e0c);
  transition: background 120ms ease, border-color 120ms ease, transform 100ms ease, box-shadow 120ms ease;
}
.personal-chip:hover {
  background: var(--surface-sunken, #f4f2ed);
  border-color: var(--line-strong, #d9d3c7);
  box-shadow: var(--shadow-sm, 0 2px 6px rgba(15,14,12,0.06));
}
.personal-chip:active { transform: scale(0.97); }
.personal-chip:focus-visible {
  outline: none;
  box-shadow: 0 0 0 3px var(--primary-glow, rgba(79,70,229,0.18));
}
.personal-chip--primary { border-left-color: var(--primary, #4f46e5); }
.personal-chip--success { border-left-color: var(--success, #16a34a); }
.personal-chip--amber   { border-left-color: var(--amber, #d97706); }
.personal-chip--danger  { border-left-color: var(--danger, #dc2626); }

.personal-chip__title {
  font-size: 13px;
  font-weight: 600;
  line-height: 1.3;
  color: var(--text, #0f0e0c);
  display: inline-flex;
  align-items: baseline;
  gap: 4px;
}
.personal-chip__id {
  font-family: var(--font-mono, ui-monospace, SFMono-Regular, monospace);
  font-weight: 600;
  letter-spacing: 0.01em;
}
.personal-chip__star {
  color: var(--amber, #d97706);
  font-size: 12px;
  line-height: 1;
}
.personal-chip__sub {
  font-size: 11px;
  color: var(--text-tertiary, #8c8880);
  letter-spacing: 0.01em;
  line-height: 1.3;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Pin toggle button inside detail topbars. 32×32 ghost button matching
   the .btn-back visual weight. */
.btn-pin {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 34px;
  height: 34px;
  margin-left: 6px;
  padding: 0;
  background: transparent;
  border: 1px solid transparent;
  border-radius: var(--radius, 8px);
  color: var(--text-tertiary, #8c8880);
  cursor: pointer;
  font-family: inherit;
  transition: background 120ms ease, color 120ms ease, border-color 120ms ease, transform 150ms ease;
}
.btn-pin:hover {
  background: var(--surface-sunken, #f4f2ed);
  color: var(--amber, #d97706);
  border-color: var(--line, #ebe7df);
}
.btn-pin:focus-visible {
  outline: none;
  box-shadow: 0 0 0 3px var(--primary-glow, rgba(79,70,229,0.18));
}
.btn-pin.is-pinned {
  color: var(--amber, #d97706);
  background: color-mix(in srgb, var(--amber, #d97706) 10%, transparent);
  border-color: color-mix(in srgb, var(--amber, #d97706) 22%, transparent);
}
.btn-pin.is-pinned:hover {
  background: color-mix(in srgb, var(--amber, #d97706) 16%, transparent);
}
.btn-pin:active { transform: scale(0.94); }

@media (prefers-reduced-motion: reduce) {
  .personal-chip:active { transform: none; }
  .btn-pin:active { transform: none; }
  .personal-chip, .btn-pin { transition: none; }
}

/* v20.1 · Saved views (Spotify "playlists"). Per-user rail above the
   filter chip-row on list modules. Idle chips use --primary-glow bg +
   --primary text; active chips filled --primary with white text. */
.saved-views-rail {
  margin: 0 0 12px;
}
.saved-views-rail__eyebrow {
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--muted, #78716c);
  margin-bottom: 6px;
}
.saved-views-rail__row {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  align-items: center;
}
.saved-view-chip {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 5px 12px;
  border-radius: var(--radius-full, 9999px);
  border: 1px solid transparent;
  background: var(--primary-glow, rgba(79,70,229,0.15));
  color: var(--primary, #4f46e5);
  font-size: 12px;
  font-weight: 600;
  line-height: 1.25;
  cursor: pointer;
  font-family: inherit;
  max-width: 220px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  transition: background 120ms ease, color 120ms ease, transform 100ms ease, box-shadow 120ms ease;
}
.saved-view-chip:hover {
  background: color-mix(in srgb, var(--primary, #4f46e5) 22%, transparent);
}
.saved-view-chip:focus-visible {
  outline: none;
  box-shadow: 0 0 0 3px var(--primary-glow, rgba(79,70,229,0.18));
}
.saved-view-chip:active { transform: scale(0.97); }
.saved-view-chip.active {
  background: var(--primary, #4f46e5);
  color: #ffffff;
}
.saved-view-chip.active:hover {
  background: color-mix(in srgb, var(--primary, #4f46e5) 88%, #000);
}

/* "+ Save view…" button lives at the tail of the filter chip-row. Ghost
   idle → primary on hover. Only rendered when filters are non-default. */
.saved-view-add-btn {
  margin-left: auto;
  padding: 5px 10px;
  border-radius: var(--radius-full, 9999px);
  border: 1px dashed var(--line, #ebe7df);
  background: transparent;
  color: var(--text-secondary, #78716c);
  font-size: 12px;
  font-weight: 500;
  line-height: 1.25;
  cursor: pointer;
  font-family: inherit;
  white-space: nowrap;
  transition: color 120ms ease, border-color 120ms ease, background 120ms ease;
}
.saved-view-add-btn:hover {
  color: var(--primary, #4f46e5);
  border-color: var(--primary, #4f46e5);
  background: var(--primary-glow, rgba(79,70,229,0.12));
}
.saved-view-add-btn:focus-visible {
  outline: none;
  box-shadow: 0 0 0 3px var(--primary-glow, rgba(79,70,229,0.18));
}

@media (prefers-reduced-motion: reduce) {
  .saved-view-chip:active { transform: none; }
  .saved-view-chip, .saved-view-add-btn { transition: none; }
}

/* v19.6 · "Needs attention" dashboard list (replaces gradient card rail).
   Compact list rows: label + inline badge · description · action chip.
   Matches ui-mockup.html Section 02. */
.dash-attention-card {
  background: var(--surface-raised, #ffffff);
  border: 1px solid var(--line, #ebe7df);
  border-radius: 14px;
  margin-top: var(--sp-6, 24px);
  overflow: hidden;
  box-shadow: var(--shadow-xs, 0 1px 2px rgba(15,14,12,0.04));
}
.dash-attention-card__head {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 16px 20px;
  border-bottom: 1px solid var(--line, #ebe7df);
}
.dash-attention-card__icon {
  width: 32px;
  height: 32px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: color-mix(in srgb, var(--amber, #d97706) 12%, transparent);
  color: var(--amber, #d97706);
  border-radius: 8px;
  flex: 0 0 auto;
}
.dash-attention-card__title {
  flex: 1;
  margin: 0;
  font-size: 15px;
  font-weight: 600;
  color: var(--text, #0f0e0c);
  letter-spacing: -0.01em;
}
.dash-attention-card__count {
  font-size: 11px;
  font-weight: 600;
  color: var(--muted, #78716c);
  text-transform: uppercase;
  letter-spacing: 0.08em;
}
.dash-attention-list {
  list-style: none;
  margin: 0;
  padding: 0;
}
.dash-attention-row {
  display: grid;
  grid-template-columns: minmax(240px, 1fr) minmax(200px, 1.4fr) auto;
  align-items: center;
  gap: 16px;
  padding: 14px 20px;
  border-top: 1px solid var(--line, #ebe7df);
  cursor: pointer;
  transition: background 120ms ease;
}
.dash-attention-row:first-child { border-top: none; }
.dash-attention-row:hover,
.dash-attention-row:focus-visible {
  background: var(--surface-sunken, #f4f2ed);
  outline: none;
}
.dash-attention-row__label {
  font-size: 14px;
  color: var(--text, #0f0e0c);
  line-height: 1.4;
}
.dash-attention-row__label strong {
  font-weight: 600;
  font-family: var(--font-mono, ui-monospace, SFMono-Regular, monospace);
  letter-spacing: 0.01em;
}
.dash-attention-row__badge {
  display: inline-flex;
  align-items: center;
  padding: 2px 8px;
  border-radius: 999px;
  font-size: 11px;
  font-weight: 600;
  background: var(--surface-sunken, #f4f2ed);
  color: var(--text-secondary, #4b4843);
  vertical-align: middle;
  margin-left: 2px;
}
.dash-attention-row__badge.danger {
  background: color-mix(in srgb, var(--danger, #dc2626) 10%, transparent);
  color: var(--danger, #dc2626);
}
.dash-attention-row__badge.amber {
  background: color-mix(in srgb, var(--amber, #d97706) 12%, transparent);
  color: var(--amber-dark, #b45309);
}
.dash-attention-row__badge.primary {
  background: color-mix(in srgb, var(--primary, #4f46e5) 10%, transparent);
  color: var(--primary, #4f46e5);
}
.dash-attention-row__desc {
  font-size: 13px;
  color: var(--text-secondary, #4b4843);
  line-height: 1.4;
}
.dash-attention-row__action {
  font-size: 13px;
  font-weight: 500;
  color: var(--primary, #4f46e5);
  white-space: nowrap;
  padding: 6px 12px;
  border-radius: 8px;
  background: transparent;
  transition: background 120ms ease;
}
.dash-attention-row:hover .dash-attention-row__action {
  background: color-mix(in srgb, var(--primary, #4f46e5) 8%, transparent);
}
.dash-attention-empty {
  padding: 32px 20px;
  text-align: center;
  color: var(--muted, #78716c);
  font-size: 13px;
}
@media (max-width: 768px) {
  .dash-attention-row {
    grid-template-columns: 1fr;
    gap: 6px;
    padding: 14px 16px;
  }
  .dash-attention-row__action { justify-self: start; padding-left: 0; padding-right: 0; }
}

/* 6. First-paint skeleton layout. */
html[data-ui-version="v2"] .program-detail--skeleton .detail-meta-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
  gap: var(--sp-3, 12px);
  margin-bottom: var(--sp-4, 16px);
}
html[data-ui-version="v2"] .program-detail--skeleton .detail-meta-item {
  display: flex;
  flex-direction: column;
  gap: 6px;
}
/* ═══ v2-p2-u3 · Lot Report polish (Agent P2-U3) ═══════════════════════
   Hover-elevation on per-lot rows, icon-prefixed PDF button.
   Shade-progress-fill rules already exist at ~:2179 (shared with v1's
   expanded-row bar) — this block only wires the new surfaces. */

/* Per-lot row hover elevation. The lot rows render as <tr>; they get the
   .lot-rpt-lot-card class in v2. box-shadow+translate on <tr> works in
   modern engines and gracefully no-ops elsewhere. */
html[data-ui-version="v2"] .lot-rpt-lot-card {
  transition: box-shadow var(--dur-base) var(--ease-out),
              transform var(--dur-base) var(--ease-out);
}
html[data-ui-version="v2"] .lot-rpt-lot-card:hover {
  box-shadow: var(--shadow-sm);
  transform: translateY(-1px);
}

/* Tighten the inline PDF-button icon inside the filter bar. */
html[data-ui-version="v2"] .lot-rpt-pdf-btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
}
html[data-ui-version="v2"] .lot-rpt-pdf-btn svg {
  flex-shrink: 0;
}

@media (prefers-reduced-motion: reduce) {
  html[data-ui-version="v2"] .lot-rpt-lot-card,
  html[data-ui-version="v2"] .lot-rpt-lot-card:hover {
    transition: none !important;
    transform: none !important;
  }
}

/* ── v2-p2-u6: search "did you mean?" dropdown (Workstream 5b) ──────────
   Scoped under html[data-ui-version="v2"]: v1 never sees these rules, and
   the dropdown DOM node is only inserted by app.js when v2 is active. */
html[data-ui-version="v2"] .search-suggest {
  position: absolute;
  top: calc(100% + 4px);
  left: 0;
  min-width: 220px;
  max-width: 320px;
  background: var(--surface-popover, #fff);
  border: 1px solid var(--line);
  border-radius: 10px;
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12);
  padding: 6px;
  z-index: 60;
  display: none;
  font-size: 0.85rem;
}
html[data-ui-version="v2"] .search-suggest__header {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 6px 8px 4px 8px;
  font-size: 0.72rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--text-secondary, #6b7280);
}
html[data-ui-version="v2"] .search-suggest__item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  width: 100%;
  padding: 8px 10px;
  background: transparent;
  border: none;
  border-radius: 6px;
  cursor: pointer;
  text-align: left;
  font: inherit;
  color: var(--text, #111827);
}
html[data-ui-version="v2"] .search-suggest__item:hover,
html[data-ui-version="v2"] .search-suggest__item:focus-visible {
  background: var(--surface-sunken, #f3f4f6);
  outline: none;
}
html[data-ui-version="v2"] .search-suggest__token {
  font-weight: 500;
}
html[data-ui-version="v2"] .search-suggest__pct {
  font-size: 0.72rem;
  font-weight: 600;
  color: var(--success, #16a34a);
  background: #f0fdf4;
  padding: 2px 6px;
  border-radius: 99px;
}

/* ── v2-p2-u5c · Mobile v2 polish ─────────────────────────────────────
   Agent P2-U5c · Workstream 5c (narrowed scope).
   Appended 2026-04-24. Additive only, CSS-only, scoped to v2 + mobile.
   Covers: (1) bigger mobile type, (2) module colour-accent tokens +
   bottom-nav active tint, (3) larger badges, (4) FAB hide-on-focus
   via :has() (pure CSS, no JS — Safari 15.4+, Chrome 105+, FF 121+).
   Deferred items (sticky group headers, title-tap search, swipe
   polish, section chrome) are NOT included — see PLAN §5c.
   ──────────────────────────────────────────────────────────────────── */
@media (max-width: 768px) {
  /* (1) Bigger type on mobile — section/title/table/body scale */
  html[data-ui-version="v2"] body {
    font-size: 16px;
    line-height: 1.55;
  }
  html[data-ui-version="v2"] h1,
  html[data-ui-version="v2"] h2,
  html[data-ui-version="v2"] h3,
  html[data-ui-version="v2"] .section-title {
    font-size: 19px;
    letter-spacing: -0.01em;
    line-height: 1.3;
  }
  html[data-ui-version="v2"] table th,
  html[data-ui-version="v2"] table td {
    font-size: 15px;
  }

  /* (2) Module colour-accent tokens — one per top-level module.
     Body-scoped so every descendant picks them up. Bottom-nav
     `.active` tab tints itself with the matching token. */
  html[data-ui-version="v2"] body {
    --c-module-programs: var(--primary);
    --c-module-bills: #8b5cf6;
    --c-module-lots: var(--success);
    --c-module-receiving: var(--amber);
    --c-module-invoices: var(--muted);
  }
  html[data-ui-version="v2"] .bottom-nav button[data-mod="programs"].active {
    color: var(--c-module-programs);
  }
  html[data-ui-version="v2"] .bottom-nav button[data-mod="bills"].active {
    color: var(--c-module-bills);
  }
  html[data-ui-version="v2"] .bottom-nav button[data-mod="lot_report"].active {
    color: var(--c-module-lots);
  }
  html[data-ui-version="v2"] .bottom-nav button[data-mod="receiving"].active {
    color: var(--c-module-receiving);
  }
  html[data-ui-version="v2"] .bottom-nav button[data-mod="invoices"].active {
    color: var(--c-module-invoices);
  }

  /* (3) Larger badges on mobile — readable at arm's length. */
  html[data-ui-version="v2"] .badge {
    font-size: 13px;
    padding: 4px 10px;
  }

  /* (4) FAB hide-on-focus — prevents "+ Ad" truncation when the
     keyboard pushes the FAB over form inputs. Pure CSS via :has(). */
  html[data-ui-version="v2"] body:has(input:focus, textarea:focus, select:focus) .fab {
    opacity: 0;
    pointer-events: none;
    transition: opacity var(--dur-fast) var(--ease-out);
  }
}

/* ═══════════════════════════════════════════════════════════════════════
   v2 · Airtable-style slide-in detail drawer (Workstream 6b)
   Mounted via window.openDetailDrawer() — see app.js installDetailDrawer.
   [data-mode] variants: "drawer" (520px) · "expanded" (~92%) · "full".
   Mobile ≤768px becomes a bottom-sheet.
   ═══════════════════════════════════════════════════════════════════════ */
.drawer-host {
  position: fixed;
  inset: 0;
  z-index: 9998;
  pointer-events: none;
}
.drawer-scrim {
  position: absolute;
  inset: 0;
  background: rgba(15, 14, 12, 0.18);
  backdrop-filter: blur(2px);
  -webkit-backdrop-filter: blur(2px);
  pointer-events: auto;
  animation: drawer-scrim-in var(--dur-base) var(--ease-out);
}
@keyframes drawer-scrim-in { from { opacity: 0 } to { opacity: 1 } }

.drawer-panel {
  position: absolute;
  top: 0; right: 0; bottom: 0;
  width: min(520px, 100vw);
  background: var(--surface, #fff);
  border-left: 1px solid var(--line, #e5e1d8);
  box-shadow: -20px 0 60px rgba(15, 14, 12, 0.16);
  display: flex;
  flex-direction: column;
  pointer-events: auto;
  animation: drawer-slide-in var(--dur-slow) var(--ease-out);
  overflow: hidden;
}
/* v2-motion-polish: subtle "settle" micro-overshoot — sheet slides in,
   travels 4px past target, then rests. No scale overshoot (keeps border
   crisp against the scrim). */
@keyframes drawer-slide-in {
  0%   { transform: translateX(100%); }
  80%  { transform: translateX(-4px); }
  100% { transform: translateX(0); }
}

/* Mode 2 — expanded: near-fullscreen with 16px margins. */
.drawer-host[data-mode="expanded"] .drawer-scrim {
  background: rgba(15, 14, 12, 0.32);
  backdrop-filter: blur(4px);
  -webkit-backdrop-filter: blur(4px);
}
.drawer-host[data-mode="expanded"] .drawer-panel {
  top: var(--sp-4, 16px);
  right: var(--sp-4, 16px);
  bottom: var(--sp-4, 16px);
  left: var(--sp-4, 16px);
  width: auto;
  border-radius: 14px;
  border-left: none;
  box-shadow: 0 24px 60px rgba(15, 14, 12, 0.22);
  transition: top var(--dur-slow) var(--ease-out),
              right var(--dur-slow) var(--ease-out),
              left var(--dur-slow) var(--ease-out),
              bottom var(--dur-slow) var(--ease-out),
              border-radius var(--dur-slow) var(--ease-out);
}

/* Mode 3 — full page: panel covers the viewport. */
.drawer-host[data-mode="full"] .drawer-scrim {
  background: var(--surface, #fff);
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
}
.drawer-host[data-mode="full"] .drawer-panel {
  inset: 0;
  width: 100vw;
  border-radius: 0;
  border-left: none;
  box-shadow: none;
}

/* Header */
.drawer-panel__head {
  display: flex;
  align-items: center;
  gap: var(--sp-2, 8px);
  padding: var(--sp-3, 12px) var(--sp-4, 16px);
  border-bottom: 1px solid var(--line, #e5e1d8);
  background: var(--surface, #fff);
  flex-shrink: 0;
}
.drawer-panel__title {
  flex: 1;
  font-weight: 600;
  font-size: var(--text-md, 15px);
  letter-spacing: -0.01em;
  color: var(--text, #0f0e0c);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.drawer-panel__icon-btn {
  width: 32px; height: 32px;
  flex-shrink: 0;
  display: inline-grid;
  place-items: center;
  border-radius: var(--radius, 8px);
  border: 1px solid transparent;
  background: transparent;
  color: var(--text-secondary, #4b4843);
  cursor: pointer;
  transition: background var(--dur-fast) var(--ease-out),
              color var(--dur-fast) var(--ease-out),
              border-color var(--dur-fast) var(--ease-out),
              transform var(--dur-fast) var(--ease-out);
}
.drawer-panel__icon-btn:hover {
  background: var(--surface-sunken, #f5f3ef);
  color: var(--text, #0f0e0c);
  border-color: var(--line, #e5e1d8);
}
.drawer-panel__icon-btn:focus-visible {
  outline: 2px solid var(--primary, #4f46e5);
  outline-offset: 2px;
}

.drawer-panel__body {
  flex: 1;
  overflow-y: auto;
  overflow-x: hidden;
  padding: var(--sp-3, 12px) var(--sp-4, 16px);
}
/* Back-button inside the detail body is redundant with header ← button. */
.drawer-panel__body .detail-topbar .btn-back { display: none; }

@media (max-width: 768px) {
  .drawer-panel {
    top: auto;
    left: 0;
    right: 0;
    bottom: 0;
    width: 100vw;
    height: 92dvh;
    max-height: 92dvh;
    border-left: none;
    border-top: 1px solid var(--line, #e5e1d8);
    border-radius: 20px 20px 0 0;
    box-shadow: 0 -12px 40px rgba(15, 14, 12, 0.18);
    animation: drawer-sheet-up var(--dur-drawer) var(--ease-out);
  }
  @keyframes drawer-sheet-up { from { transform: translateY(100%) } to { transform: none } }
  .drawer-panel__body { padding-bottom: calc(var(--sp-6, 24px) + env(safe-area-inset-bottom, 0)); }
  .drawer-host[data-mode="full"] .drawer-panel {
    height: 100dvh;
    max-height: 100dvh;
    border-radius: 0;
  }
}

@media (prefers-reduced-motion: reduce) {
  .drawer-scrim, .drawer-panel {
    animation: none !important;
    transition: none !important;
  }
}

/* ── v2-p2-w5c-rest · Mobile v2 Bills chrome + grouped list ───────────
   Agent P2-W5c-rest · Workstream 5c (deferred-items ship).
   Appended 2026-04-24. Additive only, scoped to v2 + mobile (≤768 px).
   Covers: (1) sticky grouped-section headers on Bills list,
   (2) restructured section chrome (title + count chip + search/expand
   icons), (3) "Expand all" repositioned to icon button on module title.
   Desktop (>768px) and mobile v1 see ZERO changes — every selector is
   gated by html[data-ui-version="v2"] + @media (max-width: 768px).
   ─────────────────────────────────────────────────────────────────── */
@media (max-width: 768px) {
  /* (1) Sticky group headers for Bills list — grouping rows emitted
     by renderBillsModule when mobile v2 is active. Rendered as sticky
     blocks (since the mobile card treatment makes tbody/tr `display:
     block`), with padding + uppercase typography. The single TD
     (colSpan=all) carries the visible content; all other tds are
     hidden via the table-row mobile rules above. */
  html[data-ui-version="v2"] #module-workspace table tbody tr.mobile-group-header {
    display: block;
    position: sticky;
    top: 0;
    z-index: 2;
    margin: 10px 0 6px;
    padding: 0;
    background: var(--surface-sunken);
    border: 0;
    border-bottom: 1px solid var(--line);
    border-radius: 6px 6px 0 0;
    box-shadow: none;
    cursor: default;
  }
  html[data-ui-version="v2"] #module-workspace table tbody tr.mobile-group-header:first-child {
    margin-top: 0;
  }
  html[data-ui-version="v2"] #module-workspace table tbody tr.mobile-group-header:active {
    transform: none;
  }
  html[data-ui-version="v2"] #module-workspace table tbody tr.mobile-group-header::after,
  html[data-ui-version="v2"] #module-workspace table tbody tr.mobile-group-header::before {
    content: none !important;
  }
  html[data-ui-version="v2"] #module-workspace table tbody tr.mobile-group-header > td {
    display: block;
    padding: 8px 12px;
    border: 0;
    font-weight: 600;
    font-size: 13px;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--text-secondary);
    white-space: normal;
  }
  /* Suppress mobile card label pseudo-element on header td */
  html[data-ui-version="v2"] #module-workspace table tbody tr.mobile-group-header > td::before,
  html[data-ui-version="v2"] #module-workspace table tbody tr.mobile-group-header > td::after {
    content: none !important;
  }
  html[data-ui-version="v2"] #module-workspace table tbody tr.mobile-group-header .group-count {
    display: inline-block;
    margin-left: 8px;
    color: var(--muted);
    font-weight: 500;
    letter-spacing: 0;
    text-transform: none;
    font-size: 12px;
  }

  /* (2) Section chrome — large Bills title, inline count, search +
     expand-all icon buttons. Emitted only when renderBillsModule is
     in list mode on mobile v2. */
  html[data-ui-version="v2"] .bills-mobile-chrome {
    display: flex;
    align-items: center;
    gap: var(--sp-2);
    padding: 4px 2px 10px;
    margin-bottom: 4px;
    border-bottom: 1px solid var(--line);
  }
  html[data-ui-version="v2"] .bills-mobile-chrome .chrome-title {
    font-size: 22px;
    font-weight: 600;
    letter-spacing: -0.01em;
    line-height: 1.2;
    color: var(--text, #0f0e0c);
    margin: 0;
  }
  html[data-ui-version="v2"] .bills-mobile-chrome .chrome-count {
    font-size: 14px;
    color: var(--muted);
    font-weight: 400;
    margin-left: 2px;
  }
  html[data-ui-version="v2"] .bills-mobile-chrome .chrome-spacer {
    flex: 1;
  }
  html[data-ui-version="v2"] .bills-mobile-chrome .chrome-btn {
    width: 36px;
    height: 36px;
    display: inline-grid;
    place-items: center;
    border-radius: 10px;
    border: 1px solid transparent;
    background: transparent;
    color: var(--text-secondary);
    cursor: pointer;
    transition: background var(--dur-fast) var(--ease-out),
                border-color var(--dur-fast) var(--ease-out),
                color var(--dur-fast) var(--ease-out),
                transform var(--dur-fast) var(--ease-out);
  }
  html[data-ui-version="v2"] .bills-mobile-chrome .chrome-btn:active {
    transform: scale(0.94);
  }
  html[data-ui-version="v2"] .bills-mobile-chrome .chrome-btn:hover,
  html[data-ui-version="v2"] .bills-mobile-chrome .chrome-btn:focus-visible {
    background: var(--surface-sunken);
    border-color: var(--line);
    color: var(--text, #0f0e0c);
  }
  html[data-ui-version="v2"] .bills-mobile-chrome .chrome-btn[aria-pressed="true"] {
    background: var(--surface-sunken);
    border-color: var(--line);
    color: var(--primary);
  }
  /* Inline search field that appears when the search icon is tapped.
     [data-expanded="1"] toggles visibility from the JS handler. */
  html[data-ui-version="v2"] .bills-mobile-chrome .chrome-search {
    display: none;
    flex: 1;
    min-width: 0;
    height: 36px;
    padding: 0 12px;
    border-radius: 10px;
    border: 1px solid var(--line);
    background: var(--surface-sunken);
    font-size: 16px;
    color: var(--text, #0f0e0c);
  }
  html[data-ui-version="v2"] .bills-mobile-chrome[data-expanded="1"] .chrome-title,
  html[data-ui-version="v2"] .bills-mobile-chrome[data-expanded="1"] .chrome-count,
  html[data-ui-version="v2"] .bills-mobile-chrome[data-expanded="1"] .chrome-spacer,
  html[data-ui-version="v2"] .bills-mobile-chrome[data-expanded="1"] .chrome-btn.chrome-search-toggle {
    display: none;
  }
  html[data-ui-version="v2"] .bills-mobile-chrome[data-expanded="1"] .chrome-search {
    display: block;
  }

  /* (3) Hide the generic floating "Expand all" pill on mobile v2 — our
     chrome button (chrome-btn.chrome-expand-all) owns this action now.
     Scoped to Bills workspace only to preserve behaviour on other
     modules that rely on the auto-injected toolbar. */
  html[data-ui-version="v2"] #module-workspace:has(.bills-mobile-chrome) .card-expand-toolbar {
    display: none;
  }
}

/* ═══ v2-p2-extend · Receiving + Invoices parity polish ═══════════════════
   Applies the five polish primitives (status badges, skeleton rows, empty
   states, dimmed rows, pill-chip filters) to the Receiving + Invoices
   modules. All rules are gated on html[data-ui-version="v2"] so that
   `?v2=0` users continue to see the original v1 rendering untouched.
   Additive only — no existing selectors are modified.
   ═════════════════════════════════════════════════════════════════════ */

/* Empty-state host cell sits inside a colspan td; strip borders/padding so
   the shared .empty-state block breathes correctly in both tables
   (.recv-table, .detail-table). */
html[data-ui-version="v2"] .recv-table tbody tr td > .empty-state,
html[data-ui-version="v2"] .detail-table tbody tr td > .empty-state {
  margin: 0;
}
html[data-ui-version="v2"] .recv-table tbody tr:has(> td > .empty-state) td,
html[data-ui-version="v2"] .detail-table tbody tr:has(> td > .empty-state) td {
  border: none;
  background: transparent;
}

/* Skeleton host cell — zero td padding so shimmer rows align flush with
   real rows underneath. */
html[data-ui-version="v2"] .recv-table tbody tr:has(> td > .skeleton-row:first-child) td,
html[data-ui-version="v2"] .detail-table tbody tr:has(> td > .skeleton-row:first-child) td {
  padding: 0;
  border: none;
}

/* Badge inside a right-aligned numeric cell (Invoices · "Bales" column)
   should align with the tabular-num values sitting above/below it. */
html[data-ui-version="v2"] .recv-table td .badge {
  vertical-align: middle;
}

/* ═══════════════════════════════════════════════════════════════════════
   v2-motion-polish · 2026-04-24
   Tactile micro-interactions + spring easing + reduced-motion fidelity.
   Everything below is additive and scoped to [data-ui-version="v2"]
   except the unconditional skeleton fade-in keyframe and the single
   consolidated @media (prefers-reduced-motion) block at the end.

   Motion vocabulary:
     --dur-fast  (120ms) --dur-base (180ms)
     --dur-slow  (260ms) --dur-drawer (320ms)
     --ease-out / --ease-in-out / --ease-spring
   Primitive tokens are declared in :root above.
   ═══════════════════════════════════════════════════════════════════════ */

/* ── Tactile :active press — subtle scale on all clickable primitives ── */
html[data-ui-version="v2"] .btn {
  transition: background var(--dur-fast) var(--ease-out),
              border-color var(--dur-fast) var(--ease-out),
              color var(--dur-fast) var(--ease-out),
              box-shadow var(--dur-base) var(--ease-out),
              transform var(--dur-fast) var(--ease-out);
}
html[data-ui-version="v2"] .btn:active {
  /* Override the legacy translateY(1px) with a spring-scale push. */
  transform: scale(0.98);
}
html[data-ui-version="v2"] .drawer-panel__icon-btn:active { transform: scale(0.92); }
html[data-ui-version="v2"] .fab:active { transform: scale(0.93); }
html[data-ui-version="v2"] .lot-rpt-lot-card:active {
  /* Push-down complement to the existing hover lift. */
  transform: translateY(0) scale(0.99);
  transition-duration: var(--dur-fast);
}
html[data-ui-version="v2"] .section-card:active {
  transform: scale(0.995);
  transition-duration: var(--dur-fast);
}

/* Mobile-only tactile feedback — bottom nav + swipe-action buttons. */
@media (max-width: 768px) {
  html[data-ui-version="v2"] .bottom-nav button:active { transform: scale(0.95); }
}

/* ── Hover duration normalization (dignity over speed) ────────────────── */
html[data-ui-version="v2"] .chip {
  /* Chips feel snappy — 120ms. Transform stays at 120ms as before. */
  transition-duration: var(--dur-fast);
}
html[data-ui-version="v2"] .section-card {
  /* Section lift uses --dur-base (180ms) — slower carries more weight. */
  transition-duration: var(--dur-base);
}

/* ── Skeleton → content fade-in (prevents pop on replace) ─────────────── */
@keyframes v2-fade-in {
  from { opacity: 0; }
  to   { opacity: 1; }
}
html[data-ui-version="v2"] #module-workspace tbody > tr:not(.skeleton-row):not(.mobile-group-header) {
  animation: v2-fade-in var(--dur-fast) var(--ease-out);
}

/* ═══ Consolidated prefers-reduced-motion overrides ═════════════════════
   Aggregates the previously scattered reduced-motion rules so the full
   no-motion surface area is maintainable in one place. The older
   targeted @media blocks above still apply (they're narrower) — this
   one catches anything else that animates. */
@media (prefers-reduced-motion: reduce) {
  /* Animations: hard off across the v2 motion surface. */
  html[data-ui-version="v2"] .drawer-scrim,
  html[data-ui-version="v2"] .drawer-panel,
  html[data-ui-version="v2"] .modal-backdrop,
  html[data-ui-version="v2"] .modal-dialog,
  html[data-ui-version="v2"] .toast-item,
  html[data-ui-version="v2"] .toast-item.fade-out,
  html[data-ui-version="v2"] .chip,
  html[data-ui-version="v2"] .chip:active,
  html[data-ui-version="v2"] .btn,
  html[data-ui-version="v2"] .btn:active,
  html[data-ui-version="v2"] .fab,
  html[data-ui-version="v2"] .fab:active,
  html[data-ui-version="v2"] .drawer-panel__icon-btn,
  html[data-ui-version="v2"] .drawer-panel__icon-btn:active,
  html[data-ui-version="v2"] .section-card,
  html[data-ui-version="v2"] .section-card:active,
  html[data-ui-version="v2"] .lot-rpt-lot-card,
  html[data-ui-version="v2"] .lot-rpt-lot-card:active,
  html[data-ui-version="v2"] .bottom-nav button:active,
  html[data-ui-version="v2"] .bills-mobile-chrome .chrome-btn,
  html[data-ui-version="v2"] .bills-mobile-chrome .chrome-btn:active,
  html[data-ui-version="v2"] #module-workspace tbody > tr,
  html[data-ui-version="v2"] #module-workspace tbody > tr:not(.skeleton-row):not(.mobile-group-header) {
    animation: none !important;
    transition: none !important;
    transform: none !important;
  }
  /* Skeleton shimmer should pause entirely — user noted it's the
     most visually aggressive element when reduced-motion is on. */
  html[data-ui-version="v2"] .skeleton,
  html[data-ui-version="v2"] .skeleton-cell {
    animation: none !important;
    background: var(--surface-sunken) !important;
  }
}

/* ═══════════════════════════════════════════════════════════════════════
   v2-cmd-palette · Section 6 · Global ⌘K / Ctrl+K command palette
   ─────────────────────────────────────────────────────────────────────
   The palette reuses the existing .modal-backdrop primitive so the
   scrim, blur, and Esc/Tab focus-trap behavior are inherited. We
   override the dialog chrome — no padding, no title bar, rounded
   corners all the way down — and give it a wider max-width so more
   results stay visible without scrolling. Shown in both v1 and v2.
   ═══════════════════════════════════════════════════════════════════════ */
.modal-backdrop.cmd-palette-host {
  align-items: flex-start;
  padding-top: 12vh;
}
.modal-dialog.cmd-palette {
  max-width: 560px;
  padding: 0;
  border-radius: 14px;
  overflow: hidden;
  background: var(--surface-popover);
  border: 1px solid var(--line);
}
.cmd-palette__input {
  display: flex;
  align-items: center;
  gap: var(--sp-3, 12px);
  padding: 14px 18px;
  border-bottom: 1px solid var(--line);
  color: var(--text-secondary);
}
.cmd-palette__input svg { color: var(--text-secondary); flex-shrink: 0; }
.cmd-palette__input input {
  flex: 1;
  background: transparent;
  border: 0;
  outline: 0;
  font: inherit;
  font-size: var(--text-md, 0.95rem);
  font-weight: 400;
  color: var(--text);
  padding: 0;
}
.cmd-palette__input input::placeholder { color: var(--text-secondary); opacity: 0.8; }
.cmd-palette__hint {
  font-size: 10px;
  color: var(--text-secondary);
  background: var(--surface-sunken);
  padding: 2px 6px;
  border-radius: 4px;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  flex-shrink: 0;
}
.cmd-palette__list {
  list-style: none;
  margin: 0;
  padding: 6px 0;
  max-height: 60vh;
  overflow-y: auto;
}
.cmd-palette__group-label {
  font-size: 10px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--text-secondary);
  padding: 8px 18px 4px;
  user-select: none;
}
.cmd-palette__row {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 10px 18px;
  cursor: pointer;
  color: var(--text);
  font-size: var(--text-base, 0.9rem);
  border: 0;
  background: transparent;
  width: 100%;
  text-align: left;
  font-family: inherit;
}
.cmd-palette__row svg { color: var(--text-secondary); flex-shrink: 0; }
.cmd-palette__row strong { font-weight: 600; }
.cmd-palette__row .kind {
  font-size: 11px;
  color: var(--text-secondary);
  margin-left: auto;
  padding-left: 12px;
  flex-shrink: 0;
}
.cmd-palette__row .label {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.cmd-palette__row:hover { background: var(--surface-sunken); }
.cmd-palette__row.active {
  background: var(--primary);
  color: #fff;
}
.cmd-palette__row.active svg,
.cmd-palette__row.active .kind { color: rgba(255, 255, 255, 0.85); }
.cmd-palette__empty {
  padding: 22px 18px;
  text-align: center;
  color: var(--text-secondary);
  font-size: var(--text-sm, 0.8125rem);
}
@media (max-width: 640px) {
  .modal-backdrop.cmd-palette-host { align-items: flex-end; padding: 0; }
  .modal-dialog.cmd-palette {
    max-width: 100%;
    border-radius: 16px 16px 0 0;
  }
  .cmd-palette__list { max-height: 50vh; }
}

/* ═══ v2-notif-panel · right-rail desktop + bottom-sheet mobile ═════════
   Agent mockup-section-8b · ported from ui-mockup.html sections 8b/8c.
   The .v2-notif-root overlay is attached to <body> when the bell is
   clicked under window.__uiVersion === "v2". Every selector here is
   scoped to v2 so the old dropdown (see #notif-dropdown inline styles
   in index.html) is untouched when users opt out via ?v2=0.
   ══════════════════════════════════════════════════════════════════════ */
html[data-ui-version="v2"] .v2-notif-root {
  /* Z-index ladder: drawer 9998 · notif-panel 9998 (same tier — mutually exclusive) ·
     modal / cmd-palette 9999 · toast 10000. */
  position: fixed;
  inset: 0;
  z-index: 9998;
  display: block;
  animation: v2-notif-root-in 160ms ease-out;
}
@keyframes v2-notif-root-in { from { opacity: 0; } to { opacity: 1; } }
html[data-ui-version="v2"] .v2-notif-root--closing {
  animation: v2-notif-root-out 180ms ease-in forwards;
}
@keyframes v2-notif-root-out { to { opacity: 0; } }

html[data-ui-version="v2"] .v2-notif-backdrop {
  position: absolute;
  inset: 0;
  background: rgba(15, 14, 12, 0.28);
  backdrop-filter: blur(12px) saturate(120%);
  -webkit-backdrop-filter: blur(12px) saturate(120%);
  cursor: pointer;
}

html[data-ui-version="v2"] .v2-notif-panel {
  position: absolute;
  top: 64px;
  right: 16px;
  width: 380px;
  max-width: calc(100vw - 32px);
  max-height: min(640px, calc(100vh - 96px));
  background: var(--surface, #fff);
  border: 1px solid var(--line, #e5e1d8);
  border-radius: 14px;
  box-shadow: 0 24px 60px rgba(15, 14, 12, 0.22);
  display: flex;
  flex-direction: column;
  overflow: hidden;
  outline: none;
  animation: v2-notif-panel-in 220ms cubic-bezier(0.2, 0.9, 0.3, 1.1);
}
@keyframes v2-notif-panel-in {
  0%   { transform: translateY(-8px) scale(0.98); opacity: 0; }
  80%  { transform: translateY(0) scale(1); opacity: 1; }
  100% { transform: translateY(0) scale(1); opacity: 1; }
}

html[data-ui-version="v2"] .v2-notif-panel__head {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 12px 16px;
  border-bottom: 1px solid var(--line, #e5e1d8);
  flex-shrink: 0;
}
html[data-ui-version="v2"] .v2-notif-panel__title {
  flex: 1;
  margin: 0;
  font-size: 15px;
}
html[data-ui-version="v2"] .v2-notif-panel__mark {
  font-size: 12px;
  padding: 4px 10px;
}
html[data-ui-version="v2"] .v2-notif-panel__close {
  width: 30px;
  height: 30px;
  display: inline-grid;
  place-items: center;
  background: transparent;
  border: 1px solid transparent;
  border-radius: 8px;
  color: var(--text-secondary, #4b4843);
  cursor: pointer;
  padding: 0;
  transition: background 140ms, color 140ms, border-color 140ms;
}
html[data-ui-version="v2"] .v2-notif-panel__close:hover {
  background: var(--surface-sunken, #f5f3ef);
  color: var(--text, #0f0e0c);
  border-color: var(--line, #e5e1d8);
}
html[data-ui-version="v2"] .v2-notif-panel__close:focus-visible {
  outline: 2px solid var(--primary, #4f46e5);
  outline-offset: 2px;
}

html[data-ui-version="v2"] .v2-notif-panel__body {
  flex: 1;
  overflow-y: auto;
  overflow-x: hidden;
  -webkit-overflow-scrolling: touch;
}

html[data-ui-version="v2"] .v2-notif-empty {
  padding: 32px 20px;
  text-align: center;
  color: var(--text-secondary, #4b4843);
  font-size: 13px;
}

html[data-ui-version="v2"] .v2-notif-group-header {
  padding: 10px 16px 4px;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-secondary, #4b4843);
  background: var(--surface-sunken, #f5f3ef);
  position: sticky;
  top: 0;
  z-index: 1;
  display: flex;
  align-items: center;
  gap: 8px;
  border-bottom: 1px solid var(--line, #e5e1d8);
}
html[data-ui-version="v2"] .v2-notif-group-header .count {
  margin-left: auto;
  font-weight: 500;
  text-transform: none;
  letter-spacing: 0;
  color: var(--text-secondary, #4b4843);
  font-variant-numeric: tabular-nums;
}

html[data-ui-version="v2"] .v2-notif-row {
  display: grid;
  grid-template-columns: 28px 1fr auto;
  gap: 10px;
  padding: 12px 16px;
  border-bottom: 1px solid var(--line, #e5e1d8);
  cursor: pointer;
  align-items: start;
  background: var(--surface, #fff);
  transition: background 140ms;
}
html[data-ui-version="v2"] .v2-notif-row:hover {
  background: var(--surface-sunken, #f5f3ef);
}
html[data-ui-version="v2"] .v2-notif-row:focus-visible {
  outline: 2px solid var(--primary, #4f46e5);
  outline-offset: -2px;
}
html[data-ui-version="v2"] .v2-notif-row.unread {
  background: color-mix(in srgb, var(--primary, #4f46e5) 6%, var(--surface, #fff));
}
html[data-ui-version="v2"] .v2-notif-row.unread:hover {
  background: color-mix(in srgb, var(--primary, #4f46e5) 10%, var(--surface, #fff));
}

html[data-ui-version="v2"] .v2-notif-row__severity {
  width: 28px;
  height: 28px;
  border-radius: 999px;
  display: grid;
  place-items: center;
  flex-shrink: 0;
  margin-top: 1px;
}
html[data-ui-version="v2"] .v2-notif-row__severity.sev-1 {
  background: var(--surface-sunken, #f5f3ef);
  color: var(--text-secondary, #4b4843);
}
html[data-ui-version="v2"] .v2-notif-row__severity.sev-2 {
  background: color-mix(in srgb, var(--primary, #4f46e5) 14%, transparent);
  color: var(--primary, #4f46e5);
}
html[data-ui-version="v2"] .v2-notif-row__severity.sev-3 {
  background: color-mix(in srgb, #f59e0b 18%, transparent);
  color: #b45309;
}
html[data-ui-version="v2"] .v2-notif-row__severity.sev-4 {
  background: color-mix(in srgb, #dc2626 18%, transparent);
  color: #b91c1c;
}
html[data-ui-version="v2"] .v2-notif-row__body {
  min-width: 0;
}
html[data-ui-version="v2"] .v2-notif-row__msg {
  font-size: 13px;
  color: var(--text, #0f0e0c);
  font-weight: 500;
  line-height: 1.45;
  letter-spacing: -0.005em;
  word-wrap: break-word;
}
html[data-ui-version="v2"] .v2-notif-row__time {
  font-size: 11px;
  color: var(--text-secondary, #4b4843);
  white-space: nowrap;
  margin-top: 3px;
  font-variant-numeric: tabular-nums;
}

/* ── Mobile bottom-sheet variant ─────────────────────────────────────── */
@media (max-width: 768px) {
  html[data-ui-version="v2"] .v2-notif-panel--sheet {
    top: auto;
    right: 0;
    left: 0;
    bottom: 0;
    width: 100%;
    max-width: 100%;
    max-height: 82dvh;
    border-radius: 20px 20px 0 0;
    border-left: none;
    border-right: none;
    border-bottom: none;
    padding-bottom: env(safe-area-inset-bottom, 0);
    animation: v2-notif-sheet-in 260ms cubic-bezier(0.2, 0.9, 0.3, 1);
  }
  @keyframes v2-notif-sheet-in {
    from { transform: translateY(100%); }
    to   { transform: translateY(0); }
  }
  html[data-ui-version="v2"] .v2-notif-panel__handle {
    width: 40px;
    height: 4px;
    background: var(--line, #e5e1d8);
    border-radius: 999px;
    margin: 8px auto 4px;
    flex-shrink: 0;
  }
  html[data-ui-version="v2"] .v2-notif-panel__body {
    padding-bottom: calc(12px + env(safe-area-inset-bottom, 0));
  }
}

@media (prefers-reduced-motion: reduce) {
  html[data-ui-version="v2"] .v2-notif-root,
  html[data-ui-version="v2"] .v2-notif-panel,
  html[data-ui-version="v2"] .v2-notif-panel--sheet {
    animation: none !important;
  }
}

/* ═══════════════════════════════════════════════════════════════════════
   v2-sec-2-8d-9 · Dashboard + inline module chips + sticky context bar
   Ported from ui-mockup.html Sections 2, 8d, 9. All rules scoped to
   html[data-ui-version="v2"] or the dedicated v2-only DOM nodes
   (#v2-context-bar, .dash-*, .module-chip) which are never emitted on v1.
   Additive only — no existing selectors modified.
   ═══════════════════════════════════════════════════════════════════════ */

/* ── Section 2 · Dashboard layout (mockup Section 2 fidelity port) ──────
   Hero display-type greeting · 4-col stat grid with delta indicators ·
   horizontal rail of gradient rail-cards (amber / danger / success /
   neutral / default indigo variants) · pending-actions rail reuses the
   same rail-card shape with severity-coloured art blocks. */
html[data-ui-version="v2"] .dash-wrap {
  display: flex;
  flex-direction: column;
  gap: 32px;
}
html[data-ui-version="v2"] .dash-hero {
  padding: 4px 0 0;
}
html[data-ui-version="v2"] .dash-hero__greeting {
  font-size: 28px;
  font-weight: 600;
  letter-spacing: -0.02em;
  color: var(--text, #0f0e0c);
  line-height: 1.15;
}
html[data-ui-version="v2"] .dash-hero__hint {
  margin-top: 6px;
  color: var(--muted, #8a857d);
  font-size: var(--text-xs, 12px);
  letter-spacing: 0.04em;
  text-transform: uppercase;
  font-weight: 500;
}

/* Stat-card grid · 4-col desktop, 2-col tablet, 1-col narrow phones. */
html[data-ui-version="v2"] .dash-stats-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 16px;
}
@media (max-width: 900px) {
  html[data-ui-version="v2"] .dash-stats-grid { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 560px) {
  html[data-ui-version="v2"] .dash-stats-grid { grid-template-columns: 1fr; }
}
html[data-ui-version="v2"] .dash-stat-card {
  background: var(--surface-raised, #fff);
  border: 1px solid var(--line, #ebe7df);
  border-radius: var(--radius-lg, 14px);
  padding: 20px;
  text-align: left;
  cursor: pointer;
  font: inherit;
  color: inherit;
  transition: box-shadow 220ms ease, border-color 220ms ease, transform 220ms ease;
}
html[data-ui-version="v2"] .dash-stat-card:hover {
  box-shadow: var(--shadow-md, 0 2px 8px rgba(15,14,12,0.07));
  border-color: var(--line-strong, #d9d3c7);
  transform: translateY(-1px);
}
html[data-ui-version="v2"] .dash-stat-card__label {
  font-size: 11px;
  color: var(--muted, #8a857d);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  font-weight: 500;
}
html[data-ui-version="v2"] .dash-stat-card__value {
  font-size: 28px;
  font-weight: 600;
  letter-spacing: -0.02em;
  margin: 6px 0 4px;
  font-variant-numeric: tabular-nums;
  color: var(--text, #0f0e0c);
  line-height: 1.1;
}
html[data-ui-version="v2"] .dash-stat-card__delta {
  font-size: var(--text-xs, 12px);
  color: var(--text-secondary, #4b4843);
  display: inline-flex;
  align-items: center;
  gap: 4px;
}
html[data-ui-version="v2"] .dash-stat-card__delta.up     { color: var(--success, #16a34a); }
html[data-ui-version="v2"] .dash-stat-card__delta.down   { color: var(--danger,  #dc2626); }
html[data-ui-version="v2"] .dash-stat-card__delta.warn   { color: var(--amber,   #d97706); }
html[data-ui-version="v2"] .dash-stat-card__delta .arrow {
  font-size: 10px;
  line-height: 1;
  font-variant-numeric: tabular-nums;
}

/* Rail section header: 20px title · muted pill "See all" chip right-aligned. */
html[data-ui-version="v2"] .dash-rail-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 4px;
}
html[data-ui-version="v2"] .dash-rail-head__title {
  margin: 0;
  font-size: 20px;
  font-weight: 600;
  letter-spacing: -0.01em;
  color: var(--text, #0f0e0c);
}
html[data-ui-version="v2"] .dash-rail-head__see-all {
  font-size: 11px;
  color: var(--muted, #8a857d);
  background: var(--surface-sunken, #f4f2ed);
  border: 1px solid transparent;
  cursor: pointer;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  font-weight: 500;
  padding: 5px 12px;
  border-radius: var(--radius-full, 999px);
  transition: background 150ms ease, color 150ms ease, border-color 150ms ease;
  font-family: inherit;
  line-height: 1.3;
}
html[data-ui-version="v2"] .dash-rail-head__see-all:hover {
  background: var(--primary-light, #eef2ff);
  color: var(--primary, #4f46e5);
  border-color: color-mix(in srgb, var(--primary, #4f46e5) 18%, transparent);
}
html[data-ui-version="v2"] .dash-rail-head__see-all:focus-visible {
  outline: 2px solid var(--primary, #4f46e5);
  outline-offset: 2px;
}

/* Horizontal rail · scroll-snap · subtle scrollbar (Spotify library shelf). */
html[data-ui-version="v2"] .dash-rail {
  display: flex;
  gap: 16px;
  overflow-x: auto;
  padding: 4px 0 12px;
  scroll-snap-type: x mandatory;
  scrollbar-width: thin;
  -webkit-overflow-scrolling: touch;
}
html[data-ui-version="v2"] .dash-rail::-webkit-scrollbar { height: 6px; }
html[data-ui-version="v2"] .dash-rail::-webkit-scrollbar-thumb {
  background: var(--line-strong, #d9d3c7);
  border-radius: var(--radius-full, 999px);
}
html[data-ui-version="v2"] .dash-rail__empty {
  width: 100%;
  padding: 24px;
  text-align: center;
  color: var(--muted, #8a857d);
  font-size: var(--text-sm, 13px);
  border: 1px dashed var(--line, #ebe7df);
  border-radius: var(--radius-lg, 14px);
  background: var(--surface-raised, #fff);
}

/* Rail-card · gradient art block up top, title + dot-meta below.
   200px wide tile · 140px tall art block with 40px overlaid program number. */
html[data-ui-version="v2"] .dash-rail-card {
  flex: 0 0 auto;
  width: 180px;
  scroll-snap-align: start;
  background: var(--surface-raised, #fff);
  border: 1px solid var(--line, #ebe7df);
  border-radius: var(--radius-lg, 14px);
  padding: 10px;
  cursor: pointer;
  font: inherit;
  color: inherit;
  text-align: left;
  transition: transform 220ms ease, box-shadow 220ms ease, border-color 220ms ease;
}
html[data-ui-version="v2"] .dash-rail-card:hover {
  transform: translateY(-2px);
  box-shadow: var(--shadow-md, 0 2px 8px rgba(15,14,12,0.07));
  border-color: var(--line-strong, #d9d3c7);
}
html[data-ui-version="v2"] .dash-rail-card:focus-visible {
  outline: 2px solid var(--primary, #4f46e5);
  outline-offset: 2px;
}
html[data-ui-version="v2"] .dash-rail-card__art {
  height: 100px;
  border-radius: var(--radius, 10px);
  margin-bottom: 10px;
  background: linear-gradient(135deg, var(--primary, #4f46e5) 0%, #8b5cf6 100%);
  display: flex; align-items: center; justify-content: center;
  color: #fff;
  font-size: 30px;
  font-weight: 600;
  letter-spacing: -0.02em;
  font-variant-numeric: tabular-nums;
  line-height: 1;
}
/* Status variants — each program tile picks a gradient matching its state. */
html[data-ui-version="v2"] .dash-rail-card__art.danger  { background: linear-gradient(135deg, var(--danger,  #dc2626) 0%, #f97316 100%); }
html[data-ui-version="v2"] .dash-rail-card__art.amber   { background: linear-gradient(135deg, var(--amber,   #d97706) 0%, #fbbf24 100%); }
html[data-ui-version="v2"] .dash-rail-card__art.success { background: linear-gradient(135deg, var(--success, #16a34a) 0%, #10b981 100%); }
html[data-ui-version="v2"] .dash-rail-card__art.neutral { background: linear-gradient(135deg, #6b7280 0%, #9ca3af 100%); }
html[data-ui-version="v2"] .dash-rail-card__title {
  font-weight: 600;
  font-size: 14px;
  color: var(--text, #0f0e0c);
  letter-spacing: -0.01em;
  margin-bottom: 4px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
html[data-ui-version="v2"] .dash-rail-card__meta {
  font-size: 13px;
  font-weight: 600;
  color: var(--text-secondary, #4b4843);
  display: flex;
  gap: 6px;
  align-items: center;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
html[data-ui-version="v2"] .dash-rail-card__meta .dot {
  width: 6px; height: 6px; border-radius: 999px;
  background: currentColor; opacity: 0.85;
  flex-shrink: 0;
}

/* Pending-action cards reuse the rail-card geometry (gradient art + title +
   meta) so the two rails feel like one visual family. The art block overlays
   a glyph (!, ○, →) instead of a program number. */
html[data-ui-version="v2"] .dash-action-card {
  flex: 0 0 auto;
  width: 200px;
  scroll-snap-align: start;
  background: var(--surface-raised, #fff);
  border: 1px solid var(--line, #ebe7df);
  border-radius: var(--radius-lg, 14px);
  padding: 12px;
  cursor: pointer;
  font: inherit;
  color: inherit;
  text-align: left;
  transition: transform 220ms ease, box-shadow 220ms ease, border-color 220ms ease;
}
html[data-ui-version="v2"] .dash-action-card:hover {
  transform: translateY(-2px);
  box-shadow: var(--shadow-md, 0 2px 8px rgba(15,14,12,0.07));
  border-color: var(--line-strong, #d9d3c7);
}
html[data-ui-version="v2"] .dash-action-card:focus-visible {
  outline: 2px solid var(--primary, #4f46e5);
  outline-offset: 2px;
}
html[data-ui-version="v2"] .dash-action-card__art {
  height: 140px;
  border-radius: var(--radius, 10px);
  margin-bottom: 12px;
  background: linear-gradient(135deg, var(--primary, #4f46e5) 0%, #8b5cf6 100%);
  display: flex; align-items: center; justify-content: center;
  color: #fff;
  font-size: 40px;
  font-weight: 600;
  letter-spacing: -0.02em;
  line-height: 1;
}
html[data-ui-version="v2"] .dash-action-card__art.danger  { background: linear-gradient(135deg, var(--danger,  #dc2626) 0%, #f97316 100%); }
html[data-ui-version="v2"] .dash-action-card__art.amber   { background: linear-gradient(135deg, var(--amber,   #d97706) 0%, #fbbf24 100%); }
html[data-ui-version="v2"] .dash-action-card__art.success { background: linear-gradient(135deg, var(--success, #16a34a) 0%, #10b981 100%); }
html[data-ui-version="v2"] .dash-action-card__art.neutral { background: linear-gradient(135deg, #6b7280 0%, #9ca3af 100%); }
html[data-ui-version="v2"] .dash-action-card__title {
  font-weight: 600;
  font-size: 14px;
  color: var(--text, #0f0e0c);
  letter-spacing: -0.01em;
  margin-bottom: 4px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
html[data-ui-version="v2"] .dash-action-card__meta {
  font-size: 12px;
  color: var(--muted, #8a857d);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* ── Section 8d · Inline module chips on Program Detail ─────────────── */
/* Ported from ui-mockup.html lines ~998-1009. Same visual spec as the
   mockup; colours resolve via the v2 semantic tokens declared at the top
   of this file so dark-mode / print don't need separate rules. */
html[data-ui-version="v2"] .module-chip-row {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
  margin: 10px 0 4px;
}
html[data-ui-version="v2"] .module-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 4px 10px;
  border-radius: 999px;
  background: var(--amber-light, #fef3c7);
  color: var(--amber, #d97706);
  font-size: 12px;
  font-weight: 500;
  border: 1px solid transparent;
  line-height: 1.4;
}
html[data-ui-version="v2"] .module-chip .dot {
  width: 6px; height: 6px;
  border-radius: 999px;
  background: currentColor;
}
html[data-ui-version="v2"] .module-chip.danger {
  background: var(--danger-light, #fee2e2);
  color: var(--danger, #dc2626);
}
html[data-ui-version="v2"] .module-chip.info {
  background: var(--primary-light, #eef2ff);
  color: var(--primary, #4f46e5);
}

/* ── Section 9 · Sticky context bar (Spotify-footer pattern) ────────── */
/* v19.6: the bottom sticky context bar is retired. Progress now lives inline
   in the detail hero (see .detail-progress-strip below). Keep the rules in
   place so nothing breaks if a state flag leaks through, but force-hide. */
#v2-context-bar.context-bar {
  display: none !important;
}
#v2-context-bar.context-bar[data-retired="keep-original-rules"] {
  position: fixed;
  left: 16px;
  right: 16px;
  bottom: 16px;
  z-index: 40;
  padding: 10px 14px;
  display: none;
  align-items: center;
  gap: 14px;
  background: #1c1917;
  color: #fbfaf8;
  border-radius: 14px;
  box-shadow: 0 14px 36px rgba(15,14,12,0.24);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
  border: 1px solid rgba(255,255,255,0.06);
  font-family: inherit;
}
html[data-ui-version="v2"] #v2-context-bar.context-bar[data-visible="1"] {
  display: flex;
  animation: v2-ctxbar-rise 220ms cubic-bezier(0.22, 1, 0.36, 1);
}
@keyframes v2-ctxbar-rise {
  from { transform: translateY(24px); opacity: 0; }
  to   { transform: translateY(0);    opacity: 1; }
}
#v2-context-bar .context-bar__mark {
  width: 44px; height: 44px;
  border-radius: 10px;
  background: linear-gradient(135deg, #4f46e5, #8b5cf6);
  display: grid; place-items: center;
  color: #fff;
  font-weight: 600;
  font-size: 15px;
  letter-spacing: -0.02em;
  font-variant-numeric: tabular-nums;
  flex-shrink: 0;
}
#v2-context-bar .context-bar__info {
  flex: 1;
  min-width: 0;
}
#v2-context-bar .context-bar__title {
  font-size: 13px;
  font-weight: 600;
  letter-spacing: -0.01em;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
#v2-context-bar .context-bar__meta {
  font-size: 11px;
  opacity: 0.7;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  margin-top: 2px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
#v2-context-bar .context-bar__progress {
  flex: 0 0 180px;
  height: 4px;
  background: rgba(255,255,255,0.12);
  border-radius: 999px;
  overflow: hidden;
  position: relative;
}
#v2-context-bar .context-bar__progress > span {
  display: block;
  height: 100%;
  width: 0%;
  background: linear-gradient(90deg, #16a34a, #4ade80);
  border-radius: 999px;
  transition: width 260ms ease;
}
#v2-context-bar .context-bar__actions {
  display: flex;
  gap: 6px;
  flex-shrink: 0;
}
#v2-context-bar .context-bar__actions button {
  background: rgba(255,255,255,0.08);
  border: 0;
  color: #fff;
  width: 32px; height: 32px;
  border-radius: 999px;
  cursor: pointer;
  display: grid; place-items: center;
  transition: background 150ms ease, transform 120ms ease;
  font-size: 14px;
  line-height: 1;
}
#v2-context-bar .context-bar__actions button:hover { background: rgba(255,255,255,0.16); }
#v2-context-bar .context-bar__actions button:active { transform: scale(0.92); }

/* Mobile: clear the bottom-nav (~64px) + safe-area, hide the progress
   bar to save horizontal room, shrink the mark tile so the title has
   priority on narrow screens. */
@media (max-width: 768px) {
  #v2-context-bar.context-bar {
    left: 8px;
    right: 8px;
    bottom: calc(72px + env(safe-area-inset-bottom, 0px));
    padding: 8px 10px;
    gap: 10px;
    border-radius: 12px;
  }
  #v2-context-bar .context-bar__progress { display: none; }
  #v2-context-bar .context-bar__mark {
    width: 36px; height: 36px;
    font-size: 13px;
    border-radius: 8px;
  }
  #v2-context-bar .context-bar__actions button {
    width: 28px; height: 28px;
  }
}

/* Reduced motion: no slide-up animation, no progress transition. */
@media (prefers-reduced-motion: reduce) {
  html[data-ui-version="v2"] #v2-context-bar.context-bar[data-visible="1"] {
    animation: none;
  }
  #v2-context-bar .context-bar__progress > span {
    transition: none;
  }
}

/* ═══════════════════════════════════════════════════════════════════════
   v2 · Things 3 + Spotify signature polish (v18.5 coherence)
   Breathing whitespace · tactile tap states · muted sidebar accents.
   All rules v2-scoped so v1 is byte-identical.
   ═══════════════════════════════════════════════════════════════════════ */

/* Things-3 signature: 56px mobile row height with generous vertical padding.
   Replaces the default 44px compact rows on mobile Bills / Programs / Lots. */
@media (max-width: 768px) {
  html[data-ui-version="v2"] #module-workspace tbody > tr:not(.mobile-group-header) > td {
    padding-top: var(--sp-4, 16px);
    padding-bottom: var(--sp-4, 16px);
    line-height: 1.45;
  }
  html[data-ui-version="v2"] tbody > tr:not(.mobile-group-header) {
    min-height: 56px;
  }
}

/* Sidebar module colour-dot accents — Spotify/Things signature of colour as
   module identity. Render as a tiny 6px dot left of the nav-item label. */
html[data-ui-version="v2"] #master-nav a[data-mod]::before,
html[data-ui-version="v2"] .nav-item[data-mod]::before,
html[data-ui-version="v2"] #master-nav button[data-mod]::before {
  content: "";
  display: inline-block;
  width: 6px;
  height: 6px;
  border-radius: 999px;
  background: var(--c-module, var(--text-secondary, #8a857d));
  opacity: 0.55;
  margin-right: var(--sp-3, 12px);
  vertical-align: middle;
  transition: opacity var(--dur-fast, 120ms) var(--ease-out, ease), transform var(--dur-fast, 120ms) var(--ease-out, ease);
}
html[data-ui-version="v2"] #master-nav a[data-mod]:hover::before,
html[data-ui-version="v2"] #master-nav a[data-mod].active::before,
html[data-ui-version="v2"] .nav-item[data-mod].active::before {
  opacity: 1;
  transform: scale(1.1);
}
html[data-ui-version="v2"] [data-mod="dashboard"]     { --c-module: var(--primary, #4f46e5); }
html[data-ui-version="v2"] [data-mod="programs"]      { --c-module: var(--primary, #4f46e5); }
html[data-ui-version="v2"] [data-mod="bills"]         { --c-module: #8b5cf6; }
html[data-ui-version="v2"] [data-mod="dyeing_lots"]   { --c-module: var(--success, #16a34a); }
html[data-ui-version="v2"] [data-mod="lot_report"]    { --c-module: var(--success, #16a34a); }
html[data-ui-version="v2"] [data-mod="bales"]         { --c-module: var(--amber, #d97706); }
html[data-ui-version="v2"] [data-mod="receiving"]     { --c-module: var(--amber, #d97706); }
html[data-ui-version="v2"] [data-mod="invoices"]      { --c-module: #64748b; }
html[data-ui-version="v2"] [data-mod="shades"]        { --c-module: #ec4899; }
html[data-ui-version="v2"] [data-mod="fabrics"]       { --c-module: #0ea5e9; }
html[data-ui-version="v2"] [data-mod="contracts"]     { --c-module: #14b8a6; }
html[data-ui-version="v2"] [data-mod="reports"]       { --c-module: #f59e0b; }

/* Things-3 signature: dignified section dividers — hairline + generous gap
   rather than heavy borders. v2-scoped so cards still look crisp in v1. */
html[data-ui-version="v2"] .section-card + .section-card {
  margin-top: var(--sp-6, 24px);
}
html[data-ui-version="v2"] .detail-card + .detail-card {
  margin-top: var(--sp-6, 24px);
}

/* Things-3 signature: soft-depth section cards — lift subtly on group-hover
   so the whole card feels tappable as a unit. */
html[data-ui-version="v2"] .section-card,
html[data-ui-version="v2"] .detail-card {
  border: 1px solid var(--line-strong, var(--line, #ebe7df));
  border-radius: var(--radius-lg, 16px);
  box-shadow: var(--shadow-sm, 0 1px 2px rgba(15,14,12,0.06));
  transition: box-shadow var(--dur-base, 180ms) var(--ease-out, ease),
              border-color var(--dur-base, 180ms) var(--ease-out, ease);
}
html[data-ui-version="v2"] .section-card:hover,
html[data-ui-version="v2"] .detail-card:hover {
  box-shadow: var(--shadow, 0 2px 8px rgba(15,14,12,0.07));
  border-color: var(--line-strong, var(--line, #d9d3c7));
}

/* Respect reduced-motion for all new rules. */
@media (prefers-reduced-motion: reduce) {
  html[data-ui-version="v2"] #master-nav a[data-mod]::before,
  html[data-ui-version="v2"] .nav-item[data-mod]::before,
  html[data-ui-version="v2"] .section-card,
  html[data-ui-version="v2"] .detail-card {
    transition: none !important;
  }
}

/* ═══════════════════════════════════════════════════════════════════════
   v2-hero-polish (v18.6) — program-detail 4-stat hero + pure-underline
   lot-report tabs. All rules are v2-scoped via html[data-ui-version="v2"]
   so the v1 visual baseline is untouched. No new tokens introduced.
   ═══════════════════════════════════════════════════════════════════════ */

/* 4-stat hero row beside the program number (mockup Section 03). Sits
   between the title-row and the meta-grid. Flex row wraps cleanly on
   narrow widths; value is the dominant read, label is a muted caption. */
html[data-ui-version="v2"] .detail-hero-stats {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-6, 24px);
  padding: var(--sp-3, 12px) 0 var(--sp-4, 16px);
  margin-bottom: var(--sp-4, 16px);
  border-bottom: 1px solid var(--line, #ebe7df);
}
html[data-ui-version="v2"] .detail-hero-stat {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 96px;
}
html[data-ui-version="v2"] .detail-hero-stat-value {
  font-family: "JetBrains Mono", monospace;
  font-size: var(--text-lg, 17px);
  font-weight: 700;
  color: var(--text);
  line-height: 1.2;
  letter-spacing: -0.01em;
}
html[data-ui-version="v2"] .detail-hero-stat-label {
  font-size: var(--text-xs, 11px);
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--muted);
}
@media (max-width: 640px) {
  html[data-ui-version="v2"] .detail-hero-stats { gap: var(--sp-4, 16px); }
  html[data-ui-version="v2"] .detail-hero-stat { min-width: 72px; flex: 1 1 40%; }
}

/* Lot Report tabs — pure underline (mockup Section 05). Strip the pill
   shape + filled active state; keep the animated scaleX underline from
   the v2 block at styles.css:2166. v1 keeps its pill appearance because
   this rule is scoped to html[data-ui-version="v2"]. */
html[data-ui-version="v2"] .lot-rpt-tab {
  background: transparent;
  border: 0;
  padding: 8px 14px;
  color: var(--text-secondary);
  border-radius: 0;
}
html[data-ui-version="v2"] .lot-rpt-tab:hover:not(.active) {
  background: transparent;
  border-color: transparent;
  color: var(--text);
}
html[data-ui-version="v2"] .lot-rpt-tab.active {
  background: transparent;
  border-color: transparent;
  color: var(--text);
  font-weight: 600;
}

/* ═══════════════════════════════════════════════════════════════════════
   v2-mockup-fidelity (v18.7) — sidebar chrome + Bills hero + pure-
   underline Bills tabs + status-badge dots + program-detail hero-row
   flex layout. All rules scoped to html[data-ui-version="v2"] so the v1
   baseline stays byte-identical. No new tokens; falls back to existing
   CSS custom properties with literal fallbacks for older browsers.
   ═══════════════════════════════════════════════════════════════════════ */

/* ── 1 · Sidebar chrome ─────────────────────────────────────────────── */
/* Brand row: purple-gradient "D" mark + wordmark. Rendered via ::before
   (the "D" badge) and ::after (the "Dyeing" wordmark) on the existing
   .sidebar h1 so no markup change is required. The h1's own "Dyeing
   Manager" text is hidden via font-size:0 on the element; each pseudo
   declares its own font-size so the brand renders correctly. */
html[data-ui-version="v2"] .sidebar h1 {
  position: relative;
  display: flex;
  align-items: center;
  gap: 10px;
  font-family: "DM Sans", Inter, system-ui, sans-serif;
  font-style: normal;
  font-weight: 600;
  font-size: 0;                          /* hide the original wordmark text */
  letter-spacing: -0.01em;
  color: #f8fafc;
  padding: 0 var(--sp-2, 8px) var(--sp-4, 16px);
  border-bottom: 1px solid rgba(255,255,255,0.08);
}
html[data-ui-version="v2"] .sidebar h1::before {
  content: "D";
  flex: 0 0 auto;
  width: 28px;
  height: 28px;
  background: linear-gradient(135deg, #4f46e5, #8b5cf6);
  border-radius: 8px;
  display: grid;
  place-items: center;
  color: #fff;
  font-family: "DM Sans", Inter, system-ui, sans-serif;
  font-weight: 700;
  font-size: 14px;
  line-height: 1;
  letter-spacing: 0;
}
html[data-ui-version="v2"] .sidebar h1::after {
  content: "Dyeing";
  font-family: "DM Sans", Inter, system-ui, sans-serif;
  font-weight: 600;
  font-size: 15px;
  letter-spacing: -0.01em;
  color: inherit;
}
/* Subtitle "Suppliers & Orders" becomes noise next to the brand mark. */
html[data-ui-version="v2"] .sidebar .subtitle {
  display: none;
}

/* Version badge under the brand mark. Visible in both palettes. */
.sidebar-version {
  font-size: 11px;
  margin: 4px 0 8px;
  letter-spacing: 0.5px;
  font-weight: 700;
  color: rgba(255,255,255,0.7);  /* v1 dark-navy sidebar */
  padding-left: 12px;
}
html[data-ui-version="v2"] .sidebar-version {
  color: var(--text-tertiary, #8c8880);
  padding: 0 var(--sp-2, 8px) var(--sp-3, 12px);
  margin: -8px 0 4px;             /* sit snug under the brand h1 */
  border-bottom: 1px solid var(--line, #ebe7df);
}

/* Section dividers — small-caps labels between module groups. */
html[data-ui-version="v2"] .nav-section-label {
  font-size: 11px;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: rgba(255,255,255,0.45);
  padding: var(--sp-3, 12px) var(--sp-3, 12px) var(--sp-1, 4px);
  margin-top: 4px;
}
html[data-ui-version="v2"] .nav-section-label:first-child {
  margin-top: 0;
}

/* Count badges on priority nav items (Lot Reports, Bills, Programs). */
html[data-ui-version="v2"] .nav-item__badge {
  margin-left: auto;
  min-width: 18px;
  height: 18px;
  padding: 0 6px;
  border-radius: 999px;
  font-size: 11px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  background: var(--danger, #dc2626);
  color: #fff;
  display: inline-grid;
  place-items: center;
  line-height: 1;
}
html[data-ui-version="v2"] .nav-item__badge.amber {
  background: var(--amber, #d97706);
}
html[data-ui-version="v2"] .nav-item__badge.info {
  background: var(--primary, #4f46e5);
}
html[data-ui-version="v2"] .nav-item__badge.muted {
  background: rgba(255,255,255,0.12);
  color: rgba(255,255,255,0.75);
}
/* The nav-item is a flex container so badge flushes right via margin-left:auto. */
html[data-ui-version="v2"] .nav-item {
  display: flex;
  align-items: center;
}
/* On active items the white-on-white badge needs a translucent surface. */
html[data-ui-version="v2"] .nav-item.active .nav-item__badge {
  background: rgba(255,255,255,0.24);
  color: #fff;
}

/* ── 2 · Bills page hero (desktop v2) ───────────────────────────────── */
/* Breadcrumb + display title + meta line. Mirrors mockup Section 01 page-head. */
html[data-ui-version="v2"] .v2-page-hero {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding-bottom: var(--sp-4, 16px);
  margin-bottom: var(--sp-4, 16px);
  border-bottom: 1px solid var(--line, #ebe7df);
}
html[data-ui-version="v2"] .v2-page-hero__crumbs {
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted, #8a857d);
}
html[data-ui-version="v2"] .v2-page-hero__title {
  margin: 2px 0 0;
  font-size: 28px;
  font-weight: 700;
  letter-spacing: -0.02em;
  line-height: 1.15;
  color: var(--text, #1c1917);
}
html[data-ui-version="v2"] .v2-page-hero__meta {
  margin-top: 4px;
  font-size: 14px;
  color: var(--muted, #8a857d);
  font-variant-numeric: tabular-nums;
}

/* ── 3 · Bills tabs · pure underline (scoped to #module-workspace to
   win specificity over any late-loaded rules). Mirrors the Lot Report
   override above so v2 tab bars read as a single coherent pattern. ─── */
html[data-ui-version="v2"] #module-workspace .lot-rpt-tab {
  background: transparent;
  border: 0;
  padding: 8px 14px;
  color: var(--text-secondary, #57534e);
  border-radius: 0;
}
html[data-ui-version="v2"] #module-workspace .lot-rpt-tab:hover:not(.active) {
  background: transparent;
  border-color: transparent;
  color: var(--text, #1c1917);
}
html[data-ui-version="v2"] #module-workspace .lot-rpt-tab.active {
  background: transparent;
  border-color: transparent;
  color: var(--text, #1c1917);
  font-weight: 600;
}

/* ── 4 · Status badge dots ─────────────────────────────────────────── */
/* Mockup shows every .badge as "● Label" with a small colored dot that
   inherits currentColor. Scoped so v1 pills stay dotless. */
html[data-ui-version="v2"] .badge::before {
  content: "";
  display: inline-block;
  width: 6px;
  height: 6px;
  border-radius: 999px;
  background: currentColor;
  margin-right: 6px;
  vertical-align: middle;
  opacity: 0.85;
}

/* ── 5 · Program detail hero-row flex layout ───────────────────────── */
/* Place the 4-stat block to the right of the title + chips instead of
   stacking it below. Wraps on narrow widths so mobile keeps the stacked
   behaviour automatically via flex-wrap. */
html[data-ui-version="v2"] .v2-hero-row {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--sp-5, 20px);
  flex-wrap: wrap;
}
html[data-ui-version="v2"] .v2-hero-row__title-block {
  flex: 1 1 auto;
  min-width: 0;
}
html[data-ui-version="v2"] .v2-hero-row .detail-hero-stats {
  flex: 0 0 auto;
  flex-shrink: 0;
  display: flex;
  flex-wrap: nowrap;
  gap: var(--sp-4, 16px);
  padding: 0;
  margin-bottom: 0;
  border-bottom: 0;
  align-self: flex-start;
}
/* On mobile the stats drop below the title naturally thanks to
   flex-wrap; restore the bottom border so they still feel like a
   distinct section when stacked. */
@media (max-width: 720px) {
  html[data-ui-version="v2"] .v2-hero-row .detail-hero-stats {
    flex: 1 1 100%;
    padding-top: var(--sp-3, 12px);
    border-top: 1px solid var(--line, #ebe7df);
  }
}

/* ═══════════════════════════════════════════════════════════════════════
   v19.0 · Unified palette sweep — catch every hardcoded surface so the
   whole app reads as one coherent v2 theme. Every rule scoped to v2.
   User feedback: "every inch of app should match every pixel should be
   smooth, color palette should be unified like mock".
   ═══════════════════════════════════════════════════════════════════════ */

/* Every panel / card / modal / sheet / dropdown uses warm surface. */
html[data-ui-version="v2"] .panel,
html[data-ui-version="v2"] .card,
html[data-ui-version="v2"] .detail-card,
html[data-ui-version="v2"] .section-card,
html[data-ui-version="v2"] .modal-dialog,
html[data-ui-version="v2"] .drawer-panel,
html[data-ui-version="v2"] .sheet-mobile,
html[data-ui-version="v2"] .ph-card,
html[data-ui-version="v2"] .lot-rpt-lot-card,
html[data-ui-version="v2"] #notif-dropdown,
html[data-ui-version="v2"] .cmd-palette {
  background: var(--surface-raised, #ffffff);
}

/* Table surfaces + mobile card rows use ivory, not pure white. */
html[data-ui-version="v2"] .table-wrap,
html[data-ui-version="v2"] .detail-table-wrap {
  background: var(--surface-raised, #ffffff);
}

/* Mobile card pattern (at styles.css:1638 / 1719 / 1738) currently hardcodes
   #fff — force v2 rows onto surface-raised. */
@media (max-width: 768px) {
  html[data-ui-version="v2"] table.table tbody tr,
  html[data-ui-version="v2"] table.detail-table tbody tr,
  html[data-ui-version="v2"] tbody tr {
    background: var(--surface-raised, #ffffff);
  }
  html[data-ui-version="v2"] .card-expand-toolbar button:active {
    background: var(--surface-sunken, #f4f2ed);
  }
}

/* Main workspace + content area use the app background. */
html[data-ui-version="v2"] body,
html[data-ui-version="v2"] .layout,
html[data-ui-version="v2"] .main,
html[data-ui-version="v2"] #module-workspace {
  background: var(--bg, #fbfaf8);
}

/* Main header uses surface (slightly whiter than bg) for a subtle lift. */
html[data-ui-version="v2"] .main-header {
  background: var(--surface-raised, #ffffff);
  border-bottom: 1px solid var(--line, #ebe7df);
}

/* v2 search bar — big rounded pill per mockup Section 7b.
   Input grows to fill toolbar space, icon on left, ⌘K kbd on right,
   soft shadow-xs lift, warm ivory focus ring. */
html[data-ui-version="v2"] .search-bar {
  flex: 1 1 420px;
  max-width: 560px;
}
html[data-ui-version="v2"] #search-input {
  border-radius: var(--radius-full, 999px);
  background: var(--surface, #fbfaf8);
  border: 1px solid var(--line, #ebe7df);
  padding: 10px 92px 10px 38px;
  font-size: var(--text-base, 0.95rem);
  color: var(--text, #0f0e0c);
  box-shadow: var(--shadow-xs, 0 1px 2px rgba(15,14,12,0.04));
  transition: border-color 120ms ease, box-shadow 120ms ease, background 120ms ease;
}
html[data-ui-version="v2"] #search-input::placeholder {
  color: var(--text-tertiary, #8c8880);
}
html[data-ui-version="v2"] #search-input:focus {
  background: var(--surface-raised, #ffffff);
  border-color: var(--primary, #4f46e5);
  box-shadow: 0 0 0 3px var(--primary-glow, rgba(79,70,229,0.18));
}
html[data-ui-version="v2"] .search-bar__icon {
  color: var(--text-tertiary, #8c8880);
}
html[data-ui-version="v2"] .search-bar__kbd {
  font-family: var(--font-mono, ui-monospace, SFMono-Regular, monospace);
  font-size: 11px;
  font-weight: 500;
  color: var(--text-secondary, #4b4843);
  background: var(--surface-sunken, #f4f2ed);
  border: 1px solid var(--line, #ebe7df);
  border-radius: 5px;
  padding: 2px 6px;
  box-shadow: 0 1px 0 rgba(15,14,12,0.04);
}
html[data-ui-version="v2"] .search-bar__badge {
  background: var(--surface-sunken, #f4f2ed);
  color: var(--text-secondary, #4b4843);
}

/* Mobile: drop the ⌘K kbd (no keyboard); shrink right padding. */
@media (max-width: 768px) {
  html[data-ui-version="v2"] .search-bar__kbd { display: none !important; }
  html[data-ui-version="v2"] #search-input { padding-right: 40px; }
  html[data-ui-version="v2"] .search-bar__badge { right: 12px; }
}

/* Form inputs: warmer focus ring, warmer line. */
html[data-ui-version="v2"] input[type="text"],
html[data-ui-version="v2"] input[type="number"],
html[data-ui-version="v2"] input[type="date"],
html[data-ui-version="v2"] input[type="search"],
html[data-ui-version="v2"] input[type="email"],
html[data-ui-version="v2"] input[type="password"],
html[data-ui-version="v2"] select,
html[data-ui-version="v2"] textarea {
  background: var(--surface-raised, #ffffff);
  color: var(--text, #0f0e0c);
  border: 1px solid var(--line, #ebe7df);
  border-radius: var(--radius-sm, 8px);
}
html[data-ui-version="v2"] input:focus,
html[data-ui-version="v2"] select:focus,
html[data-ui-version="v2"] textarea:focus {
  border-color: var(--primary, #4f46e5);
  outline: none;
  box-shadow: 0 0 0 3px var(--primary-glow, rgba(79,70,229,0.18));
}

/* Buttons: primary = indigo flat; ghost = subtle warm. */
html[data-ui-version="v2"] .btn {
  border-radius: var(--radius-sm, 8px);
  font-weight: 500;
  transition: background var(--dur-fast, 120ms) var(--ease-out, ease),
              color var(--dur-fast, 120ms) var(--ease-out, ease),
              border-color var(--dur-fast, 120ms) var(--ease-out, ease);
}
html[data-ui-version="v2"] .btn.primary {
  background: var(--primary, #4f46e5);
  color: #ffffff;
  border-color: var(--primary, #4f46e5);
}
html[data-ui-version="v2"] .btn.primary:hover {
  background: var(--primary-hover, #4338ca);
}
html[data-ui-version="v2"] .btn:not(.primary):not(.danger):not(.success):not(.ghost) {
  background: var(--surface-raised, #ffffff);
  color: var(--text, #0f0e0c);
  border: 1px solid var(--line, #ebe7df);
}
html[data-ui-version="v2"] .btn:not(.primary):not(.danger):not(.success):not(.ghost):hover {
  background: var(--surface-sunken, #f4f2ed);
  border-color: var(--line-strong, #d9d3c7);
}
html[data-ui-version="v2"] .btn.ghost {
  background: transparent;
  color: var(--text-secondary, #4b4843);
  border-color: transparent;
}
html[data-ui-version="v2"] .btn.ghost:hover {
  background: var(--surface-sunken, #f4f2ed);
  color: var(--text, #0f0e0c);
}

/* Dropdowns + popovers (search suggest · notif-dropdown · cmd-palette). */
html[data-ui-version="v2"] .search-suggest,
html[data-ui-version="v2"] #notif-dropdown,
html[data-ui-version="v2"] .cmd-palette__list,
html[data-ui-version="v2"] .module-dropdown {
  background: var(--surface-popover, #ffffff);
  border: 1px solid var(--line, #ebe7df);
  box-shadow: var(--shadow-lg, 0 12px 30px rgba(15,14,12,0.10));
}

/* Any remaining inline-style tr.style.background white rows get caught by
   the mobile selectors above. Desktop row backgrounds are driven by
   .detail-table tr rules at styles.css:~1930+ which already use surface. */

/* Consistent hover depth on all clickable cards. */
html[data-ui-version="v2"] .dash-rail-card,
html[data-ui-version="v2"] .dash-stat-card,
html[data-ui-version="v2"] .dash-action-card {
  background: var(--surface-raised, #ffffff);
  border: 1px solid var(--line, #ebe7df);
  transition: box-shadow var(--dur-base, 180ms) var(--ease-out, ease),
              transform var(--dur-base, 180ms) var(--ease-out, ease),
              border-color var(--dur-base, 180ms) var(--ease-out, ease);
}

/* Typography consistency — tighter display headings per mockup. */
html[data-ui-version="v2"] h1,
html[data-ui-version="v2"] h2,
html[data-ui-version="v2"] h3,
html[data-ui-version="v2"] .detail-title,
html[data-ui-version="v2"] .v2-page-hero__title,
html[data-ui-version="v2"] .dash-hero__title {
  letter-spacing: -0.02em;
  font-weight: 600;
  color: var(--text, #0f0e0c);
}

/* Number display (tabular everywhere money/meters appear). */
html[data-ui-version="v2"] .dash-stat-card__value,
html[data-ui-version="v2"] .detail-hero-stats .value,
html[data-ui-version="v2"] .rail-card__art,
html[data-ui-version="v2"] td.num,
html[data-ui-version="v2"] td[align="right"] {
  font-variant-numeric: tabular-nums;
}

@media (prefers-reduced-motion: reduce) {
  html[data-ui-version="v2"] .btn,
  html[data-ui-version="v2"] .dash-rail-card,
  html[data-ui-version="v2"] .dash-stat-card,
  html[data-ui-version="v2"] .dash-action-card {
    transition: none !important;
  }
}

/* ═══════════════════════════════════════════════════════════════════════
   v2 · Unify all sub-tab strips as chip-style (every module matches Bills).
   Overrides earlier pure-underline fidelity rules AND v1 pill+underline.
   Covers Programs · Receiving · Invoices · Lot Report · Shortage · Approvals.
   ═══════════════════════════════════════════════════════════════════════ */

/* Container becomes a horizontal chip row with breathing space. */
html[data-ui-version="v2"] .lot-rpt-filter,
html[data-ui-version="v2"] #module-workspace .lot-rpt-filter {
  display: flex !important;
  flex-wrap: wrap;
  gap: var(--sp-2, 8px);
  padding: var(--sp-2, 8px) 0 var(--sp-4, 16px) !important;
  border-bottom: none !important;
  margin-bottom: var(--sp-3, 12px);
  overflow-x: auto;
  scrollbar-width: none;
}
html[data-ui-version="v2"] .lot-rpt-filter::-webkit-scrollbar { display: none; }

/* Each tab becomes a pill chip. */
html[data-ui-version="v2"] .lot-rpt-tab,
html[data-ui-version="v2"] #module-workspace .lot-rpt-tab {
  display: inline-flex !important;
  align-items: center;
  flex-shrink: 0;
  padding: 6px 14px !important;
  border-radius: var(--radius-full, 999px) !important;
  background: var(--surface-sunken, #f4f2ed) !important;
  color: var(--text-secondary, #4b4843) !important;
  border: 1px solid transparent !important;
  font-weight: 500;
  font-size: var(--text-sm, 13px);
  line-height: 1.3;
  cursor: pointer;
  white-space: nowrap;
  transition: background var(--dur-fast, 120ms) var(--ease-out, ease),
              color var(--dur-fast, 120ms) var(--ease-out, ease),
              box-shadow var(--dur-base, 180ms) var(--ease-out, ease),
              transform var(--dur-fast, 120ms) var(--ease-out, ease);
}
html[data-ui-version="v2"] .lot-rpt-tab:hover {
  background: color-mix(in srgb, var(--text, #0f0e0c) 8%, var(--surface, #fbfaf8)) !important;
  color: var(--text, #0f0e0c) !important;
  border-color: var(--line-strong, #d9d3c7) !important;
}
html[data-ui-version="v2"] .lot-rpt-tab:active {
  transform: scale(0.97);
}
html[data-ui-version="v2"] .lot-rpt-tab.active {
  background: var(--text, #0f0e0c) !important;
  color: var(--surface-raised, #ffffff) !important;
  border-color: var(--text, #0f0e0c) !important;
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--text, #0f0e0c) 18%, transparent);
}

/* Kill the animated underline — we're chip-style now, not underline. */
html[data-ui-version="v2"] .lot-rpt-tab::after,
html[data-ui-version="v2"] .lot-rpt-tab.active::after {
  display: none !important;
  content: none !important;
}

/* Receiving + Invoices often have their own mini sub-nav — same treatment. */
html[data-ui-version="v2"] .subnav,
html[data-ui-version="v2"] .nav-tabs {
  display: flex;
  gap: var(--sp-2, 8px);
  flex-wrap: wrap;
  padding: var(--sp-2, 8px) 0 var(--sp-4, 16px);
  border-bottom: none;
}
html[data-ui-version="v2"] .subnav > *,
html[data-ui-version="v2"] .nav-tabs > * {
  padding: 6px 14px;
  border-radius: var(--radius-full, 999px);
  background: var(--surface-sunken, #f4f2ed);
  color: var(--text-secondary, #4b4843);
  border: 1px solid transparent;
}
html[data-ui-version="v2"] .subnav > .active,
html[data-ui-version="v2"] .nav-tabs > .active {
  background: var(--text, #0f0e0c);
  color: var(--surface-raised, #ffffff);
}

/* ─────────────────────────────────────────────────────────────────────
 * v20.3 · Shift Wrapped (Spotify Wrapped-style end-of-shift card)
 *
 * Full-screen sheet on mobile; 560 × 640 px centered card on desktop. The
 * container is attached as a direct child of `.modal-dialog` via the
 * modal primitive, with the `.shift-wrapped__dialog` flag to relax the
 * dialog's default width. Reuses v2 tokens throughout; falls back to
 * legacy tokens under v1 via var() defaults.
 * ────────────────────────────────────────────────────────────────── */
.modal-dialog.shift-wrapped__dialog {
  max-width: 560px;
  padding: 0;
  overflow: hidden;
}
.shift-wrapped {
  display: flex;
  flex-direction: column;
  gap: var(--sp-4, 16px);
  padding: var(--sp-5, 24px);
  min-height: 480px;
}
.shift-wrapped__head {
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.shift-wrapped__title {
  font-family: "DM Sans", ui-sans-serif, system-ui, sans-serif;
  font-weight: 700;
  font-size: 22px;
  line-height: 1.2;
  letter-spacing: -0.01em;
  color: var(--text, #0f0e0c);
  margin: 0;
}
.shift-wrapped__subtitle {
  font-size: 13px;
  color: var(--text-tertiary, var(--muted, #8c8880));
  letter-spacing: 0.01em;
}
.shift-wrapped__time {
  font-family: "JetBrains Mono", ui-monospace, "SF Mono", Menlo, monospace;
  font-variant-numeric: tabular-nums;
  color: var(--text-secondary, #4b4843);
}
/* v20.3 hero — single big number for meters handled, anchors the card. */
.shift-wrapped__hero {
  position: relative;
  margin-top: var(--sp-2, 8px);
  padding: var(--sp-5, 24px) var(--sp-4, 16px);
  border-radius: 16px;
  background: var(--surface-raised, var(--surface, #ffffff));
  background:
    radial-gradient(120% 140% at 50% 0%,
      color-mix(in srgb, var(--primary, #4f46e5) 18%, transparent) 0%,
      transparent 60%),
    var(--surface-raised, var(--surface, #ffffff));
  border: 1px solid var(--line, #ebe7df);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 6px;
  overflow: hidden;
}
.shift-wrapped__hero-value {
  font-family: "DM Sans", ui-sans-serif, system-ui, sans-serif;
  font-weight: 700;
  font-size: 64px;
  line-height: 1;
  letter-spacing: -0.03em;
  color: var(--text, #0f0e0c);
  font-variant-numeric: tabular-nums;
}
.shift-wrapped__hero-label {
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--text-tertiary, var(--muted, #78716c));
}

/* Two equal supporting cells below the hero — kaam + browse. */
.shift-wrapped__minigrid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--sp-3, 12px);
}
.shift-wrapped__minigrid .shift-wrapped__value {
  font-size: 32px;
}
.shift-wrapped__minigrid .shift-wrapped__stat {
  min-height: 84px;
}

/* Conditional shortage pill — only rendered when shortagesFlagged > 0. */
.shift-wrapped__alert {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 10px 14px;
  border-radius: 12px;
  background: rgba(220, 38, 38, 0.08);
  background: color-mix(in srgb, var(--danger, #dc2626) 8%, transparent);
  border: 1px solid rgba(220, 38, 38, 0.28);
  border-color: color-mix(in srgb, var(--danger, #dc2626) 28%, transparent);
  color: var(--danger, #dc2626);
  font-size: 13px;
  font-weight: 600;
}
.shift-wrapped__alert-icon {
  font-size: 16px;
  line-height: 1;
}
.shift-wrapped__stat {
  position: relative;
  min-height: 100px;
  padding: 18px 16px 16px 19px;
  background: var(--surface-raised, var(--surface, #ffffff));
  border: 1px solid var(--line, #ebe7df);
  border-radius: 12px;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 4px;
  overflow: hidden;
}
.shift-wrapped__stat::before {
  content: "";
  position: absolute;
  left: 0; top: 0; bottom: 0;
  width: 3px;
  background: var(--muted, #8c8880);
  border-radius: 3px 0 0 3px;
}
.shift-wrapped__stat--accent-success::before { background: var(--success, #16a34a); }
.shift-wrapped__stat--accent-primary::before { background: var(--primary, #4f46e5); }
.shift-wrapped__stat--accent-danger::before  { background: var(--danger, #dc2626); }
.shift-wrapped__stat--accent-muted::before   { background: var(--muted, #8c8880); }
.shift-wrapped__eyebrow {
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-tertiary, var(--muted, #78716c));
}
.shift-wrapped__value {
  font-family: "DM Sans", ui-sans-serif, system-ui, sans-serif;
  font-weight: 700;
  font-size: 40px;
  line-height: 1;
  letter-spacing: -0.02em;
  color: var(--text, #0f0e0c);
  font-variant-numeric: tabular-nums;
}
.shift-wrapped__stat--anim {
  opacity: 0;
  transform: translateY(8px);
  animation: shift-wrapped-stat-in 360ms var(--ease-out, cubic-bezier(0.2, 0.8, 0.2, 1)) forwards;
}
@keyframes shift-wrapped-stat-in {
  from { opacity: 0; transform: translateY(8px); }
  to   { opacity: 1; transform: none; }
}
@media (prefers-reduced-motion: reduce) {
  .shift-wrapped__stat--anim {
    animation: none;
    opacity: 1;
    transform: none;
  }
}
.shift-wrapped__encourage {
  font-size: 14px;
  color: var(--text-secondary, var(--muted, #4b4843));
  margin-top: var(--sp-1, 4px);
  display: flex;
  align-items: center;
  gap: 6px;
}
.shift-wrapped__emoji {
  font-size: 18px;
  line-height: 1;
}
.shift-wrapped__footnote {
  font-size: 11px;
  color: var(--text-tertiary, var(--muted, #8c8880));
  font-style: italic;
  margin-top: -4px;
}
.shift-wrapped__empty {
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  color: var(--text-secondary, var(--muted, #4b4843));
  font-size: 15px;
  padding: var(--sp-6, 32px) var(--sp-4, 16px);
}
.shift-wrapped__actions {
  display: flex;
  gap: var(--sp-2, 8px);
  margin-top: auto;
  padding-top: var(--sp-2, 8px);
}
.shift-wrapped__btn {
  height: 48px;
  flex: 1;
  border-radius: 24px;
  border: 1px solid transparent;
  font-weight: 600;
  font-size: 14px;
  cursor: pointer;
  transition: transform 120ms ease, background 160ms ease;
}
.shift-wrapped__btn:active { transform: scale(0.98); }
.shift-wrapped__btn--primary {
  background: var(--primary, #4f46e5);
  color: #fff;
}
.shift-wrapped__btn--primary:hover {
  background: var(--primary-hover, #4338ca);
}
.shift-wrapped__btn--ghost {
  background: transparent;
  color: var(--text-secondary, #4b4843);
  border-color: var(--line, #ebe7df);
}
.shift-wrapped__btn--ghost:hover {
  background: var(--surface-sunken, #f4f2ed);
}

@media (max-width: 640px) {
  /* Modal primitive already slides the dialog to bottom on mobile; we just
     stretch it to fill the viewport for the Wrapped card. */
  .modal-dialog.shift-wrapped__dialog {
    border-radius: 20px 20px 0 0;
    max-width: 100%;
    min-height: 100dvh;
  }
  .shift-wrapped {
    min-height: calc(100dvh - 20px);
    padding-bottom: calc(var(--sp-5, 24px) + env(safe-area-inset-bottom));
  }
  .shift-wrapped__hero-value { font-size: 56px; }
  .shift-wrapped__minigrid .shift-wrapped__value { font-size: 28px; }
}

/* Sidebar "Wrap up shift" trigger — appears below the version badge.
   Tonal treatment adapts to both palettes via var() fallbacks. */
.sidebar-wrap-btn {
  display: block;
  width: calc(100% - 24px);
  margin: 4px 12px 10px;
  padding: 6px 10px;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  background: transparent;
  color: rgba(255, 255, 255, 0.6);
  border: 1px solid rgba(255, 255, 255, 0.15);
  border-radius: 6px;
  cursor: pointer;
  text-align: left;
  transition: background 140ms ease, color 140ms ease, border-color 140ms ease;
}
.sidebar-wrap-btn:hover {
  background: rgba(255, 255, 255, 0.08);
  color: rgba(255, 255, 255, 0.92);
  border-color: rgba(255, 255, 255, 0.24);
}
html[data-ui-version="v2"] .sidebar-wrap-btn {
  color: var(--text-tertiary, #8c8880);
  border-color: var(--line, #ebe7df);
  background: transparent;
}
html[data-ui-version="v2"] .sidebar-wrap-btn:hover {
  color: var(--text, #0f0e0c);
  background: var(--surface-sunken, #f4f2ed);
  border-color: var(--primary, #4f46e5);
}

/* ── Filter chip row (Tadabase-style; ported from dyeing ui-mockup) ────── */
.chip-row {
  display: flex;
  gap: var(--sp-2, 8px);
  overflow-x: auto;
  padding: var(--sp-2, 8px) 0 var(--sp-3, 12px);
  margin-bottom: var(--sp-2, 8px);
  scrollbar-width: thin;
  scroll-snap-type: x proximity;
}
.chip-row::-webkit-scrollbar { height: 4px; }
.chip-row::-webkit-scrollbar-thumb { background: var(--line); border-radius: 999px; }
.chip {
  --chip-c: var(--text-secondary);
  --chip-soft: var(--surface-muted);
  flex-shrink: 0;
  padding: 6px 14px;
  border-radius: 999px;
  background: var(--chip-soft);
  color: var(--chip-c);
  border: 1px solid transparent;
  font: inherit;
  font-size: 13px;
  font-weight: 600;
  cursor: pointer;
  transition: background 150ms ease, color 150ms ease, box-shadow 180ms ease, transform 120ms ease;
  scroll-snap-align: start;
  white-space: nowrap;
}
.chip:hover {
  background: color-mix(in srgb, var(--chip-c) 12%, var(--surface));
  border-color: color-mix(in srgb, var(--chip-c) 28%, transparent);
  color: var(--chip-c);
}
.chip:active { transform: scale(0.97); }
.chip.active {
  background: var(--chip-c);
  color: #ffffff;
  border-color: var(--chip-c);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--chip-c) 22%, transparent);
}
.chip.active:hover {
  background: color-mix(in srgb, var(--chip-c) 88%, #000);
  color: #fff;
}
.chip__count {
  margin-left: 6px;
  opacity: 0.75;
  font-weight: 500;
  font-variant-numeric: tabular-nums;
}
/* Variants */
.chip--all,
.chip--default { --chip-c: var(--primary);   --chip-soft: var(--primary-light); }
.chip--success { --chip-c: var(--success);   --chip-soft: var(--success-light); }
.chip--primary { --chip-c: var(--primary);   --chip-soft: var(--primary-light); }
.chip--info    { --chip-c: var(--info);      --chip-soft: var(--info-light, var(--primary-light)); }
.chip--amber   { --chip-c: var(--amber);     --chip-soft: var(--amber-light); }
.chip--danger  { --chip-c: var(--danger);    --chip-soft: var(--danger-light); }
.chip--muted   { --chip-c: var(--muted);     --chip-soft: var(--surface-muted); }

/* ═══════════════════════════════════════════════════════════════════════
   Mobile bottom nav + FAB (ported from dyeing app, 2026-05-01)
   - Visible only on mobile / coarse-pointer devices.
   - Hidden on desktops with hover (>=1081px).
   - 5-column grid: 4 module shortcuts + a "More" toggle for the sidebar.
   - FAB sits 76px above the nav, respects iPhone safe-area insets.
   ═══════════════════════════════════════════════════════════════════════ */
.bottom-nav {
  position: fixed;
  left: 0; right: 0; bottom: 0;
  z-index: 100;
  display: grid;
  grid-template-columns: repeat(5, 1fr);
  background: var(--surface);
  border-top: 1px solid var(--line);
  padding: 8px 4px calc(8px + env(safe-area-inset-bottom));
  box-shadow: 0 -4px 16px rgba(0,0,0,0.04);
  /* NO transform / translateZ / will-change here. Promoting a position:fixed
     bar to its own compositor layer is a documented iOS-standalone drift
     trigger (the bar is left at stale coords mid-screen). The nav now stays in
     normal fixed flow; app.js `_repinBottomNav()` actively re-anchors it if iOS
     ever drifts it — covering both the mid-screen drift and the old "stuck
     after goBack" symptom this GPU-pin used to mask. */
}
.bottom-nav button {
  background: none;
  border: none;
  cursor: pointer;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 2px;
  padding: 4px 2px;
  min-height: 52px;
  font-size: 11px;
  font-weight: 600;
  color: var(--muted);
  transition: color var(--t-press, 150ms ease), transform var(--t-press, 150ms ease);
}
.bottom-nav button .icon,
.bottom-nav button svg { line-height: 1; }
.bottom-nav button.active,
.bottom-nav button[aria-current="page"] { color: var(--primary); }
.bottom-nav button:active { transform: scale(0.94); }

.fab {
  position: fixed;
  z-index: 90;
  right: 16px;
  bottom: calc(76px + env(safe-area-inset-bottom));
  width: 56px; height: 56px;
  border-radius: 50%;
  background: var(--primary);
  color: #fff;
  border: none;
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow: var(--shadow-lg);
  cursor: pointer;
  transition: transform var(--t-press, 150ms ease), box-shadow var(--t-press, 150ms ease);
  /* No translateZ / will-change (same iOS drift reason as .bottom-nav); the FAB
     is re-anchored by _repinBottomNav() alongside the nav. */
}
.fab:active { transform: scale(0.92); box-shadow: var(--shadow-md); }
.fab[hidden] { display: none !important; }

/* Mobile-only surfacing.
   Show on small viewports OR coarse-pointer devices (tablets, phones).
   When the bottom-nav is visible, lift the workspace so its content is
   not hidden behind the bar (76px nav + safe-area padding). */
@media (max-width: 1080px) and (hover: none), (max-width: 768px) {
  body.has-bottom-nav #module-workspace { padding-bottom: calc(132px + env(safe-area-inset-bottom)); }
  body.has-bottom-nav .main { padding-bottom: calc(76px + env(safe-area-inset-bottom)); }
  /* The "More" button uses the existing sidebar slide-in. The sidebar's
     own @media (max-width: 1080px) rule already translates it off-screen;
     toggling .sidebar-open slides it back in. */
}

/* Hide the bottom-nav and FAB on real desktops (hover-capable, wide). */
@media (min-width: 1081px) and (hover: hover) {
  .bottom-nav, .fab { display: none !important; }
}

.chip--clear   { --chip-c: var(--muted);     --chip-soft: transparent; border-color: var(--line); }

/* Form select fallback (since makeInput doesn't cover <select>) */
.lst-form-input {
  padding: 10px 12px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm, 6px);
  background: var(--surface);
  color: var(--text);
  font: inherit;
}
.lst-form-input:focus {
  outline: 2px solid var(--primary);
  outline-offset: 1px;
}

/* Fabric Issues timeline view */
.tl-group        { margin: 24px 0 12px; }
.tl-group__head  { font-family: "Instrument Serif", Georgia, serif; font-size: 1.4rem; color: var(--text-secondary); margin: 0 0 10px; padding: 0 4px; }
.tl-cards        { display: grid; gap: 8px; }
.tl-card         { background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius, 12px); padding: 12px 14px; box-shadow: var(--shadow-xs); display: grid; grid-template-columns: 1fr auto; align-items: center; gap: 10px; }
.tl-card__main   { display: grid; gap: 2px; min-width: 0; }
.tl-card__title  { font-weight: 600; color: var(--text); }
.tl-card__title strong { color: var(--primary); }
.tl-card__sub    { font-size: .85rem; color: var(--muted); }
.tl-card__del    { background: transparent; border: 0; color: var(--muted); cursor: pointer; padding: 6px 8px; border-radius: 6px; }
.tl-card__del:hover { background: var(--surface-sunken); color: var(--danger); }

/* Command palette (⌘K / Ctrl+K) — appended; built by installCommandPalette()
   in app.js. The earlier .cmd-palette block (~line 3855) targets a
   different layout (.modal-dialog.cmd-palette) and stays untouched. */
.cmd-palette { display: flex; flex-direction: column; min-height: 200px; }
.cmd-palette__input { width: 100%; padding: 14px 18px; border: 0; border-bottom: 1px solid var(--line); font-size: 1rem; outline: none; background: transparent; color: var(--text); font-family: inherit; }
.cmd-palette__list { list-style: none; margin: 0; padding: 8px; max-height: 60vh; overflow-y: auto; }
.cmd-palette__item { display: flex; align-items: center; gap: 12px; padding: 10px 12px; border-radius: 8px; cursor: pointer; color: var(--text); }
.cmd-palette__item.is-active { background: var(--primary-light); color: var(--text); }
.cmd-palette__item-main { display: grid; gap: 2px; flex: 1; min-width: 0; }
.cmd-palette__item-title { font-weight: 500; }
.cmd-palette__item-sub { font-size: .8rem; color: var(--muted); }
.cmd-palette__item-kbd { font-family: "JetBrains Mono", monospace; font-size: .75rem; color: var(--muted); }
.cmd-palette__empty { padding: 32px 16px; color: var(--muted); text-align: center; font-size: .9rem; }

/* ─── Order items kanban board ──────────────────────────────────────── */
.kb-board {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: 280px;
  gap: 12px;
  padding: 16px 4px;
  overflow-x: auto;
  overscroll-behavior-x: contain;
  -webkit-overflow-scrolling: touch;
}
.kb-col {
  background: var(--surface-sunken);
  border-radius: var(--radius);
  padding: 12px;
  display: flex;
  flex-direction: column;
  gap: 8px;
  max-height: calc(100dvh - 320px);
}
.kb-col__head {
  display: flex;
  justify-content: space-between;
  align-items: center;
  font-size: .85rem;
  text-transform: uppercase;
  letter-spacing: .04em;
  font-weight: 600;
  color: var(--text-secondary);
  padding: 4px 6px 8px;
  border-top: 4px solid var(--accent, var(--muted));
  border-radius: 4px 4px 0 0;
}
.kb-col__count {
  background: var(--surface);
  color: var(--muted);
  border-radius: 999px;
  padding: 1px 8px;
  font-size: .75rem;
  font-weight: 500;
}
.kb-col__body {
  display: flex;
  flex-direction: column;
  gap: 8px;
  overflow-y: auto;
}
.kb-card {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm, 6px);
  padding: 10px 12px;
  box-shadow: var(--shadow-xs);
  cursor: pointer;
  display: grid;
  gap: 4px;
  font-size: .85rem;
  transition: box-shadow 120ms ease, transform 120ms ease;
}
@media (hover: hover) and (pointer: fine) {
  .kb-card:hover { box-shadow: var(--shadow-sm); transform: translateY(-1px); }
}
.kb-card__title { font-weight: 600; color: var(--text); display: flex; justify-content: space-between; align-items: baseline; gap: 8px; }
.kb-card__title b { font-variant-numeric: tabular-nums; color: var(--text-secondary); font-weight: 500; }
.kb-card__sub   { color: var(--muted); font-size: .8rem; line-height: 1.35; }
.kb-card__yds   { font-variant-numeric: tabular-nums; color: var(--primary); font-weight: 500; }
/* Type row — shows UNIFORM / REPEAT pill on the kanban card so the
   priority-flag (UNIFORM) and re-buy-flag (REPEAT) are spottable at
   a glance, mirroring the same chips used on the list view. NEW
   rows render no pill (most rows). */
.kb-card__type-row {
  margin-top: 4px;
  display: flex;
  gap: 6px;
  flex-wrap: wrap;
}
.kb-card__type {
  display: inline-flex;
  align-items: center;
  padding: 2px 8px;
  border-radius: 999px;
  font-size: .7rem;
  font-weight: 700;
  letter-spacing: 0.02em;
  line-height: 1.4;
  white-space: nowrap;
}
.kb-card__type--uniform {
  background: var(--danger, #dc2626);
  color: #fff;
  box-shadow: 0 1px 2px rgba(220,38,38,.25);
}
.kb-card__type--repeat {
  background: rgba(99,102,241,.14);
  color: #4338ca;
  border: 1px solid rgba(99,102,241,.30);
  font-weight: 600;
}
html[data-theme="dark"] .kb-card__type--repeat {
  background: rgba(129,140,248,.16);
  color: #c7d2fe;
  border-color: rgba(129,140,248,.32);
}
/* UNIFORM cards get a subtle red left-edge accent so they stand out
   in a long Balance/Running column without making the whole card
   feel angry. */
.kb-card--uniform {
  box-shadow: inset 3px 0 0 0 var(--danger, #dc2626), 0 1px 2px rgba(0,0,0,.04);
}
.kb-col--balance { --accent: var(--amber); }
.kb-col--running { --accent: var(--success); }
.kb-col--ok      { --accent: var(--info); }
.kb-col--pending { --accent: var(--danger); }
.kb-col--export  { --accent: var(--muted); }
.kb-col--uniform { --accent: var(--accent-violet); }

/* View toggle pill (used by order_items list ↔ board switch) */
.view-toggle { display: inline-flex; border: 1px solid var(--line); border-radius: 999px; overflow: hidden; }
.view-toggle button {
  background: transparent; border: 0; padding: 6px 12px; font-size: .85rem; color: var(--muted); cursor: pointer;
  display: inline-flex; align-items: center; gap: 4px;
}
.view-toggle button.is-active { background: var(--primary-light); color: var(--primary); font-weight: 500; }

@media (max-width: 768px) {
  .kb-board { grid-auto-flow: row; grid-auto-columns: 1fr; }
  .kb-col   { max-height: none; }
  /* The List/Board toggle inside section-card__head was occasionally
   * dropping taps on iOS — buttons were ~32px tall, below the 44px
   * Apple HIG target. Bump to 44 and force pointer-events auto so a
   * stray ancestor rule can't swallow the tap. */
  .view-toggle button {
    min-height: 44px;
    padding: 8px 14px;
    pointer-events: auto !important;
  }
  .view-toggle button .icon { pointer-events: none; }
}

/* ────────────────────────────────────────────────────────────────────────
 * Mobile board-view flicker fix — 2026-05-06
 *
 * On iPhone, every tap on the board view caused visible flicker + page
 * scroll jumps. Audit traced this to two compounding GPU paint hazards:
 *
 *  (1) `.kb-card:active { transform: scale(0.97); transition: 120ms; }` —
 *      every tap promotes the card to its own GPU layer for the
 *      transition. Combined with…
 *
 *  (2) `.modal-backdrop { backdrop-filter: blur(6px) saturate(120%) }` —
 *      backdrop-filter on iOS recalculates for the entire viewport on
 *      every paint frame.
 *
 * Together they triggered a paint storm whenever a card was tapped (modal
 * open) or while the user just dragged through the board. Disabling both
 * on touch / mobile devices reverts to instant tap feedback (still has
 * iOS's native press-dim) and a cheap solid backdrop. Desktop unaffected.
 * ──────────────────────────────────────────────────────────────────── */
@media (hover: none) and (pointer: coarse) {
  .kb-card:active,
  .od-line-row:active,
  .be-card:active,
  /* Chips and buttons too — the scale-down on tap was causing the
   * page to jump up/down whenever the user tapped a filter chip
   * (Running / OK / Pending). Same paint-storm root cause as the
   * board view. iOS already dims pressed elements natively. */
  .chip:active,
  button:active,
  .btn:active,
  .nav-item:active {
    transform: none !important;
    transition: none !important;
  }
  .modal-backdrop {
    backdrop-filter: none !important;
    -webkit-backdrop-filter: none !important;
  }
}
@media (max-width: 768px) {
  /* Belt-and-braces: opacity-only backdrop on phones. */
  .modal-backdrop {
    backdrop-filter: none !important;
    -webkit-backdrop-filter: none !important;
  }
  /* Kanban board: contain overscroll so iOS doesn't bounce the document
   * when the user vertical-scrolls inside a tall column. */
  .kb-board { overscroll-behavior: contain; }
}

/* ─── Designs gallery view ─────────────────────────────────────────────── */
.dg-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
  gap: 12px;
  padding: 4px;
}
.dg-tile {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  overflow: hidden;
  cursor: pointer;
  transition: box-shadow 120ms ease, transform 120ms ease;
  display: flex;
  flex-direction: column;
}
.dg-tile:hover { box-shadow: var(--shadow-md); transform: translateY(-2px); }
.dg-tile__img-wrap {
  width: 100%;
  aspect-ratio: 1 / 1;
  background: var(--surface-sunken);
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  color: var(--muted);
  font-family: "Instrument Serif", Georgia, serif;
  font-size: 1.1rem;
}
.dg-tile__img-wrap img { width: 100%; height: 100%; object-fit: cover; display: block; }
.dg-tile__body { padding: 10px 12px; display: grid; gap: 2px; min-width: 0; }
.dg-tile__title { font-weight: 600; color: var(--text); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.dg-tile__sub  { font-size: .78rem; color: var(--muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.dg-tile__chip { font-size: .7rem; color: var(--muted); }

/* List ↔ Gallery segmented control (Designs module). */
.dg-view-toggle {
  display: inline-flex;
  border: 1px solid var(--line);
  border-radius: 999px;
  overflow: hidden;
  background: var(--surface);
}
.dg-view-toggle__btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 6px 10px;
  background: transparent;
  border: 0;
  color: var(--muted);
  font: inherit;
  font-size: .82rem;
  cursor: pointer;
  transition: background 120ms ease, color 120ms ease;
}
.dg-view-toggle__btn + .dg-view-toggle__btn { border-left: 1px solid var(--line); }
.dg-view-toggle__btn:hover { background: var(--surface-sunken); color: var(--text); }
.dg-view-toggle__btn.is-active { background: var(--surface-sunken); color: var(--text); font-weight: 600; }
.dg-view-toggle__glyph { font-size: .9rem; line-height: 1; }

@media (max-width: 768px) {
  .dg-grid { grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); gap: 8px; }
}

/* Compact density mode — rows are smaller, padding is tighter.
   Default = comfortable (44px rows). Compact ≈ 32px rows.
   Toggled via <html data-density="compact">. */
html[data-density="compact"] table th,
html[data-density="compact"] table td { padding: 6px 8px !important; line-height: 1.3; }
html[data-density="compact"] .chip { padding: 3px 10px; font-size: 12px; }
html[data-density="compact"] .section-card__head { padding-top: 14px; padding-bottom: 14px; }
html[data-density="compact"] .kb-card { padding: 6px 8px; }
html[data-density="compact"] .tl-card { padding: 8px 10px; }
html[data-density="compact"] .dg-tile__body { padding: 6px 8px; }
html[data-density="compact"] #lst-search { padding-top: 6px; padding-bottom: 6px; }

/* ─── Microinteractions (2025 baseline) ─────────────────────────────
   1. Button press scale 0.97
   2. Row stagger fade-in on filter / page change (~120ms)
   3. Animated focus ring (smooth grow)
   4. Inline live validation colour (red→green as user types)
   5. Success checkmark draw-in for save actions
   ─────────────────────────────────────────────────────────────────── */

/* 1. Button press */
.btn, .chip, button[data-pg], button[data-view], button[data-chip-idx] {
  transition: transform 100ms cubic-bezier(0.2, 0, 0, 1.2),
              box-shadow 120ms ease,
              background-color 120ms ease,
              color 120ms ease;
}
.btn:active, .chip:active,
button[data-pg]:active, button[data-view]:active, button[data-chip-idx]:active {
  transform: scale(0.97);
}

/* 2. Row stagger fade-in (table tbody rows + kanban cards + tile grid + timeline cards).
 * Opacity-only — dropping the prior translateY(4px) eliminates any
 * perceived vertical motion during list reload. */
@keyframes row-fade-in {
  from { opacity: 0; }
  to   { opacity: 1; }
}
#lst-table-wrap > table > tbody > tr,
.kb-card,
.dg-tile,
.tl-card {
  animation: row-fade-in 200ms cubic-bezier(0.2, 0, 0, 1) backwards;
}
/* Stagger via :nth-child for the first 12 — beyond that, instant (cheap) */
#lst-table-wrap > table > tbody > tr:nth-child(1)  { animation-delay: 0ms; }
#lst-table-wrap > table > tbody > tr:nth-child(2)  { animation-delay: 18ms; }
#lst-table-wrap > table > tbody > tr:nth-child(3)  { animation-delay: 36ms; }
#lst-table-wrap > table > tbody > tr:nth-child(4)  { animation-delay: 54ms; }
#lst-table-wrap > table > tbody > tr:nth-child(5)  { animation-delay: 72ms; }
#lst-table-wrap > table > tbody > tr:nth-child(6)  { animation-delay: 90ms; }
#lst-table-wrap > table > tbody > tr:nth-child(7)  { animation-delay: 108ms; }
#lst-table-wrap > table > tbody > tr:nth-child(8)  { animation-delay: 126ms; }
#lst-table-wrap > table > tbody > tr:nth-child(n + 9) { animation-delay: 144ms; }

/* 3. Animated focus ring — grows in instead of popping */
@keyframes focus-grow {
  from { outline-offset: 0; outline-width: 1px; }
  to   { outline-offset: 2px; outline-width: 2px; }
}
*:focus-visible { animation: focus-grow 140ms cubic-bezier(0.2, 0, 0, 1) forwards; }

/* 4. Inline live validation — input gets red border while invalid, green when valid */
input:not(:placeholder-shown):invalid {
  border-color: var(--danger) !important;
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--danger) 15%, transparent);
}
input:not(:placeholder-shown):valid:required {
  border-color: var(--success);
}

/* 5. Success checkmark draw-in (used by submit-success state) */
@keyframes checkmark-draw {
  from { stroke-dashoffset: 24; }
  to   { stroke-dashoffset: 0; }
}
.checkmark-svg {
  display: inline-block; width: 18px; height: 18px; vertical-align: middle;
}
.checkmark-svg path {
  stroke: var(--success);
  stroke-width: 3;
  stroke-linecap: round;
  stroke-linejoin: round;
  fill: none;
  stroke-dasharray: 24;
  stroke-dashoffset: 24;
  animation: checkmark-draw 360ms cubic-bezier(0.2, 0, 0, 1) 60ms forwards;
}

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

/* Pager disabled state (FIX 7) — replaces inline opacity hack. */
#lst-pager .btn:disabled,
#dg-pager .btn:disabled { opacity:.45; cursor:default; pointer-events:none; }

/* Filter banner back-link (FIX 10). */
.lst-nav-banner__back { color: var(--primary); cursor: pointer; text-decoration: underline; }

/* Global focus-visible ring (FIX 11). */
*:focus-visible {
  outline: 2px solid var(--primary);
  outline-offset: 2px;
  border-radius: 4px;
}
button:focus-visible, .btn:focus-visible, .chip:focus-visible {
  outline-offset: 2px;
}
input:focus-visible, select:focus-visible, textarea:focus-visible {
  outline-offset: 1px;
}

/* ── Sidebar Favorites + Recent + per-item star button ─────────────── */
.sidebar-section--dim .nav-item-wrap .nav-item { opacity: .85; }
.sidebar-section--dim .sidebar-label { opacity: .7; font-style: italic; }
.nav-item-wrap {
  display: flex; align-items: center; gap: 4px; padding-right: 6px;
}
.nav-item-wrap .nav-item { flex: 1; min-width: 0; }
.nav-fav {
  background: transparent; border: 0; cursor: pointer; padding: 4px;
  color: var(--muted); border-radius: 4px;
  transition: color 120ms ease, transform 120ms cubic-bezier(0.2, 0, 0, 1.4);
  display: inline-flex; align-items: center; justify-content: center;
}
.nav-item-wrap:hover .nav-fav { color: var(--text-secondary); }
.nav-fav:hover { color: var(--amber, #d97706) !important; transform: scale(1.15); }
.nav-fav:active { transform: scale(0.95); }

/* ─── Column pinning (Airtable-style) ────────────────────────────────────
   Extends .sticky-first to support up to 2 pinned cols. Activated when
   #lst-table-wrap has the .sticky-pinned class (added by renderTable when
   any column has _pinned: true). Disabled on phone — table just scrolls. */
@media (min-width: 769px) {
  #lst-table-wrap > table th[data-pinned], #lst-table-wrap > table td[data-pinned] {
    position: sticky;
    background: var(--surface);
    z-index: 5;
    box-shadow: 1px 0 0 var(--line);
  }
  #lst-table-wrap > table th[data-pinned-idx="0"], #lst-table-wrap > table td[data-pinned-idx="0"] { left: 0; }
  #lst-table-wrap > table th[data-pinned-idx="1"], #lst-table-wrap > table td[data-pinned-idx="1"] { left: var(--lst-pin-col-0-width, 120px); }
  #lst-table-wrap > table thead th[data-pinned] { background: var(--surface-sunken, var(--surface)); z-index: 6; }
}
@media (max-width: 768px) {
  /* On phone, disable pinning — table just scrolls. */
  #lst-table-wrap > table th[data-pinned], #lst-table-wrap > table td[data-pinned] {
    position: static; box-shadow: none;
  }
}

/* ─── Column prefs modal (Pin / Show / Drag-to-reorder) ────────────────── */
.col-prefs-modal { display: grid; gap: 8px; min-width: 300px; max-width: 380px; }
.col-prefs-list  { display: grid; gap: 2px; max-height: 60vh; overflow: auto; }
.col-prefs-row   {
  display: flex; align-items: center; gap: 8px;
  padding: 6px 8px; border-radius: 6px;
  cursor: grab; user-select: none;
  background: var(--surface);
  border: 1px solid transparent;
}
.col-prefs-row:hover { background: var(--surface-sunken); border-color: var(--line); }
.col-prefs-row.is-dragging { opacity: 0.5; }
.col-prefs-row__handle {
  color: var(--muted); font-family: monospace; cursor: grab;
  letter-spacing: -2px; padding: 0 2px;
}
.col-prefs-row__name  { flex: 1; font-size: .9rem; }
.col-prefs-row__chk   {
  display: inline-flex; align-items: center; gap: 4px;
  font-size: .8rem; color: var(--muted); cursor: pointer;
}
.col-prefs-row__chk input { margin: 0; cursor: pointer; }

/* Keyboard-focused row highlight */
#lst-table-wrap > table > tbody > tr.kbd-focus {
  background: var(--primary-light) !important;
  outline: 2px solid var(--primary);
  outline-offset: -2px;
}

/* Vaul-style bottom sheet on mobile.
   Replaces the centered modal with a slide-up panel that snaps to
   ~50% (peek) or 92% (full) of the viewport, with swipe-down to close. */
@media (max-width: 768px), (hover: none) and (pointer: coarse) {
  .modal-backdrop.modal-backdrop--sheet {
    align-items: flex-end !important;
    padding: 0 !important;
  }
  .modal-dialog.modal-dialog--sheet {
    width: 100% !important;
    max-width: 100% !important;
    border-radius: 18px 18px 0 0 !important;
    padding: 8px 18px calc(20px + env(safe-area-inset-bottom)) !important;
    max-height: 92dvh;
    transform: translateY(100%);
    /* Visual slide-up only. The pointer-events shield is now JS-driven via
       [data-pointer-armed="0"] (set in openModal), so it survives
       prefers-reduced-motion stripping CSS animations. See the rule below. */
    animation: sheet-slide-up 280ms cubic-bezier(0.2, 0, 0, 1) forwards;
    touch-action: pan-y;
    overflow-y: auto;
  }
  .modal-dialog.modal-dialog--sheet.is-closing {
    animation: sheet-slide-down 220ms ease forwards;
  }
  /* Drag-handle pill at top */
  .modal-dialog.modal-dialog--sheet::before {
    content: "";
    display: block;
    width: 36px; height: 4px;
    background: var(--line);
    border-radius: 2px;
    margin: 0 auto 12px;
    cursor: grab;
  }
  .modal-dialog.modal-dialog--sheet:active::before { cursor: grabbing; }
}
@keyframes sheet-slide-up   { to { transform: none; } }
@keyframes sheet-slide-down { to { transform: translateY(100%); opacity: 0; } }
/* Highlighted total-yards display in the New Export Entry preview.
   ~47% bigger than the prior muted stats line (1.15rem vs 0.78rem) plus
   bold weight and a tinted background — the KPI reads at a glance after
   the user picks a sr_no. tabular-nums keeps digits aligned. Same
   styling on every viewport with a small mobile font cap. Theme-aware
   via CSS variables (--primary, --primary-light, --primary-glow are
   defined in both light and dark theme blocks). */
.ee-total-yards {
  font-size: 1.15rem;
  font-weight: 700;
  color: var(--primary, #4f46e5);
  padding: 8px 12px;
  background: var(--primary-light, rgba(79, 70, 229, 0.08));
  border: 1px solid var(--primary-glow, rgba(79, 70, 229, 0.3));
  border-radius: 8px;
  text-align: center;
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.02em;
}
@media (max-width: 768px) {
  .ee-total-yards { font-size: 1.05rem; padding: 6px 10px; }
}

/* Ghost-click shield: dialogs ignore pointer events while
   [data-pointer-armed="0"] is set (cleared 500ms after mount by openModal /
   openImageModal in app.js). Survives prefers-reduced-motion because it's a
   plain attribute selector, not an animation. Replaces the prior
   modal-pointer-arm keyframe + @media (hover: none) shield, which broke
   under reduced-motion (animation: none stripped the shield). 500ms covers
   the 280ms slide-up plus margin for late synthesized clicks on MIUI/
   Xiaomi WebView variants (crbug.com/1198765). */
.modal-dialog[data-pointer-armed="0"] { pointer-events: none; }

/* ─── Inline cell editing (Order Items table) ─────────────────────────── */
.cell-edit {
  cursor: text;
  padding: 2px 4px;
  border-radius: 4px;
  display: inline-block;
  min-width: 24px;
  outline: 1px solid transparent;
  transition: background 120ms ease, outline-color 120ms ease;
}
.cell-edit:hover {
  background: var(--surface-sunken);
  outline-color: var(--line);
}
.cell-edit:focus,
.cell-edit:focus-visible {
  background: var(--surface-sunken);
  outline-color: var(--primary);
}
.cell-edit__input {
  width: 100%;
  min-width: 80px;
  padding: 2px 6px;
  font: inherit;
  border: 1px solid var(--primary);
  background: var(--surface);
  color: var(--text);
  border-radius: 4px;
  outline: none;
  box-shadow: 0 0 0 3px var(--primary-glow);
}

/* ─── Sidebar — dot + label, dyeing-app style ─────────────────────────── */
.sidebar-label {
  font-size: 11px !important;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted);
  font-weight: 600;
  padding: 0 14px;
  margin: 18px 0 6px;
}
.sidebar-section:first-child .sidebar-label { margin-top: 4px; }
.nav-item-wrap {
  display: flex;
  align-items: center;
  gap: 0;
  padding: 0;
  margin: 1px 8px;
  border-radius: 8px;
  position: relative;
  transition: background 100ms ease;
}
.nav-item-wrap:hover { background: var(--surface-sunken); }
.nav-item-wrap .nav-item {
  flex: 1;
  display: flex !important;
  align-items: center;
  gap: 10px;
  padding: 7px 12px !important;
  background: transparent !important;
  border: 0 !important;
  text-align: left;
  font-size: 14px;
  color: var(--text-secondary);
  cursor: pointer;
  border-radius: 8px;
  width: 100%;
  min-width: 0;
}
.nav-item-wrap .nav-item.active {
  color: var(--text);
  background: var(--primary-light) !important;
  font-weight: 500;
}
.nav-dot {
  display: inline-block;
  width: 7px; height: 7px;
  border-radius: 50%;
  background: var(--nav-dot-color, var(--muted));
  flex-shrink: 0;
}
.nav-label {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  flex: 1;
}
/* Star button — hidden by default, fades in on hover */
.nav-fav {
  position: absolute;
  right: 6px;
  background: transparent;
  border: 0;
  padding: 4px;
  color: var(--muted);
  border-radius: 4px;
  cursor: pointer;
  opacity: 0;
  pointer-events: none;
  transform: scale(0.85);
  transition: opacity 120ms ease, transform 120ms cubic-bezier(0.2, 0, 0, 1.4), color 120ms ease;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
.nav-item-wrap:hover .nav-fav {
  opacity: 1;
  pointer-events: auto;
  transform: none;
}
.nav-fav:hover { color: var(--amber, #d97706) !important; transform: scale(1.1); }
/* Stars in the Favorites section stay visible (filled) */
.sidebar-section .nav-fav.is-pinned { opacity: 1; pointer-events: auto; color: var(--amber, #d97706); }

/* ─── Table polish phase: row-action button, status pills, cell rhythm ─── */

/* A. Subtle row-action button, hidden until hover. Replaces the always-on
   red Delete eyesore. */
td.cell-action {
  text-align: right;
  white-space: nowrap;
  width: 1%;  /* shrink to content */
}
.btn-row-action {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  background: transparent;
  border: 1px solid transparent;
  color: var(--muted);
  padding: 4px 10px;
  border-radius: 6px;
  font-size: .78rem;
  cursor: pointer;
  opacity: 0;
  transform: translateX(4px);
  transition: opacity 140ms ease, transform 140ms cubic-bezier(0.2, 0, 0, 1), color 140ms ease, background 140ms ease, border-color 140ms ease;
  pointer-events: none;
}
tr:hover .btn-row-action,
tr:focus-within .btn-row-action,
.btn-row-action:focus-visible {
  opacity: 1;
  transform: none;
  pointer-events: auto;
}
.btn-row-action:hover {
  color: var(--danger);
  background: var(--danger-light);
  border-color: var(--danger-border, var(--danger));
}
@media (max-width: 768px), (hover: none) and (pointer: coarse) {
  /* On touch devices, hover doesn't apply — show row-action with reduced opacity always */
  .btn-row-action { opacity: .7; transform: none; pointer-events: auto; }
  .btn-row-action .row-action__label { display: none; }  /* icon-only on phone */
}

/* B. Status pills — coloured by value, used in Order Items */
.pill {
  display: inline-block;
  padding: 2px 10px;
  border-radius: 999px;
  font-size: .72rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: .04em;
  white-space: nowrap;
}
.pill--muted    { background: var(--surface-sunken); color: var(--muted); }
.pill--balance  { background: color-mix(in srgb, var(--amber) 18%, transparent);         color: var(--amber-dark, var(--amber)); }
.pill--running  { background: color-mix(in srgb, var(--success) 18%, transparent);       color: var(--success-dark, var(--success)); }
.pill--ok       { background: color-mix(in srgb, var(--info) 18%, transparent);          color: var(--info-strong, var(--info)); }
.pill--pending  { background: color-mix(in srgb, var(--danger) 18%, transparent);        color: var(--danger-strong, var(--danger)); }
.pill--export   { background: var(--surface-sunken); color: var(--text-secondary); }
.pill--uniform  { background: color-mix(in srgb, var(--accent-violet) 18%, transparent); color: var(--accent-violet); }

/* GH Ready status pills — match chip filter colors (Export Entries) */
.pill--not_ready     { background: color-mix(in srgb, var(--amber) 18%, transparent);   color: var(--amber-dark, var(--amber)); }
.pill--hold          { background: color-mix(in srgb, var(--danger) 18%, transparent);  color: var(--danger-strong, var(--danger)); }
.pill--lot_complete  { background: color-mix(in srgb, var(--success) 18%, transparent); color: var(--success-dark, var(--success)); }
.pill--ready         { background: color-mix(in srgb, var(--info) 18%, transparent);    color: var(--info-strong, var(--info)); }
.pill--shipment      { background: var(--surface-sunken); color: var(--text-secondary); }
/* Country pills (export_category) — soft neutral so they read as data, not status */
.pill--lagos    { background: color-mix(in srgb, var(--accent-violet) 14%, transparent); color: var(--accent-violet); }
.pill--kano     { background: color-mix(in srgb, var(--info) 14%, transparent);          color: var(--info-strong, var(--info)); }
.pill--cj       { background: color-mix(in srgb, var(--success) 14%, transparent);       color: var(--success-dark, var(--success)); }
.pill--sn       { background: color-mix(in srgb, var(--amber) 14%, transparent);         color: var(--amber-dark, var(--amber)); }
.pill--aa       { background: color-mix(in srgb, var(--danger) 14%, transparent);        color: var(--danger-strong, var(--danger)); }
.pill--austria  { background: color-mix(in srgb, var(--accent-violet) 14%, transparent); color: var(--accent-violet); }
.pill--uk       { background: color-mix(in srgb, var(--info) 14%, transparent);          color: var(--info-strong, var(--info)); }
.pill--open     { background: var(--surface-sunken); color: var(--text-secondary); }

/* Smooth post-save transition: form panel collapses, new row flashes */
#lst-form-panel {
  transition: max-height 240ms ease, opacity 220ms ease, padding 220ms ease, margin 220ms ease;
  overflow: hidden;
}
#lst-form-panel.lst-form-panel--closing {
  max-height: 0 !important;
  opacity: 0;
  padding-top: 0 !important;
  padding-bottom: 0 !important;
  margin-top: 0 !important;
  margin-bottom: 0 !important;
}
@keyframes row-just-added-flash {
  0%   { background: color-mix(in srgb, var(--success) 28%, transparent); }
  60%  { background: color-mix(in srgb, var(--success) 14%, transparent); }
  100% { background: transparent; }
}
tr.row-just-added > td { animation: row-just-added-flash 1.6s ease-out; }

/* C. List-table polish — better breathing room + scannable headers */
#lst-table-wrap > table th {
  font-size: 11px !important;
  font-weight: 600 !important;
  text-transform: uppercase;
  letter-spacing: .08em;
  color: var(--muted) !important;
  padding: 12px 10px !important;
  white-space: nowrap;
  background: var(--surface);
  border-bottom: 1px solid var(--line);
}
#lst-table-wrap > table td {
  padding: 10px 10px !important;
  vertical-align: middle;
  font-size: 14px;
  color: var(--text);
  line-height: 1.4;
}
#lst-table-wrap > table tbody tr {
  transition: background 80ms ease;
}
#lst-table-wrap > table tbody tr:hover {
  background: var(--surface-sunken);
}
/* Compact-density override stays the same — already has !important padding 6px 8px */

/* D. Soften the chip-link backgrounds — replaces washed-out neutral grey
   with a soft primary tint so chip clicks read as interactive. */
.chip-link {
  background: color-mix(in srgb, var(--primary) 8%, transparent) !important;
  border: 1px solid color-mix(in srgb, var(--primary) 18%, transparent) !important;
  color: var(--text) !important;
  transition: background 120ms ease, border-color 120ms ease, transform 100ms ease;
}
.chip-link:hover {
  background: color-mix(in srgb, var(--primary) 14%, transparent) !important;
  border-color: color-mix(in srgb, var(--primary) 28%, transparent) !important;
  transform: translateY(-1px);
}

/* ─── Home dashboard ──────────────────────────────────────────────────── */
.home-dashboard { display: grid; gap: 14px; padding: 8px 4px 64px; max-width: 1240px; }
.home-header h1 {
  font-family: "Instrument Serif", Georgia, serif;
  font-size: 2.4rem;
  font-weight: 400;
  color: var(--text);
  margin: 0;
  line-height: 1.1;
}
.home-header p {
  color: var(--muted);
  margin: 4px 0 0;
  font-size: .9rem;
  text-transform: uppercase;
  letter-spacing: .04em;
}

.stat-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: 12px;
}
.stat-card {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 14px;
  padding: 18px 20px;
  display: grid;
  gap: 4px;
  transition: box-shadow 120ms ease, transform 120ms ease;
}
.stat-card:hover { box-shadow: var(--shadow-md); transform: translateY(-1px); }
.stat-card__label {
  font-size: 11px;
  color: var(--muted);
  text-transform: uppercase;
  letter-spacing: .08em;
  font-weight: 600;
}
.stat-card__value {
  font-family: "Instrument Serif", Georgia, serif;
  font-size: 2.4rem;
  font-weight: 400;
  color: var(--text);
  line-height: 1;
  margin-top: 4px;
}
.stat-card__sub { font-size: .82rem; color: var(--muted); margin-top: 4px; }
.stat-card--accent {
  background: linear-gradient(135deg, var(--primary) 0%, var(--primary-hover) 100%);
  color: #fff;
  border: 0;
}
.stat-card--accent .stat-card__label,
.stat-card--accent .stat-card__sub { color: rgba(255,255,255,.85); }
.stat-card--accent .stat-card__value { color: #fff; }

.home-section-head {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  margin-bottom: 10px;
}
.home-section-head h3 {
  font-family: "Instrument Serif", Georgia, serif;
  font-size: 1.4rem;
  font-weight: 400;
  color: var(--text);
  margin: 0;
}
.home-see-all { color: var(--muted); font-size: .82rem; cursor: pointer; }
.home-see-all:hover { color: var(--primary); }

.recent-pill-row {
  display: flex;
  gap: 8px;
  overflow-x: auto;
  padding: 4px 2px 8px;
  scrollbar-width: thin;
  -webkit-overflow-scrolling: touch;
  overscroll-behavior-x: contain;
}
.recent-pill {
  flex: 0 0 auto;
  background: var(--surface);
  border: 1px solid var(--line);
  border-left: 3px solid var(--success);
  border-radius: 10px;
  padding: 10px 14px;
  cursor: pointer;
  text-align: left;
  display: grid;
  gap: 2px;
  transition: box-shadow 120ms ease, transform 120ms ease;
}
.recent-pill:hover { box-shadow: var(--shadow-sm); transform: translateY(-1px); }
.recent-pill strong { color: var(--text); font-size: .95rem; font-variant-numeric: tabular-nums; }
.recent-pill span { color: var(--muted); font-size: .78rem; }

.progress-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
  gap: 12px;
}
.progress-card {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 14px;
  padding: 0;
  cursor: pointer;
  text-align: left;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  transition: box-shadow 120ms ease, transform 120ms ease;
}
.progress-card:hover { box-shadow: var(--shadow-md); transform: translateY(-2px); }
.progress-card__hero {
  height: 110px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 12px 12px 0 0;
}
.progress-card__num {
  font-family: "Instrument Serif", Georgia, serif;
  font-size: 2.4rem;
  font-weight: 400;
  color: #fff;
  text-shadow: 0 2px 8px rgba(0,0,0,.18);
}
.progress-card__meta { padding: 12px 14px; display: grid; gap: 2px; min-width: 0; }
.progress-card__meta strong {
  color: var(--text);
  font-size: .9rem;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.progress-card__meta span {
  color: var(--muted);
  font-size: .78rem;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.progress-card__pill {
  display: inline-block !important;
  margin-top: 6px;
  padding: 2px 10px;
  background: var(--surface-sunken);
  border-radius: 999px;
  font-size: .7rem !important;
  text-transform: uppercase;
  letter-spacing: .04em;
  font-weight: 600;
  color: var(--text-secondary) !important;
  width: max-content;
}
@media (max-width: 768px) {
  .home-header h1 { font-size: 1.8rem; }
  .stat-card__value { font-size: 1.8rem; }
  .progress-card__num { font-size: 1.8rem; }
  .progress-card__hero { height: 90px; }
}

/* ─── Home dashboard v36 (redesigned) ──────────────────────────────────────
   Ported from mocks/home-dashboard.html. New classes prefixed `home-` /
   `dash-` to avoid clashes with the legacy stat-card layout above. Reuses
   the existing --surface / --line / --primary / --shadow-* tokens. */
.home-dashboard { overflow-x: hidden; }
.home-dash-head {
  display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
}
.home-dash-head .home-dash-actions { margin-left: auto; display: flex; gap: 8px; }
.home-pdf-btn {
  background: var(--surface); color: var(--text-secondary);
  border: 1px solid var(--line); border-radius: 9px;
  font-size: .8rem; font-weight: 600; padding: 8px 13px; cursor: pointer;
  display: inline-flex; align-items: center; gap: 6px;
  -webkit-tap-highlight-color: transparent;
  transition: background .13s, border-color .13s, color .13s;
}
@media (hover: hover) {
  .home-pdf-btn:hover { background: var(--surface-sunken); color: var(--text); border-color: var(--border, var(--line)); }
}
.home-pdf-btn:active { background: var(--surface-sunken); transform: scale(.98); }
.home-greet {
  font-family: "Instrument Serif", Georgia, serif; font-size: 2.4rem;
  font-weight: 400; line-height: 1.15; overflow-wrap: anywhere;
  margin: 4px 0 0; color: var(--text);
}
.home-greet small {
  display: block; font-family: inherit; font-size: .95rem;
  color: var(--muted); font-style: italic; margin-top: 2px;
}
.home-section-label {
  font-size: .7rem; font-weight: 700; letter-spacing: .09em;
  text-transform: uppercase; color: var(--muted);
  margin: 28px 2px 12px; display: flex; align-items: center; gap: 8px;
  flex-wrap: wrap;
}
.home-section-label .note {
  font-weight: 600; letter-spacing: .02em; text-transform: none;
  color: var(--muted); font-size: .72rem;
}

/* ── AI briefing card ── */
.home-ai-card {
  position: relative;
  background: linear-gradient(160deg, #ffffff 0%, #f6f5fd 62%, #f1f1fb 100%);
  border: 1px solid #e2e0f3; border-radius: 16px; padding: 20px 22px;
  overflow: hidden; box-shadow: 0 1px 2px rgba(79,70,229,.04), 0 6px 20px -14px rgba(79,70,229,.22);
}
.home-ai-card::before {
  content: ""; position: absolute; right: -50px; top: -50px;
  width: 190px; height: 190px;
  background: radial-gradient(circle, rgba(79,70,229,.10), transparent 70%);
}
.home-ai-card__top {
  display: flex; align-items: center; gap: 9px; margin-bottom: 6px;
  flex-wrap: wrap; position: relative;
}
.home-ai-badge {
  display: inline-flex; align-items: center; gap: 6px;
  background: var(--primary); color: #fff; font-size: .66rem; font-weight: 700;
  letter-spacing: .06em; text-transform: uppercase; padding: 4px 10px;
  border-radius: 999px;
}
.home-ai-badge .dot {
  width: 6px; height: 6px; border-radius: 50%;
  background: #a5f3d0; box-shadow: 0 0 6px #6ee7b7;
}
.home-ai-card__title {
  font-family: "Instrument Serif", Georgia, serif; font-size: 1.4rem;
  font-weight: 400;
}
.home-ai-card__stamp { margin-left: auto; font-size: .72rem; color: var(--muted); }
.home-ai-reload {
  background: rgba(255,255,255,.7); border: 1px solid #d8d5f0; color: var(--primary);
  font-weight: 600; font-size: .78rem; border-radius: 9px; padding: 6px 11px;
  cursor: pointer; -webkit-tap-highlight-color: transparent;
  transition: background .13s ease, border-color .13s ease;
}
@media (hover: hover) {
  .home-ai-reload:hover { background: #fff; border-color: #c4bff0; }
}
.home-ai-reload:active { background: #f0eeff; }
.home-ai-reload:disabled { opacity: .55; cursor: default; }
.home-ai-cache-note {
  font-size: .7rem; color: var(--muted); margin: 0 0 10px;
  display: flex; align-items: center; gap: 5px; position: relative;
}
.home-ai-body { font-size: .95rem; color: #252433; position: relative; }
.home-ai-body .ai-rep-list { margin: 0; padding: 0; list-style: none; }
.home-ai-body .ai-rep-list li {
  margin: 7px 0 7px 4px; padding-left: 18px; position: relative;
  list-style: none; overflow-wrap: anywhere;
}
.home-ai-body .ai-rep-list li::before {
  content: ""; position: absolute; left: 0; top: 9px; width: 7px; height: 7px;
  border-radius: 50%; background: var(--primary); opacity: .55;
}
.home-ai-body .ai-rep-para { margin: 7px 0; overflow-wrap: anywhere; }
.home-ai-body .ai-rep-heading {
  font-family: "Instrument Serif", Georgia, serif; font-weight: 400;
  font-size: 1.05rem; margin: 8px 0 4px;
}
.home-ai-body b { font-weight: 700; color: #15141f; }
.home-ai-quiet { font-size: .9rem; color: var(--muted); font-style: italic; margin: 4px 0; }

/* ── KPI tiles ── */
.home-kpi-grid {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(158px, 1fr));
  gap: 12px;
}
.home-kpi {
  background: var(--surface); border: 1px solid var(--line);
  border-radius: 16px; padding: 16px 17px; cursor: pointer;
  box-shadow: var(--shadow-sm);
  transition: box-shadow .14s ease, transform .14s ease, border-color .14s ease;
  display: flex; flex-direction: column; gap: 4px;
  text-decoration: none; color: inherit; text-align: left;
  -webkit-tap-highlight-color: transparent; font: inherit;
}
.home-kpi:active { transform: scale(.985); }
@media (hover: hover) {
  .home-kpi:hover {
    box-shadow: var(--shadow-md); transform: translateY(-2px);
    border-color: #d6d2f0;
  }
}
.home-kpi__label {
  font-size: .66rem; font-weight: 700; letter-spacing: .06em;
  text-transform: uppercase; color: var(--muted);
}
.home-kpi__row { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; margin-top: 2px; }
.home-kpi__value {
  font-family: "Instrument Serif", Georgia, serif; font-size: 2.35rem;
  line-height: 1; font-weight: 400; color: var(--text);
}
.home-kpi__delta { font-size: .71rem; font-weight: 700; padding: 2px 8px; border-radius: 999px; }
.home-kpi__delta.d-up   { background: #dcfce7; color: #166534; }
.home-kpi__delta.d-down { background: #fee2e2; color: #991b1b; }
.home-kpi__delta.d-flat { background: var(--surface-sunken); color: var(--muted); }
.home-kpi__foot { font-size: .72rem; color: var(--muted); margin-top: 3px; }
/* Accent + alert tiles: quiet tint, not a saturated fill — calmer band. */
.home-kpi--accent {
  background: linear-gradient(150deg, #f4f3fd, #eceafa);
  border-color: #ddd9f4;
}
.home-kpi--accent .home-kpi__value { color: var(--primary); }
.home-kpi--accent .home-kpi__delta { background: #fee2e2; color: #991b1b; }
.home-kpi--alert {
  border-color: #f3d3d3;
  background: linear-gradient(150deg, var(--surface), #fdf6f6);
}
.home-kpi--alert .home-kpi__value { color: #b42318; }

/* ── Charts ── */
.home-machine-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 12px; }
.home-chart-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
.home-chart-card {
  background: var(--surface); border: 1px solid var(--line);
  border-radius: 16px; padding: 16px 17px 12px; min-width: 0;
  box-shadow: var(--shadow-sm);
  transition: box-shadow .14s ease, border-color .14s ease;
}
@media (hover: hover) {
  .home-chart-card:hover { box-shadow: var(--shadow-md); }
}
.home-chart-card__head {
  display: flex; align-items: flex-start; justify-content: space-between;
  gap: 8px; margin-bottom: 6px;
}
.home-chart-card__title {
  font-size: .7rem; font-weight: 700; letter-spacing: .05em;
  text-transform: uppercase; color: var(--muted);
}
.home-chart-card__big {
  font-family: "Instrument Serif", Georgia, serif; font-size: 1.6rem;
  line-height: 1.1; margin-top: 3px; color: var(--text);
}
.home-chart-card__sub { font-size: .7rem; color: var(--muted); margin-top: 1px; }
.home-range-toggle {
  display: inline-flex; gap: 3px; background: var(--surface-sunken);
  border-radius: 8px; padding: 3px; flex: 0 0 auto;
}
.home-range-toggle button {
  border: none; background: transparent; font-size: .7rem; font-weight: 600;
  color: var(--muted); padding: 4px 9px; border-radius: 6px; cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  transition: background .12s ease, color .12s ease;
}
@media (hover: hover) {
  .home-range-toggle button:not(.on):hover { color: var(--text-secondary); }
}
.home-range-toggle button.on {
  background: var(--surface); color: var(--text); box-shadow: var(--shadow-sm);
}
.home-chart svg { display: block; width: 100%; height: auto; overflow: visible; touch-action: pan-y; }
.home-chart-empty {
  font-size: .82rem; color: var(--muted); padding: 32px 12px;
  text-align: center; line-height: 1.6;
}
.home-chart-tooltip {
  position: fixed; pointer-events: none; background: #15141f; color: #fff;
  font-size: .72rem; padding: 5px 9px; border-radius: 7px; opacity: 0;
  transition: opacity .08s; white-space: nowrap; z-index: 1200;
  box-shadow: 0 6px 18px rgba(0,0,0,.3); left: 0; top: 0;
}
.home-hbar-row {
  display: grid; grid-template-columns: 58px 1fr 50px; align-items: center;
  gap: 10px; margin: 0; padding: 7px 8px; cursor: pointer; width: 100%;
  border: 0; background: none; border-radius: 9px; font: inherit;
  text-align: left; -webkit-tap-highlight-color: transparent;
  transition: background .12s ease;
}
@media (hover: hover) {
  .home-hbar-row:hover { background: var(--surface-sunken); }
}
.home-hbar-row:active .home-hbar-name,
.home-hbar-row:hover .home-hbar-name { color: var(--primary); }
.home-hbar-name { font-size: .8rem; font-weight: 700; overflow-wrap: anywhere; }
.home-hbar-track {
  height: 20px; border-radius: 6px; background: var(--surface-sunken);
  display: flex; overflow: hidden;
}
.home-hbar-seg { height: 100%; transition: width .5s cubic-bezier(.2,.7,.3,1); }
.home-hbar-total {
  font-size: .8rem; font-weight: 700; text-align: right;
  font-variant-numeric: tabular-nums;
}
/* Kam-stock coverage rows — name, bar, then stock/need/short label. */
.home-stock-row {
  display: flex; flex-direction: column; gap: 5px;
  width: 100%; margin: 0; padding: 8px; cursor: pointer;
  border: 0; background: none; border-radius: 9px; font: inherit;
  text-align: left; -webkit-tap-highlight-color: transparent;
  transition: background .12s ease;
}
@media (hover: hover) {
  .home-stock-row:hover { background: var(--surface-sunken); }
}
.home-stock-row:active .home-stock-name,
.home-stock-row:hover .home-stock-name { color: var(--primary); }
.home-stock-name { font-size: .8rem; font-weight: 700; overflow-wrap: anywhere; }
.home-stock-lbl {
  font-size: .72rem; color: var(--muted);
  font-variant-numeric: tabular-nums;
}
.home-stock-lbl strong { color: var(--danger); font-weight: 700; }

/* ── Fabric Report module ──────────────────────────────────────────── */
.fr-page { max-width: 680px; margin: 0 auto; padding: 10px 14px 110px; }
.fr-topline { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin: 6px 0 14px; }
.fr-topline h2 { font-size: 1.2rem; }
.fr-date {
  display: flex; align-items: center; gap: 8px; background: var(--surface);
  border: 1px solid var(--line); border-radius: 11px; padding: 8px 12px; font-weight: 600;
}
.fr-date input { border: 0; background: none; font: inherit; font-weight: 600; color: var(--text); }
.fr-link {
  margin-left: auto; font-size: .82rem; color: var(--primary); background: none;
  border: 0; font-weight: 700; cursor: pointer; padding: 6px;
}
.fr-card {
  background: var(--surface); border: 1px solid var(--line); border-radius: 16px;
  padding: 15px 14px; margin-bottom: 14px;
}
.fr-card-head { display: flex; align-items: baseline; gap: 8px; margin-bottom: 2px; }
.fr-card-head h2 { font-size: 1.04rem; margin: 0; font-weight: 600; }
.fr-refresh {
  margin-left: auto; font-size: .74rem; font-weight: 700; cursor: pointer;
  color: var(--primary); background: var(--primary-light);
  border: 0; border-radius: 8px; padding: 6px 11px; white-space: nowrap;
}
.fr-refresh:disabled { opacity: .55; }
/* v47.4 — coarse-pointer (touch) min tap-area for daily-report refresh. */
@media (hover: none) and (pointer: coarse) {
  .fr-refresh { min-height: 44px; padding: 11px 14px; }
}
.fr-card-sub { font-size: .78rem; color: var(--muted); margin: 0 0 13px; }
.fr-chip {
  font-size: .6rem; font-weight: 700; letter-spacing: .04em; padding: 2px 7px;
  border-radius: 999px; text-transform: uppercase;
}
.fr-chip.auto  { background: var(--surface-sunken); color: var(--muted); }
.fr-chip.added { background: var(--primary-light); color: var(--primary); }
.fr-chip.new   { background: var(--success-light); color: var(--success-dark); }
.fr-frow {
  border: 1px solid var(--line); border-radius: 13px; padding: 11px;
  margin-bottom: 9px; background: var(--bg);
}
.fr-frow-top { display: flex; align-items: center; gap: 7px; margin-bottom: 9px; }
.fr-frow-name { font-weight: 700; font-size: .95rem; overflow-wrap: anywhere; }
.fr-del {
  margin-left: auto; flex: 0 0 auto; width: 30px; height: 30px; border-radius: 8px;
  border: 0; background: var(--danger-light); color: var(--danger);
  font-size: 14px; cursor: pointer;
}
.fr-fields { display: grid; grid-template-columns: 1fr 1fr; gap: 9px; }
.fr-fld label {
  display: block; font-size: .64rem; font-weight: 700; color: var(--muted);
  text-transform: uppercase; letter-spacing: .04em; margin-bottom: 4px;
}
.fr-inp {
  width: 100%; border: 1.5px solid var(--line); border-radius: 10px;
  padding: 11px 12px; font: inherit; font-size: 1.05rem; font-weight: 600;
  font-variant-numeric: tabular-nums; background: var(--surface); color: var(--text);
}
.fr-inp:focus { outline: none; border-color: var(--primary); box-shadow: 0 0 0 3px var(--primary-glow); }
.fr-inp.prefilled { background: var(--primary-light); border-color: var(--primary-light); }
.fr-add {
  width: 100%; border: 1.5px dashed var(--line); background: var(--surface);
  border-radius: 11px; padding: 11px; font: inherit; font-weight: 600;
  color: var(--primary); cursor: pointer; margin-top: 3px;
}
.fr-tots { display: flex; gap: 9px; margin-top: 12px; }
.fr-tot { flex: 1; background: var(--surface-sunken); border-radius: 11px; padding: 9px 11px; }
.fr-tot .k {
  font-size: .62rem; font-weight: 700; color: var(--muted);
  text-transform: uppercase; letter-spacing: .04em;
}
.fr-tot .v { font-size: 1.16rem; font-weight: 800; font-variant-numeric: tabular-nums; margin-top: 1px; }
.fr-tot.issue .v { color: #7c3aed; }
.fr-tot.cut .v { color: var(--info); }
.fr-wrow { display: flex; gap: 8px; align-items: center; margin-bottom: 8px; }
.fr-wrow .n {
  flex: 0 0 22px; height: 22px; border-radius: 6px; background: var(--surface-sunken);
  color: var(--muted); font-size: .7rem; font-weight: 700;
  display: flex; align-items: center; justify-content: center;
}
.fr-people { display: grid; grid-template-columns: 1fr 1fr; gap: 11px; }
.fr-pbox { border: 1px solid var(--line); border-radius: 13px; padding: 12px; text-align: center; }
.fr-pbox.present { background: var(--success-tint); border-color: var(--success-border); }
.fr-pbox.absent  { background: var(--danger-light); border-color: var(--danger-border); }
.fr-pbox label {
  font-size: .66rem; font-weight: 700; text-transform: uppercase;
  letter-spacing: .04em; color: var(--muted);
}
.fr-pinp {
  width: 100%; border: 0; background: none; text-align: center; font: inherit;
  font-size: 2rem; font-weight: 800; margin-top: 4px;
  font-variant-numeric: tabular-nums; color: var(--text);
}
.fr-pbox.present .fr-pinp { color: var(--success); }
.fr-pbox.absent  .fr-pinp { color: var(--danger); }
.fr-pinp:focus { outline: none; }
.fr-pnote { font-size: .73rem; color: var(--muted); margin: 10px 2px 0; text-align: center; }
.fr-footer-in { display: flex; align-items: center; gap: 9px; margin-top: 2px; }
.fr-ftot { font-size: .72rem; color: var(--muted); line-height: 1.3; }
.fr-ftot b { display: block; font-size: .98rem; color: var(--text); font-variant-numeric: tabular-nums; }
.fr-btn {
  border: 0; border-radius: 11px; padding: 13px 15px; font: inherit;
  font-weight: 700; cursor: pointer; font-size: .92rem;
}
.fr-btn.save { background: var(--primary); color: #fff; flex: 1; }
.fr-btn.save:disabled { opacity: .55; }
.fr-btn.img { background: var(--text); color: var(--surface); display: flex; align-items: center; gap: 5px; }
.fr-listrow:last-child { border-bottom: 0 !important; }
/* Admin alteration-audit cues */
.fr-altnote {
  font-size: .69rem; color: var(--amber); font-weight: 600; margin-top: 7px;
}
.fr-altnote:empty { display: none; }
.fr-altsum { font-size: .74rem; font-weight: 600; margin: 9px 2px 0; }
.fr-altbadge {
  background: var(--amber-light); color: var(--amber); font-size: .66rem;
  font-weight: 700; padding: 3px 8px; border-radius: 999px; flex: 0 0 auto;
  white-space: nowrap;
}
.home-legend {
  display: flex; gap: 13px; flex-wrap: wrap; margin-top: 8px;
  font-size: .71rem; color: var(--muted);
}
.home-legend span { display: inline-flex; align-items: center; gap: 5px; }
.home-legend i { width: 9px; height: 9px; border-radius: 3px; display: inline-block; }

/* ── Attention panel ── */
.home-attn {
  background: var(--surface); border: 1px solid var(--line);
  border-radius: 16px; overflow: hidden; align-self: start;
  box-shadow: var(--shadow-sm);
}
.home-attn__head {
  padding: 14px 17px; border-bottom: 1px solid var(--line);
  display: flex; align-items: center; gap: 9px;
}
.home-attn__head h3 {
  font-family: "Instrument Serif", Georgia, serif; font-weight: 400;
  font-size: 1.2rem; margin: 0;
}
.home-attn__count {
  margin-left: auto; background: #fee2e2; color: #991b1b; font-weight: 700;
  font-size: .74rem; padding: 2px 9px; border-radius: 999px;
}
.home-attn-row {
  display: flex; align-items: center; gap: 12px; padding: 12px 17px;
  cursor: pointer; border-bottom: 1px solid var(--line);
  -webkit-tap-highlight-color: transparent; width: 100%; text-align: left;
  background: none; border-left: 0; border-right: 0; border-top: 0; font: inherit;
  transition: background .12s ease;
}
.home-attn-row:last-child { border-bottom: none; }
@media (hover: hover) {
  .home-attn-row:hover { background: var(--surface-sunken); }
}
.home-attn-row:active { background: var(--surface-sunken); }
.home-attn-ico {
  width: 32px; height: 32px; border-radius: 9px; display: flex;
  align-items: center; justify-content: center; font-size: 1rem; flex: 0 0 auto;
}
.home-attn-ico.i-stuck { background: #fee2e2; }
.home-attn-ico.i-hold  { background: #fef3c7; }
.home-attn-ico.i-tally { background: #e0e7ff; }
.home-attn-ico.i-stock { background: #cffafe; }
.home-attn-row strong { font-size: .88rem; display: block; overflow-wrap: anywhere; color: var(--text); }
.home-attn-row .home-attn-sub { font-size: .77rem; color: var(--muted); overflow-wrap: anywhere; }
.home-attn-row .chev {
  margin-left: auto; color: var(--muted); font-size: 1.1rem;
  transition: transform .13s ease, color .13s ease;
}
@media (hover: hover) {
  .home-attn-row:hover .chev { transform: translateX(2px); color: var(--text-secondary); }
}
.home-attn-empty {
  padding: 28px 18px; font-size: .85rem; color: var(--muted);
  text-align: center; line-height: 1.6;
}

/* ── responsive — PWA iOS / Android ── */
@media (max-width: 820px) {
  .home-machine-grid { grid-template-columns: 1fr; }
  .home-chart-grid { grid-template-columns: 1fr; }
}
@media (max-width: 760px) {
  .home-greet { font-size: 1.95rem; }
  .home-ai-card__stamp { display: none; }
}

/* ── PDF / print — A4 ───────────────────────────────────────────────────
   The app's original print block (search "body > *:not(.print-qr-view)")
   sets display:none on every direct child of <body> — including .layout,
   which is the ancestor of the whole dashboard. That made the dashboard
   PDF come out blank. This block is layered AFTER it: when the home
   dashboard is on screen we re-reveal the shell chain (.layout → .main →
   #module-workspace → .home-dashboard) and strip the SPA's full-height /
   overflow:hidden / sticky / position clipping so the content flows
   naturally down as many A4 pages as it needs.

   :has(.home-dashboard) keeps this scoped to the Home module — every
   other module still uses the app's QR-only print rule untouched. */
@page {
  size: A4;
  margin: 14mm 13mm 16mm;
}
@media print {
  /* ════════════════════════════════════════════════════════════════════
     Home dashboard → polished A4 report.
     Everything below is scoped to @media print + body:has(.home-dashboard)
     so the on-screen dashboard is never affected.
     ════════════════════════════════════════════════════════════════════ */

  /* Re-show the shell chain the original block hid, and flatten every
     scroll/clip container so nothing collapses to one viewport. */
  body:has(.home-dashboard),
  body:has(.home-dashboard) .layout,
  body:has(.home-dashboard) .main,
  body:has(.home-dashboard) #module-workspace,
  body:has(.home-dashboard) .home-dashboard {
    display: block !important;
    height: auto !important;
    min-height: 0 !important;
    max-height: none !important;
    overflow: visible !important;
    position: static !important;
  }
  /* Hide all app chrome + every interactive dashboard control — only the
     report data prints. (range toggles, Reload/Export/PDF buttons, cache
     note, attention-row chevrons, dev MOCK banner.) */
  body:has(.home-dashboard) .sidebar,
  body:has(.home-dashboard) #sidebar-overlay,
  body:has(.home-dashboard) .main-header,
  body:has(.home-dashboard) .bottom-nav,
  body:has(.home-dashboard) #app-toast-stack,
  body:has(.home-dashboard) #app-progress-stack,
  body:has(.home-dashboard) .mock-flag,
  body:has(.home-dashboard) [class*="mock-banner"],
  .home-range-toggle, .home-pdf-btn, .home-dash-actions,
  .home-ai-reload, .home-ai-cache-note,
  .home-attn-row .chev {
    display: none !important;
  }

  html, body:has(.home-dashboard) { background: #fff !important; }
  body:has(.home-dashboard) {
    padding: 0 !important;
    width: auto !important;
    -webkit-print-color-adjust: exact;
    print-color-adjust: exact;
    color-adjust: exact;
  }
  /* Force exact colour reproduction on every node so KPI tiles, chips,
     chart fills and the AI card all print vivid, not washed out. */
  body:has(.home-dashboard) *,
  body:has(.home-dashboard) *::before,
  body:has(.home-dashboard) *::after {
    -webkit-print-color-adjust: exact !important;
    print-color-adjust: exact !important;
    color-adjust: exact !important;
  }

  .home-dashboard {
    padding: 0 !important;
    max-width: none !important;
    gap: 0 !important;
    font-size: 10pt;
  }

  /* ── Masthead — the dashboard head + greeting become a branded banner ──
     The "Home" h1 and the .home-dash-head row are merged into a single
     indigo-accented masthead. */
  .home-dash-head {
    border-bottom: 2.4pt solid var(--primary);
    padding: 0 0 8pt !important;
    margin: 0 0 4pt !important;
    align-items: flex-end !important;
  }
  .home-dash-head h1 {
    font-size: 23pt !important;
    color: var(--primary) !important;
    letter-spacing: .2pt;
    line-height: 1.05 !important;
  }
  /* "Orders ERP — Daily Snapshot" eyebrow above the H1, print-only. */
  .home-dash-head h1::before {
    content: "ORDERS ERP";
    display: block;
    font-family: "Inter", system-ui, sans-serif;
    font-size: 6.6pt;
    font-weight: 800;
    letter-spacing: .22em;
    color: #8884c8;
    margin-bottom: 2pt;
  }
  /* Right-aligned report tag in the masthead, print-only. */
  .home-dash-head::after {
    content: "A4 Report";
    margin-left: auto;
    align-self: flex-end;
    font-family: "Inter", system-ui, sans-serif;
    font-size: 7pt;
    font-weight: 700;
    letter-spacing: .12em;
    text-transform: uppercase;
    color: #fff;
    background: var(--primary);
    padding: 3pt 9pt;
    border-radius: 999px;
  }
  .home-greet {
    font-size: 17pt !important;
    margin: 9pt 0 2pt !important;
    color: #1a1830 !important;
  }
  .home-greet small {
    font-size: 9pt !important;
    color: #6b6890 !important;
    margin-top: 1pt !important;
  }

  /* ── Section labels — clear, indigo-ticked headers that stay with
     their content. ── */
  .home-section-label {
    break-after: avoid;
    page-break-after: avoid;
    margin: 16pt 0 8pt !important;
    font-size: 8pt !important;
    letter-spacing: .13em !important;
    color: #4a4870 !important;
    padding-left: 9pt;
    border-left: 3pt solid var(--primary);
    line-height: 1.25;
  }
  .home-section-label .note { color: #908da8 !important; font-size: 7.4pt !important; }

  /* ── AI briefing card — keep the soft indigo wash, drop the glow blob ──
     The decorative radial-gradient ::before prints as a hard solid blob,
     so it stays hidden in print. */
  .home-ai-card {
    break-inside: avoid;
    page-break-inside: avoid;
    box-shadow: none !important;
    border: 1pt solid #d6d3ef !important;
    border-left: 4pt solid var(--primary) !important;
    border-radius: 10pt !important;
    padding: 13pt 15pt !important;
    background: linear-gradient(160deg, #ffffff 0%, #f5f4fd 100%) !important;
  }
  .home-ai-card::before { display: none !important; }
  .home-ai-card__top { margin-bottom: 5pt !important; }
  .home-ai-badge {
    background: var(--primary) !important;
    color: #fff !important;
    font-size: 6.8pt !important;
    padding: 3pt 8pt !important;
  }
  .home-ai-card__title { font-size: 13.5pt !important; color: #1a1830 !important; }
  .home-ai-card__stamp { color: #8a87a8 !important; font-size: 7.6pt !important; }
  .home-ai-body { font-size: 9.2pt !important; color: #2a2940 !important; }
  .home-ai-body .ai-rep-list li { margin: 4pt 0 4pt 4pt !important; }
  .home-ai-body .ai-rep-list li::before {
    background: var(--primary) !important;
    opacity: 1 !important;
  }
  .home-ai-body b { color: #15141f !important; }

  /* ── KPI tiles — vivid coloured cards, packed tight across the page ── */
  .home-kpi-grid {
    display: grid !important;
    grid-template-columns: repeat(3, 1fr) !important;
    gap: 8pt !important;
    break-inside: avoid;
    page-break-inside: avoid;
  }
  .home-kpi {
    break-inside: avoid;
    page-break-inside: avoid;
    box-shadow: none !important;
    border: 1pt solid #ddd9f0 !important;
    border-top: 3pt solid var(--primary) !important;
    border-radius: 9pt !important;
    padding: 9pt 11pt !important;
    background: linear-gradient(165deg, #ffffff 0%, #f6f5fd 100%) !important;
    transform: none !important;
  }
  .home-kpi__label { font-size: 6.8pt !important; color: #6b6890 !important; }
  .home-kpi__value { font-size: 21pt !important; color: #1a1830 !important; }
  .home-kpi__foot  { font-size: 7.2pt !important; color: #8a87a8 !important; }
  .home-kpi__delta { font-size: 7pt !important; padding: 1.5pt 6pt !important; }
  .home-kpi__delta.d-up   { background: #d1fae5 !important; color: #065f46 !important; }
  .home-kpi__delta.d-down { background: #fee2e2 !important; color: #991b1b !important; }
  .home-kpi__delta.d-flat { background: #ececf3 !important; color: #6b6890 !important; }
  /* Accent + alert tiles keep their coloured identity in print. */
  .home-kpi--accent {
    background: linear-gradient(165deg, #f1effc 0%, #e6e3f8 100%) !important;
    border-top-color: var(--primary) !important;
  }
  .home-kpi--accent .home-kpi__value { color: var(--primary) !important; }
  .home-kpi--alert {
    background: linear-gradient(165deg, #fef5f5 0%, #fde4e4 100%) !important;
    border-color: #f0c4c4 !important;
    border-top-color: #dc2626 !important;
  }
  .home-kpi--alert .home-kpi__value { color: #b42318 !important; }

  /* ── Charts — keep both grids intact, sized for A4 width ── */
  .home-machine-grid {
    display: grid !important;
    grid-template-columns: repeat(3, 1fr) !important;
    gap: 8pt !important;
    break-inside: avoid;
    page-break-inside: avoid;
  }
  .home-chart-grid {
    display: grid !important;
    grid-template-columns: 1fr 1fr !important;
    gap: 8pt !important;
    break-inside: avoid;
    page-break-inside: avoid;
  }
  .home-chart-card {
    break-inside: avoid;
    page-break-inside: avoid;
    box-shadow: none !important;
    border: 1pt solid #e3e1ee !important;
    border-radius: 9pt !important;
    padding: 10pt 11pt 8pt !important;
    background: #fff !important;
  }
  .home-chart-card__title { font-size: 7pt !important; color: #4a4870 !important; }
  .home-chart-card__big   { font-size: 13pt !important; color: #1a1830 !important; }
  .home-chart-card__sub   { font-size: 7pt !important; color: #8a87a8 !important; }
  .home-chart svg { max-height: 150pt; }
  /* Horizontal-bar charts (export readiness / low-stock) keep segment
     colours and read crisply at print size. */
  .home-hbar-name  { font-size: 7.4pt !important; }
  .home-hbar-total { font-size: 7.4pt !important; }
  .home-hbar-track { background: #eceaf3 !important; }
  .home-legend { font-size: 6.8pt !important; color: #6b6890 !important; }

  /* ── Attention panel — never split a row, keep the whole panel together ── */
  .home-attn {
    break-inside: avoid;
    page-break-inside: avoid;
    box-shadow: none !important;
    border: 1pt solid #e3e1ee !important;
    border-radius: 9pt !important;
  }
  .home-attn__head {
    border-bottom: 1pt solid #e3e1ee !important;
    padding: 9pt 12pt !important;
    background: #f6f5fd !important;
  }
  .home-attn__head h3 { font-size: 11.5pt !important; color: #1a1830 !important; }
  .home-attn__count {
    background: #fee2e2 !important;
    color: #991b1b !important;
    font-size: 7.4pt !important;
  }
  .home-attn-row {
    break-inside: avoid;
    page-break-inside: avoid;
    padding: 7pt 12pt !important;
    border-bottom: 1pt solid #ececf3 !important;
  }
  .home-attn-ico { width: 22pt !important; height: 22pt !important; font-size: 9pt !important; }
  .home-attn-ico.i-stuck { background: #fee2e2 !important; }
  .home-attn-ico.i-hold  { background: #fef3c7 !important; }
  .home-attn-ico.i-tally { background: #e0e7ff !important; }
  .home-attn-ico.i-stock { background: #cffafe !important; }
  .home-attn-row strong { font-size: 8pt !important; }
  .home-attn-row .home-attn-sub { font-size: 7.2pt !important; color: #8a87a8 !important; }

  /* Keep the chart/grid containers intact across page breaks. */
  .home-machine-grid, .home-chart-grid, .home-kpi-grid { break-inside: avoid; }

  /* ── Print-only footer — thin accent rule + caption after all content ── */
  .home-dashboard::after {
    content: "Generated from Orders ERP  ·  nysatex.com";
    display: block;
    margin-top: 14pt;
    padding-top: 7pt;
    border-top: 1pt solid #ddd9f0;
    font-family: "Inter", system-ui, sans-serif;
    font-size: 7pt;
    font-weight: 600;
    letter-spacing: .08em;
    text-transform: uppercase;
    color: #9a97b5;
    text-align: center;
    break-inside: avoid;
  }
}

/* ─── Batch action bar ─────────────────────────────────────────────────────
   Sticky bottom strip rendered by listModule when `batchActions: true` AND at
   least one row is selected. Lives at #module-workspace > #lst-batch-bar so
   it sits OUTSIDE the section-card overflow context (sticky bottom only works
   from a parent that's not clipping its children). On mobile we float above
   the bottom-nav so the user can still reach the primary tabs.
*/
@keyframes sheet-slide-up {
  from { transform: translateY(100%); opacity: 0; }
  to   { transform: none; opacity: 1; }
}
/* Sits inside the section card, above the filter chips. Sticks to the top
   of the table area while scrolling so the action bar is always reachable
   without losing track of the filter chips below. */
.batch-action-bar {
  display: flex;
  align-items: center;
  gap: 12px;
  margin: 0 16px 8px;
  padding: 10px 14px;
  background: color-mix(in srgb, var(--primary) 8%, var(--surface));
  border: 1px solid color-mix(in srgb, var(--primary) 22%, var(--line));
  border-radius: 10px;
  box-shadow: var(--shadow-sm);
  position: sticky;
  top: 8px;
  z-index: 6;
  animation: row-fade-in 200ms cubic-bezier(0.2, 0, 0, 1);
}
.batch-action-bar[hidden] { display: none; }
.batch-action-bar__count {
  font-weight: 600;
  color: var(--primary);
  font-variant-numeric: tabular-nums;
}
@media (max-width: 768px) {
  .batch-action-bar {
    flex-wrap: wrap;
    margin: 0 12px 8px;
    padding: 10px 12px;
  }
  .batch-action-bar #lst-batch-status { flex: 1; min-width: 140px; }
}

/* Checkbox column — narrow, centered, no padding spill. */
#lst-table-wrap > table th.col-checkbox,
#lst-table-wrap > table td.col-checkbox {
  width: 36px;
  padding: 8px 10px !important;
  text-align: center;
  vertical-align: middle;
}
#lst-table-wrap > table input.row-checkbox,
#lst-table-wrap > table input.row-checkbox-all {
  cursor: pointer;
  width: 16px;
  height: 16px;
  margin: 0;
  accent-color: var(--primary);
}
#lst-table-wrap > table td.col-checkbox > label.row-checkbox-label {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  height: 100%;
  min-height: 28px;
  cursor: pointer;
  margin: 0;
}

/* ─── Batch progress card (persistent during multi-chunk batch) ───────── */
#app-progress-stack {
  position: fixed;
  left: 16px;
  bottom: 16px;
  z-index: 10001;
  display: grid;
  gap: 8px;
  pointer-events: none;
}
@media (max-width: 768px) {
  #app-progress-stack { left: 12px; right: 12px; bottom: calc(16px + env(safe-area-inset-bottom)); }
}
.batch-progress {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 12px;
  box-shadow: var(--shadow-md);
  padding: 14px 16px;
  width: 360px;
  max-width: calc(100vw - 24px);
  pointer-events: auto;
  animation: row-fade-in 220ms cubic-bezier(0.2, 0, 0, 1);
}
.batch-progress.fade-out { opacity: 0; transition: opacity 200ms ease; }
.batch-progress__head {
  display: flex; justify-content: space-between; align-items: baseline; gap: 8px;
  margin-bottom: 8px;
}
.batch-progress__title { font-size: .9rem; color: var(--text); }
.batch-progress__count { font-variant-numeric: tabular-nums; color: var(--muted); font-size: .85rem; }
.batch-progress__bar {
  height: 6px;
  background: var(--surface-sunken);
  border-radius: 3px;
  overflow: hidden;
  margin: 4px 0 10px;
}
.batch-progress__fill {
  height: 100%;
  background: var(--primary);
  border-radius: 3px;
  transition: width 220ms cubic-bezier(0.2, 0, 0, 1);
}
.batch-progress__stats {
  display: flex; gap: 12px; align-items: center; flex-wrap: wrap;
  font-size: .78rem; color: var(--muted);
}
.batch-progress__stat--ok       { color: var(--success); }
.batch-progress__stat--conflict { color: var(--amber); }
.batch-progress__stat--missing  { color: var(--text-secondary); }
.batch-progress__stat--failed   { color: var(--danger); }
.batch-progress__cancel {
  margin-left: auto;
  padding: 4px 10px;
  font-size: .75rem;
}
.batch-progress__cancel kbd {
  font-family: "JetBrains Mono", monospace;
  font-size: .7rem;
  background: var(--surface-sunken);
  border: 1px solid var(--line);
  border-radius: 3px;
  padding: 0 4px;
  margin-left: 4px;
}

/* ─── Per-row state during batch ──────────────────────────────────────── */
@keyframes row-pulse        { 50% { opacity: .85; } }
@keyframes row-flash-ok     { 0% { background: color-mix(in srgb, var(--success) 28%, transparent); } 100% { background: transparent; } }
tr.row-batch-saving {
  opacity: .65;
  animation: row-pulse 1.2s ease-in-out infinite;
  pointer-events: none;
}
tr.row-batch-ok {
  animation: row-flash-ok 1s ease-out;
}
tr.row-batch-conflict {
  background: color-mix(in srgb, var(--amber) 12%, transparent) !important;
  border-left: 3px solid var(--amber);
}
tr.row-batch-missing {
  opacity: .55;
  text-decoration: line-through;
  text-decoration-color: var(--muted);
}
tr.row-batch-failed {
  background: color-mix(in srgb, var(--danger) 10%, transparent) !important;
  border-left: 3px solid var(--danger);
}

/* ─── Disable nav during batch ────────────────────────────────────────── */
#module-workspace.batch-running .chip-row,
#module-workspace.batch-running #lst-search,
#module-workspace.batch-running #lst-pager {
  pointer-events: none;
  opacity: .55;
}

/* ─── Summary modal layout ────────────────────────────────────────────── */
.batch-summary { display: grid; gap: 14px; min-width: 320px; }
.batch-summary__head {
  font-family: "Instrument Serif", Georgia, serif;
  font-size: 1.4rem;
}
.batch-summary__head--ok       { color: var(--success); }
.batch-summary__head--partial  { color: var(--amber); }
.batch-summary__section { display: grid; gap: 6px; }
.batch-summary__section-title {
  font-size: .82rem; text-transform: uppercase; letter-spacing: .06em;
  color: var(--muted); font-weight: 600;
}
.batch-summary__list {
  list-style: none; margin: 0; padding: 0;
  display: grid; gap: 4px;
  max-height: 260px; overflow-y: auto;
  font-size: .88rem;
}
.batch-summary__list li {
  padding: 6px 10px;
  background: var(--surface-sunken);
  border-radius: 6px;
  font-variant-numeric: tabular-nums;
}
.batch-summary__actions {
  display: flex; gap: 8px; justify-content: flex-end;
}

/* ─── Order Item detail modal (Kanban card click) ─────────────────────── */
.oi-detail { position: relative; width: 100%; overscroll-behavior: contain; }
.oi-detail__close {
  position: absolute;
  top: 10px;
  right: 12px;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 999px;
  width: 30px;
  height: 30px;
  font-size: 18px;
  line-height: 1;
  cursor: pointer;
  color: var(--muted);
  z-index: 2;
  transition: background 120ms ease, color 120ms ease;
}
.oi-detail__close:hover { background: var(--surface-sunken); color: var(--text); }
.oi-detail__grid {
  display: grid;
  grid-template-columns: 280px 1fr;
  gap: 0;
}
.oi-detail__image-wrap {
  background: linear-gradient(135deg, var(--surface-sunken) 0%, var(--surface-muted) 100%);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: 18px 14px;
  min-height: 320px;
}
.oi-detail__image {
  display: block;
  max-width: 100%;
  max-height: 320px;
  border-radius: 10px;
  box-shadow: var(--shadow-md);
  background: var(--surface);
}
.oi-detail__image-fallback {
  width: 220px; height: 220px;
  display: flex; align-items: center; justify-content: center;
  background: var(--surface);
  border: 1px dashed var(--line);
  border-radius: 12px;
  color: var(--muted);
  font-family: "Instrument Serif", Georgia, serif;
  font-size: 1.1rem;
}
.oi-detail__caption {
  margin-top: 10px;
  font-family: "JetBrains Mono", monospace;
  font-size: .82rem;
  color: var(--text-secondary);
  letter-spacing: .04em;
}
.oi-detail__body {
  padding: 22px 26px 22px 22px;
  display: flex;
  flex-direction: column;
  gap: 14px;
  min-width: 0;
  max-height: 80vh;
  overflow-y: auto;
}
.oi-detail__head h3 {
  margin: 0;
  font-family: "Instrument Serif", Georgia, serif;
  font-weight: 400;
  font-size: 1.7rem;
  line-height: 1.15;
  color: var(--text);
}
.oi-detail__head .oi-detail__sr {
  color: var(--muted);
  font-size: 1.1rem;
  font-family: inherit;
}
.oi-detail__head p {
  margin: 4px 0 0;
  color: var(--muted);
  font-size: .9rem;
}
.oi-detail__pills { display: flex; gap: 6px; flex-wrap: wrap; }
.oi-detail__stats {
  display: grid;
  grid-template-columns: 130px 1fr;
  gap: 6px 14px;
  margin: 6px 0 0;
  font-size: .9rem;
}
.oi-detail__stats dt {
  color: var(--muted);
  font-size: .76rem;
  text-transform: uppercase;
  letter-spacing: .06em;
  font-weight: 600;
  align-self: center;
}
.oi-detail__stats dd {
  margin: 0;
  color: var(--text);
}
.oi-detail__stats .oi-detail__num {
  font-variant-numeric: tabular-nums;
  font-weight: 500;
}

/* ── inline-editable detail cell (Factory pkgs et al.) ───────────────
   Wrapping rules: the dd is min-width:0 inside the 1fr grid column so
   long chip rows can shrink and wrap instead of forcing horizontal
   overflow on phones. Each chip is white-space:nowrap — a single
   number stays intact, the row breaks between chips. The textarea
   auto-grows to ~6 lines then scrolls vertically; never pushes
   siblings. Tap target ≥44px (Apple HIG / Material). */
.oi-detail__stats dd.oi-detail__edit-cell {
  min-width: 0;
}
.oi-detail__edit {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px;
  width: 100%;
  min-height: 44px;
  padding: 6px 10px;
  margin: -6px -10px;
  border-radius: 8px;
  cursor: pointer;
  transition: background var(--transition, .15s);
  overflow-wrap: anywhere;
  box-sizing: border-box;
}
.oi-detail__edit[data-readonly="1"] {
  cursor: default;
}
.oi-detail__edit:hover,
.oi-detail__edit:focus-visible,
.oi-detail__edit--active {
  background: var(--surface-hover, #f0eee9);
  outline: none;
}
.oi-detail__edit-value {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  min-width: 0;
  flex: 1 1 auto;
}
.oi-detail__edit-empty {
  color: var(--muted);
}
.oi-detail__edit-hint {
  font-size: 11px;
  color: var(--muted);
  opacity: .7;
  flex: 0 0 auto;
  margin-left: auto;
}
.pkg-chip {
  display: inline-block;
  padding: 3px 10px;
  border-radius: 999px;
  background: var(--surface-sunken, #eee9df);
  color: var(--text);
  font-size: 13px;
  line-height: 1.3;
  border: 1px solid var(--line);
  white-space: nowrap;
  max-width: 100%;
  font-variant-numeric: tabular-nums;
}
.oi-detail__edit-input {
  font: inherit;
  font-size: 16px;            /* prevents iOS Safari auto-zoom on focus */
  line-height: 1.4;
  padding: 8px 10px;
  width: 100%;
  min-height: 44px;
  max-height: 160px;
  border: 1px solid var(--primary, #2f6f4f);
  border-radius: 6px;
  background: var(--surface, #fff);
  color: var(--text);
  resize: none;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  word-break: break-word;
  overflow-wrap: anywhere;
  box-sizing: border-box;
}
.oi-detail__edit-input:focus {
  outline: none;
  box-shadow: 0 0 0 3px var(--primary-glow, rgba(47, 111, 79, .15));
}
.oi-detail__edit-value--saving {
  opacity: .55;
}
.oi-detail__edit-value--saved {
  background: var(--success-glow, rgba(31, 138, 76, .15));
  border-radius: 6px;
  padding: 2px 6px;
  margin: -2px -6px;
  transition: background .4s;
}
@media (max-width: 480px) {
  /* Keep the affordance visible on phones — hiding it left PWA users
     with no clue the field was tappable (2026-06-11). flex-wrap lets it
     drop to its own line instead of squeezing the chips. */
  .oi-detail__edit-hint { font-size: 10px; }
}
.oi-detail__remarks {
  background: var(--surface-sunken);
  border-left: 3px solid var(--info);
  border-radius: 4px;
  padding: 10px 12px;
  font-size: .88rem;
  color: var(--text-secondary);
  line-height: 1.5;
}
.oi-detail__remarks strong {
  display: block;
  margin-bottom: 2px;
  color: var(--text);
  font-size: .76rem;
  text-transform: uppercase;
  letter-spacing: .06em;
}
.oi-detail__brief {
  background: var(--surface-sunken);
  border-radius: 8px;
  padding: 12px 14px;
}
.oi-detail__brief-head {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 12px;
  margin-bottom: 8px;
}
.oi-detail__brief-head strong {
  font-size: .76rem;
  text-transform: uppercase;
  letter-spacing: .06em;
  color: var(--muted);
  font-weight: 600;
}
.oi-detail__tally {
  font-variant-numeric: tabular-nums;
  font-weight: 600;
  font-size: .85rem;
}
.oi-detail__brief-list { display: grid; gap: 4px; }
.oi-detail__brief-row {
  display: grid;
  grid-template-columns: 22px 1fr 1fr 80px;
  gap: 8px;
  align-items: center;
  background: var(--surface);
  padding: 6px 10px;
  border-radius: 5px;
  font-size: .85rem;
}
.oi-detail__brief-idx {
  color: var(--muted);
  font-variant-numeric: tabular-nums;
  font-size: .75rem;
}
.oi-detail__actions {
  display: flex;
  gap: 8px;
  justify-content: flex-end;
  margin-top: 4px;
  padding-top: 12px;
  border-top: 1px solid var(--line);
}
@media (max-width: 720px) {
  .oi-detail__grid { grid-template-columns: 1fr; }
  .oi-detail__image-wrap { min-height: 220px; padding: 14px; }
  .oi-detail__image { max-height: 220px; }
  .oi-detail__body { padding: 16px; max-height: none; }
  .oi-detail__head h3 { font-size: 1.4rem; }
  .oi-detail__actions { flex-wrap: wrap; }
}

/* ─── Brief entries — multi-row pending list ──────────────────────────── */
.be-pending {
  display: grid;
  gap: 6px;
}
.be-pending-row {
  display: grid;
  grid-template-columns: 28px 1fr 110px 32px;
  gap: 8px;
  align-items: center;
  padding: 0;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 6px;
  transition: border-color 120ms ease, background 120ms ease;
}
.be-pending-row:hover { border-color: color-mix(in srgb, var(--primary) 24%, var(--line)); }
.be-pending-row--error {
  border-color: var(--danger);
  background: color-mix(in srgb, var(--danger) 6%, var(--surface));
}
.be-pending-row__idx {
  text-align: center;
  font-variant-numeric: tabular-nums;
  font-size: .78rem;
  color: var(--muted);
}
.be-pending-row .lst-form-input {
  border: 0;
  border-radius: 0;
  background: transparent;
  padding: 8px 10px;
  font-size: .88rem;
}
.be-pending-row .lst-form-input:focus {
  outline: 2px solid var(--primary);
  outline-offset: -2px;
  background: var(--surface);
}
.be-pending-row__del {
  background: transparent;
  border: 0;
  cursor: pointer;
  font-size: 1.05rem;
  line-height: 1;
  color: var(--muted);
  padding: 6px;
  border-radius: 4px;
  transition: color 120ms ease, background 120ms ease;
}
.be-pending-row__del:hover { color: var(--danger); background: var(--danger-light); }
.be-pending-row__err {
  font-size: .76rem;
  color: var(--danger);
  padding: 0 12px 6px 44px;
  margin-top: -2px;
}
@media (max-width: 640px) {
  .be-pending-row {
    grid-template-columns: 24px 1fr 90px 28px;
    gap: 4px;
  }
  .be-pending-row .lst-form-input { font-size: 16px; }  /* prevent iOS zoom */
}

/* ─── Brief entries — full-page view ──────────────────────────────────── */
.be-page-header {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 14px 0 18px;
  border-bottom: 1px solid var(--line);
  margin-bottom: 22px;
  position: sticky;
  top: 0;
  background: var(--bg);
  z-index: 4;
}
.be-page__back {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 6px 12px;
  font-size: .85rem;
  white-space: nowrap;
}
.be-page__title h2 {
  font-family: "Instrument Serif", Georgia, serif;
  font-weight: 400;
  font-size: 1.6rem;
  margin: 0;
  line-height: 1.1;
}
.be-page__title p {
  margin: 4px 0 0;
  color: var(--muted);
  font-size: .85rem;
}
.be-page-body {
  max-width: 880px;
  margin: 0 auto;
  padding-bottom: 60px;
}
.be-page-body > div {
  max-width: none !important;  /* override the panel's 720px self-cap */
}
@media (max-width: 768px) {
  .be-page-header { padding: 10px 0 12px; margin-bottom: 14px; flex-wrap: wrap; }
  .be-page__title h2 { font-size: 1.3rem; }
  .be-page-body { padding-bottom: 96px; }  /* room for bottom-nav */
  .be-page__share > span:not(.icon) { display: none; }  /* icon-only on phones */
}

/* ─── Brief entries — tally with progress bar ─────────────────────────── */
.be-tally {
  background: var(--surface-sunken);
  border-radius: 10px;
  padding: 14px 16px;
  display: grid;
  gap: 10px;
}
.be-tally__head {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 16px;
}
.be-tally__label {
  font-size: .72rem;
  text-transform: uppercase;
  letter-spacing: .08em;
  color: var(--muted);
  font-weight: 600;
}
.be-tally__num {
  font-family: "Instrument Serif", Georgia, serif;
  font-size: 1.55rem;
  color: var(--text);
  line-height: 1.1;
}
.be-tally__target { color: var(--muted); font-size: 1.05rem; }
.be-tally__status {
  font-weight: 600;
  font-size: .85rem;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.be-tally__bar {
  position: relative;
  height: 8px;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 5px;
  overflow: hidden;
}
.be-tally__fill {
  height: 100%;
  background: var(--primary);
  border-radius: 4px 0 0 4px;
  transition: width 240ms cubic-bezier(0.2, 0, 0, 1), background 200ms ease;
}
.be-tally__overflow {
  position: absolute;
  top: 0; right: 0;
  height: 100%;
  background: repeating-linear-gradient(
    45deg,
    var(--danger) 0,
    var(--danger) 6px,
    color-mix(in srgb, var(--danger) 60%, transparent) 6px,
    color-mix(in srgb, var(--danger) 60%, transparent) 12px
  );
  border-radius: 0 4px 4px 0;
}

/* Save button "blocked" state — visually muted but not the standard disabled
   grey (which we use for "fields incomplete"). Communicates "you have data,
   but it would push you over the cap". */
.btn--blocked {
  background: var(--surface-sunken) !important;
  color: var(--danger) !important;
  border-color: var(--danger-border, var(--danger)) !important;
  cursor: not-allowed;
}

/* Dual-fill tally bar: saved (solid primary), pending (striped lighter primary) */
.be-tally__bar { position: relative; }
.be-tally__fill--saved {
  position: absolute; left: 0; top: 0; bottom: 0;
  z-index: 2;
}
.be-tally__fill--pending {
  position: absolute; top: 0; bottom: 0;
  background: repeating-linear-gradient(
    45deg,
    color-mix(in srgb, var(--primary) 60%, transparent) 0,
    color-mix(in srgb, var(--primary) 60%, transparent) 6px,
    color-mix(in srgb, var(--primary) 30%, transparent) 6px,
    color-mix(in srgb, var(--primary) 30%, transparent) 12px
  );
  z-index: 1;
  transition: width 200ms ease, left 200ms ease;
}
.be-tally__pending {
  color: var(--primary);
  font-family: "Instrument Serif", Georgia, serif;
  font-size: 1.2rem;
  margin: 0 4px;
}

/* Tally — small "not yet saved" hint below the big number */
.be-tally__unsaved {
  font-size: .75rem;
  color: var(--primary);
  margin-top: 2px;
  font-variant-numeric: tabular-nums;
  opacity: .85;
}

/* GH Index dashboard ─────────────────────────────────────────────────── */
.ghi-wrap { overflow-x: auto; -webkit-overflow-scrolling: touch; }
.ghi-table { border-collapse: collapse; min-width: 100%; font-size: .82rem; }
.ghi-table th, .ghi-table td {
  padding: 8px 10px;
  border-bottom: 1px solid var(--line);
  white-space: nowrap;
  text-align: right;
  font-variant-numeric: tabular-nums;
}
.ghi-table th {
  font-weight: 600;
  color: var(--muted);
  text-transform: uppercase;
  letter-spacing: .04em;
  font-size: .7rem;
  background: var(--surface-sunken);
}
.ghi-table td:first-child,
.ghi-table th:first-child {
  text-align: left;
  position: sticky;
  left: 0;
  background: var(--surface);
  z-index: 2;
}
.ghi-table thead th:first-child { background: var(--surface-sunken); }
.ghi-table .ghi-cell-zero    { color: var(--muted); }
.ghi-table .ghi-cell-amber   { color: var(--amber); }
.ghi-table .ghi-cell-success { color: var(--success); }
.ghi-table .ghi-cell-info    { color: var(--info); }
.ghi-table tr:hover td { background: var(--surface-sunken); }
.ghi-table tr.ghi-grand-total td {
  font-weight: 700;
  border-top: 2px solid var(--line);
  background: color-mix(in srgb, var(--primary) 6%, transparent);
}
.ghi-cat-group { border-left: 1px solid var(--line); }

.ghi-metric-toggle { display: inline-flex; gap: 4px; margin-bottom: 12px; }
.ghi-metric-toggle button {
  padding: 4px 12px;
  border: 1px solid var(--line);
  background: var(--surface);
  color: var(--muted);
  cursor: pointer;
  font-size: .82rem;
  border-radius: 999px;
}
.ghi-metric-toggle button.is-active {
  background: var(--primary);
  color: #fff;
  border-color: var(--primary);
}

/* ─── AI Tools module ─────────────────────────────────────────────────── */
.ai-section-head { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; margin-bottom: 4px; }
.ai-section-head h4 { font-family: "Instrument Serif", Georgia, serif; font-weight: 400; font-size: 1.25rem; margin: 0; color: var(--text); }
.ai-meta-pill { font-size: .72rem; color: var(--primary); background: var(--primary-light); padding: 2px 10px; border-radius: 999px; font-variant-numeric: tabular-nums; }
.ai-hint { color: var(--muted); font-size: .82rem; margin: 0 0 10px; }
.ai-hint code { font-family: "JetBrains Mono", monospace; background: var(--surface-sunken); padding: 1px 6px; border-radius: 3px; font-size: .78rem; }
.ai-tools-list { display: grid; gap: 6px; }
.ai-tool { padding: 10px 12px; background: var(--surface-sunken); border: 1px solid var(--line); border-radius: 6px; }
.ai-tool__head { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.ai-tool__name { font-family: "JetBrains Mono", monospace; font-size: .85rem; color: var(--primary); font-weight: 600; }
.ai-tool__verb { font-family: "JetBrains Mono", monospace; font-size: .68rem; padding: 1px 6px; border-radius: 3px; font-weight: 600; letter-spacing: .04em; }
.ai-tool__verb--get    { background: color-mix(in srgb, var(--info) 18%, transparent); color: var(--info); }
.ai-tool__verb--post   { background: color-mix(in srgb, var(--success) 18%, transparent); color: var(--success); }
.ai-tool__verb--put    { background: color-mix(in srgb, var(--amber) 18%, transparent); color: var(--amber); }
.ai-tool__verb--delete { background: color-mix(in srgb, var(--danger) 18%, transparent); color: var(--danger); }
.ai-tool__path { font-family: "JetBrains Mono", monospace; font-size: .76rem; color: var(--text-secondary); }
.ai-tool__desc { font-size: .82rem; color: var(--muted); margin-top: 4px; line-height: 1.4; }
.ai-form-row { display: flex; gap: 8px; margin-bottom: 10px; }
.ai-form-row .lst-form-input { flex: 1; font-family: "JetBrains Mono", monospace; }
.ai-pre {
  background: var(--surface-sunken); border: 1px solid var(--line); border-radius: 6px;
  padding: 12px; font-family: "JetBrains Mono", monospace; font-size: .76rem;
  max-height: 360px; overflow: auto; margin: 0; color: var(--text-secondary);
}
.ai-find-out { display: grid; gap: 12px; }
.ai-find-section { background: var(--surface-sunken); border-radius: 6px; padding: 10px 12px; }
.ai-find-section__head { font-size: .72rem; text-transform: uppercase; letter-spacing: .06em; color: var(--muted); font-weight: 600; margin-bottom: 6px; }
.ai-find-list { list-style: none; margin: 0; padding: 0; display: grid; gap: 4px; font-size: .85rem; }
.ai-find-list code { background: transparent; color: var(--primary); font-family: "JetBrains Mono", monospace; font-size: .78rem; }

/* Searchable Sr No dropdown for the export-entry form */
.ee-sr-list {
  position: absolute;
  top: 100%;
  left: 0; right: 0;
  margin-top: 4px;
  max-height: 280px;
  overflow-y: auto;
  background: var(--surface-popover, var(--surface));
  border: 1px solid var(--line);
  border-radius: 8px;
  box-shadow: var(--shadow-md);
  z-index: 12;
  padding: 4px;
}
.ee-sr-list[hidden] { display: none; }
.ee-sr-row {
  display: grid;
  grid-template-columns: 60px 110px 1fr 70px;
  gap: 10px;
  align-items: center;
  width: 100%;
  padding: 8px 10px;
  background: transparent;
  border: 0;
  border-radius: 6px;
  cursor: pointer;
  text-align: left;
  font-size: .85rem;
  color: var(--text);
  transition: background 80ms ease;
}
.ee-sr-row:hover { background: var(--surface-sunken); }
.ee-sr-row__sr      { font-variant-numeric: tabular-nums; color: var(--muted); font-size: .8rem; }
.ee-sr-row__design  { font-family: "JetBrains Mono", monospace; color: var(--primary); font-size: .82rem; }
.ee-sr-row__item    { color: var(--text); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.ee-sr-row__yards   { color: var(--muted); font-variant-numeric: tabular-nums; font-size: .78rem; text-align: right; }
.ee-sr-empty { padding: 14px; text-align: center; color: var(--muted); font-size: .85rem; }

/* ═══════════════════════════════════════════════════════════════════════
   Accessibility & mobile baseline (added 2026-05-02)
   ═══════════════════════════════════════════════════════════════════════ */

/* Touch targets — meet Apple HIG (44 px) and Material (48 px). Apply only
   in coarse-pointer (touch) viewports so desktop density isn't bloated. */
@media (pointer: coarse) {
  button,
  [role="button"],
  input[type="checkbox"],
  input[type="radio"],
  .chip,
  .btn {
    min-height: 44px;
    min-width: 44px;
  }
  .be-pending-row__del,
  .be-del,
  td.del button {
    min-height: 32px;
    min-width: 32px;
    padding: 6px;
  }
}

/* Global reduced-motion fallback — guarantees animations are off if a
   per-component rule was missed. */
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}

/* Focus-visible — high-contrast outline for keyboard nav. Mouse clicks
   stay clean. */
:focus-visible {
  outline: 2px solid var(--primary);
  outline-offset: 2px;
  border-radius: 3px;
}
input:focus-visible,
select:focus-visible,
textarea:focus-visible {
  outline-offset: 0;
}

/* Safe-area insets for iOS notch / Android gesture bar in standalone PWA. */
:root[data-standalone="1"] body {
  padding-top: env(safe-area-inset-top);
  padding-bottom: env(safe-area-inset-bottom);
  padding-left: env(safe-area-inset-left);
  padding-right: env(safe-area-inset-right);
}

/* Mobile responsive — table list fallback to card stack < 768 px.
   Component-level overrides can opt in via .list-table.responsive. */
@media (max-width: 768px) {
  .list-table.responsive thead { display: none; }
  .list-table.responsive tbody,
  .list-table.responsive tr,
  .list-table.responsive td { display: block; width: 100%; }
  .list-table.responsive tr {
    border: 1px solid var(--line);
    border-radius: 6px;
    margin-bottom: 8px;
    padding: 8px 12px;
    background: var(--surface);
  }
  .list-table.responsive td {
    border: 0;
    padding: 4px 0;
    display: flex;
    justify-content: space-between;
    gap: 12px;
  }
  .list-table.responsive td::before {
    content: attr(data-label);
    font-size: .7rem;
    text-transform: uppercase;
    color: var(--muted);
    letter-spacing: .04em;
  }
}

/* Just-returned row highlight — soft pulse that draws the eye to the
   row the user came back from, then fades cleanly. Two beats of indigo
   wash + a left-edge accent bar keep it readable on any background. */
@keyframes returnedFlash {
  0%   { background-color: rgba(99, 102, 241, .00); box-shadow: inset 4px 0 0 0 rgba(99, 102, 241, 0); }
  10%  { background-color: rgba(99, 102, 241, .28); box-shadow: inset 4px 0 0 0 rgba(99, 102, 241, .85); }
  35%  { background-color: rgba(99, 102, 241, .14); box-shadow: inset 4px 0 0 0 rgba(99, 102, 241, .60); }
  55%  { background-color: rgba(99, 102, 241, .22); box-shadow: inset 4px 0 0 0 rgba(99, 102, 241, .70); }
  100% { background-color: transparent;             box-shadow: inset 4px 0 0 0 rgba(99, 102, 241, 0); }
}
/* Specificity bump: `#lst-table-wrap > table > tbody > tr` (1 ID + 3
   tags) sets the row-fade-in animation and would otherwise win. We
   match that specificity AND add the class qualifier so this rule
   beats it whenever the class is present. !important on animation is
   belt-and-braces against future rule additions. */
#lst-table-wrap tr.is-just-returned,
tr.is-just-returned,
tbody tr.is-just-returned {
  animation: returnedFlash 2.2s cubic-bezier(.4, 0, .2, 1) both !important;
  transition: background-color .35s ease-out, box-shadow .35s ease-out;
}
tr.is-just-returned > td {
  animation: returnedTextLift 2.2s ease-out both;
  font-weight: 600;
}
@keyframes returnedTextLift {
  0%, 100% { color: inherit; }
  10%, 55% { color: var(--text); }
}
@media (prefers-reduced-motion: reduce) {
  #lst-table-wrap tr.is-just-returned,
  tr.is-just-returned,
  tbody tr.is-just-returned {
    animation: none !important;
    background-color: rgba(99, 102, 241, .12);
    box-shadow: inset 4px 0 0 0 rgba(99, 102, 241, .65);
  }
  tr.is-just-returned > td { animation: none; }
}

/* ═══════════════════════════════════════════════════════════════════════
   UI consistency audit — 2026-05-03
   Extracted from inline-style duplication in app.js. Visual outcome is
   identical to the prior inline tile/strip rendering; only the source of
   truth has moved. See CHANGELOG.md "UI consistency audit".
   ═══════════════════════════════════════════════════════════════════════ */

/* Metric tile — was duplicated as `tileCss`/`tileLabelCss`/`tileValCss`
   string concatenation in three render paths (order detail header,
   shipment-planner totals, brief-entries detail card). Same visual. */
.metric-tile {
  padding: 12px 14px;
  background: var(--surface-sunken);
  border: 1px solid var(--line);
  border-radius: 10px;
  display: flex;
  flex-direction: column;
  gap: 3px;
  min-width: 0;
}
.metric-tile--accent {
  border-color: rgba(99, 102, 241, .3);
  background: rgba(99, 102, 241, .05);
}
.metric-tile--small { padding: 8px 12px; border-radius: 8px; justify-content: center; }
.metric-tile__label {
  font-size: .7rem;
  color: var(--muted);
  text-transform: uppercase;
  letter-spacing: .04em;
  font-weight: 600;
}
.metric-tile__value {
  font-size: 1.15rem;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  color: var(--text);
  line-height: 1.15;
}
.metric-tile__value--sm { font-size: 1rem; line-height: 1.3; font-weight: 600; }
.metric-tile__value--accent { color: var(--primary, #4f46e5); }
.metric-tile__value-suffix {
  font-size: .82rem;
  font-weight: 500;
  color: var(--muted);
  margin-left: 5px;
}

/* Context strip — inline label/value pairs over a sunken pill, used in
   the order-item detail card and the shipment-plan summary. Replaces a
   recurring `display:flex;flex-wrap;gap:6px 24px;…` inline blob. */
.context-strip {
  display: flex;
  flex-wrap: wrap;
  gap: 6px 24px;
  font-size: .85rem;
  padding: 10px 14px;
  background: var(--surface-sunken);
  border: 1px solid var(--line);
  border-radius: 10px;
  align-items: baseline;
}
.context-strip__pair { min-width: 0; }
.context-strip__label {
  color: var(--muted);
  font-weight: 500;
  margin-right: 6px;
}
.context-strip__value { color: var(--text); font-weight: 600; }
.context-strip__meta { margin-left: auto; color: var(--muted); font-size: .78rem; }

/* Order-item detail card responsive — switches the 2-col body to a single
   column on narrow screens so the 280px image column doesn't crush the
   stat tiles. Inline `grid-template-columns:minmax(0,1fr) 280px` is the
   default; this rule overrides it under 720px. */
@media (max-width: 720px) {
  .oi-detail-card__body {
    grid-template-columns: 1fr !important;
    gap: 16px !important;
  }
  .oi-detail-card__image {
    position: static !important;
    max-width: 100%;
  }
  .oi-detail-card__image img { max-height: 220px; }
}

/* kbd badges inside buttons (e.g. the "press N" hint on +New). The
   `.batch-progress__cancel kbd` rule exists for one specific button;
   this generalises so every `.btn > kbd` inherits a consistent look,
   removing the inline `style="font-size:.7rem;…"` from app.js. */
.btn kbd {
  font-family: "JetBrains Mono", monospace;
  font-size: .7rem;
  font-weight: 600;
  padding: 1px 5px;
  margin-left: 6px;
  border-radius: 3px;
  background: rgba(255, 255, 255, .2);
  border: 1px solid rgba(255, 255, 255, .3);
  line-height: 1;
}
/* On non-primary buttons (ghost/danger/default) the white-on-white kbd
   is unreadable — use a sunken surface tone instead. */
.btn.ghost kbd,
.btn:not(.primary):not(.danger) kbd {
  background: var(--surface-sunken);
  border-color: var(--line);
  color: var(--text-secondary);
}

/* Pill variants the chip system uses ("info", "amber", "success",
   "primary", "danger") — these were referenced via templated class names
   like `pill pill--${cMap[cv]}` and `pill pill--${statusVariant}` but
   only existed for specific status names (balance/running/ok/pending).
   Without these, country/status pills rendered as plain rounded text
   with no background — the user read this as "broken alignment". */
.pill--info     { background: color-mix(in srgb, var(--info) 18%, transparent);          color: var(--info-strong, var(--info)); }
.pill--success  { background: color-mix(in srgb, var(--success) 18%, transparent);       color: var(--success-dark, var(--success)); }
.pill--primary  { background: color-mix(in srgb, var(--primary) 18%, transparent);       color: var(--primary, #4338ca); }
.pill--amber    { background: color-mix(in srgb, var(--amber) 18%, transparent);         color: var(--amber-dark, var(--amber)); }
.pill--danger   { background: color-mix(in srgb, var(--danger) 18%, transparent);        color: var(--danger-strong, var(--danger)); }

/* ═══════════════════════════════════════════════════════════════════════
   iOS PWA + mobile layout fixes (2026-05-04)
   Targeted fixes for: (1) home stat-cards clipping at the right edge on
   iPhone, (2) Board view skeleton/card content clipped under flex parent
   with overflow:auto, (3) bottom-nav overlapping the last row of content.
   All rules below are mobile-gated (touch / narrow viewport) so desktop
   stays pixel-identical.
   ═══════════════════════════════════════════════════════════════════════ */

/* 1. Prevent grid/flex blow-out so content respects the viewport width.
   Without min-width:0, children with long words or fixed-width inner
   elements push the parent past 100vw and the rightmost stat-card clips
   off the right edge of the screen. */
.layout, .main, #module-workspace, .home-dashboard,
.stat-grid, .progress-grid, .kb-board, .kb-col, .kb-card {
  min-width: 0;
}

/* 2. Honour iOS safe-area-inset-right on the workspace so content never
   slides under the rounded-corner / notch area in landscape. The body
   already pads safe-area-left/right (line ~1572), but in PWA standalone
   mode the workspace itself can extend past the safe-area when its own
   padding is symmetric — explicitly mirror the inset here. */
@media (hover: none) and (pointer: coarse), (max-width: 768px) {
  #module-workspace {
    padding-left: max(14px, env(safe-area-inset-left));
    padding-right: max(14px, env(safe-area-inset-right));
    /* Bottom-nav clearance: 76px nav + safe-area + a small breathing gap
       so the last row of content isn't tucked under the bar. Wins over
       the earlier 96px static padding via specificity. */
    padding-bottom: calc(96px + env(safe-area-inset-bottom));
  }
  .home-dashboard {
    padding-left: 0;
    padding-right: 0;
    padding-bottom: calc(80px + env(safe-area-inset-bottom));
  }
}

/* 3. Stat-grid: drop minmax to 150px on narrow viewports so 2 cards fit
   side-by-side on a 375px iPhone instead of forcing one giant 220px+
   column that can overflow when paired with another card on iPad portrait
   / landscape phone. Below 480px we collapse to a single column. */
@media (max-width: 768px) {
  .stat-grid {
    grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  }
}
@media (max-width: 420px) {
  .stat-grid {
    grid-template-columns: 1fr;
  }
}

/* 4. Board view (kanban) — on mobile the columns stack vertically. The
   default `.kb-col__body { overflow-y: auto }` requires a constrained
   parent height; without it iOS Safari collapses the body and the
   skeleton-bar children render as tiny grey blocks (which the user read
   as "empty cards with checkboxes"). Switch to visible overflow on
   mobile so the column grows to fit its children. */
@media (max-width: 768px) {
  .kb-col__body { overflow-y: visible; }
  .kb-card { width: 100%; }
  .kb-card__title { min-width: 0; }
  .kb-card__title > span:first-child {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
}

/* 5. iOS sticky header: when body has a safe-area-inset-top padding (line
   ~1571), the .main-header `position: sticky; top: 0` already works, but
   the translucent backdrop on mobile (line 1679) needs to extend up into
   the inset so there's no see-through gap when scrolling under the
   status bar. */
@media (display-mode: standalone) {
  .main-header {
    padding-top: max(var(--sp-3), env(safe-area-inset-top));
  }
}

/* 6. Smooth momentum scrolling on touch containers that don't already
   declare it. iOS Safari otherwise jitters on horizontal scrollers. */
.kb-board, .recent-pill-row, .progress-grid, #module-workspace {
  -webkit-overflow-scrolling: touch;
}

/* ─────────────────────────────────────────────────────────────────────────
   7. Mobile card-row layout (rewritten 2026-05-04, replaces #41 patch).

   Goal: every list page on mobile/iOS-PWA renders each <tr> as a coherent
   80-100px card with a leading slot (checkbox and/or 48px thumbnail), a
   title+subtitle stack, and a trailing chevron — matching iOS HIG list
   cells (44pt min tap target) and Material 3 two/three-line list items
   (leading container + headline + supporting text + trailing icon).

   The renderer (app.js → tagMobileLayoutClasses) tags each <tr> with:
     .row-has-checkbox  — batchActions=true (Order Items)
     .row-has-image     — first column is a 48px thumbnail (Order Items,
                          Designs, Shades). Combined when both apply.

   Default (no checkbox, no image): td:nth-child(1) = title,
   td:nth-child(2) = subtitle, rest hidden until card-open. This is
   handled by the legacy block at line 1693+ and is preserved for
   Customers / Sources / etc.

   Layout strategy: turn the <tr> into a flexbox container ONLY when it
   has a leading checkbox or image. The promoted-cell display rules use
   adjacent-sibling selectors (no :has()) so iOS Safari ≥ 13 works.
   ─────────────────────────────────────────────────────────────────── */
/* Default (desktop): the Order Items title cell ships a synthesized
   mobile subtitle (.oi-mobile-sub) that's hidden on wide screens —
   desktop has its own dedicated columns, no duplication. */
.oi-mobile-sub { display: none; }
.oi-mobile-status { display: none; }
.oi-mobile-item { display: none; }

@media (hover: none) and (pointer: coarse), (max-width: 768px) {

  /* ── A. Tighten cards that carry a leading slot ─────────────────────
     Use flexbox to lock the leading column(s), title stack and trailing
     chevron into a single row. Inner blocks then render as plain blocks. */
  #module-workspace table tbody tr.row-has-checkbox,
  #module-workspace table tbody tr.row-has-image {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 12px 14px;
    min-height: 80px;
  }

  /* ── B. Leading checkbox slot (22px circular, 44px tap target) ─────
     Apple HIG 44pt minimum is met via padding around a visually 22px
     control. Keep it neutral — colours come from the global form sheet. */
  #module-workspace table tbody tr.row-has-checkbox > td.col-checkbox {
    display: flex;
    flex: 0 0 auto;
    align-items: center;
    justify-content: center;
    width: 44px;
    height: 44px;
    padding: 0;
    margin: 0;
    border: 0;
    position: static;          /* override legacy absolute pin */
  }
  #module-workspace table tbody tr.row-has-checkbox > td.col-checkbox::before { content: none; }
  #module-workspace table tbody tr.row-has-checkbox > td.col-checkbox input.row-checkbox {
    width: 22px;
    height: 22px;
    margin: 0;
    cursor: pointer;
    accent-color: var(--info, #2563eb);
  }

  /* ── C. Leading thumbnail slot (48×48 rounded, lazy-loaded by JS) ──
     When both checkbox and image are present the image is the cell that
     immediately follows .col-checkbox. When only image is present it's
     td:first-child. Both selectors land here. */
  #module-workspace table tbody tr.row-has-image > td:first-child:not(.col-checkbox),
  #module-workspace table tbody tr.row-has-image > td.col-checkbox + td {
    display: flex;
    flex: 0 0 auto;
    align-items: center;
    justify-content: center;
    width: 48px;
    height: 48px;
    padding: 0;
    margin: 0;
    border: 0;
    background: var(--surface-sunken, #f5f5f4);
    border-radius: 8px;
    overflow: hidden;
  }
  #module-workspace table tbody tr.row-has-image > td:first-child:not(.col-checkbox) > img.row-thumb,
  #module-workspace table tbody tr.row-has-image > td.col-checkbox + td > img.row-thumb {
    width: 48px !important;
    height: 48px !important;
    border-radius: 8px;
    object-fit: cover;
    display: block;
  }
  #module-workspace table tbody tr.row-has-image > td:first-child:not(.col-checkbox)::before,
  #module-workspace table tbody tr.row-has-image > td.col-checkbox + td::before { content: none; }

  /* ── D. Title cell — promoted from the cell that follows the leading
     slot(s). Material 3: weight 500, body-large size. */
  /* D1. Has checkbox + image → title is the 3rd <td>. */
  #module-workspace table tbody tr.row-has-checkbox.row-has-image > td.col-checkbox + td + td {
    display: block;
    flex: 1 1 auto;
    min-width: 0;
    padding: 0;
    margin: 0;
    border: 0;
    font-weight: 700;
    font-size: 16px;
    line-height: 1.3;
    color: var(--text, #1c1917);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    text-align: left;
  }
  /* D2. Has checkbox only → title is the 2nd <td>. */
  #module-workspace table tbody tr.row-has-checkbox:not(.row-has-image) > td.col-checkbox + td {
    display: block;
    flex: 1 1 auto;
    min-width: 0;
    padding: 0;
    margin: 0;
    border: 0;
    font-weight: 700;
    font-size: 16px;
    line-height: 1.3;
    color: var(--text, #1c1917);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    text-align: left;
  }
  /* D3. Has image only (no checkbox) → title is the 2nd <td>. */
  #module-workspace table tbody tr.row-has-image:not(.row-has-checkbox) > td:first-child + td {
    display: block;
    flex: 1 1 auto;
    min-width: 0;
    padding: 0;
    margin: 0;
    border: 0;
    font-weight: 700;
    font-size: 16px;
    line-height: 1.3;
    color: var(--text, #1c1917);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    text-align: left;
  }
  /* Suppress data-labels on promoted cells. */
  #module-workspace table tbody tr.row-has-checkbox.row-has-image > td.col-checkbox + td + td::before,
  #module-workspace table tbody tr.row-has-checkbox:not(.row-has-image) > td.col-checkbox + td::before,
  #module-workspace table tbody tr.row-has-image:not(.row-has-checkbox) > td:first-child + td::before { content: none; }

  /* When a promoted title cell hosts a synthesized .oi-mobile-sub line,
   * the single-line nowrap+ellipsis treatment from D1/D2/D3 above clips
   * the wrapping metadata chips to one line — they overflow off-screen
   * (Export Entries had this: long item names like "MEN POLISHED LACE"
   * spilled past the card's right edge). Allow wrapping in that case. */
  #module-workspace table tbody tr.row-has-checkbox.row-has-image > td.col-checkbox + td + td:has(.oi-mobile-sub),
  #module-workspace table tbody tr.row-has-checkbox:not(.row-has-image) > td.col-checkbox + td:has(.oi-mobile-sub),
  #module-workspace table tbody tr.row-has-image:not(.row-has-checkbox) > td:first-child + td:has(.oi-mobile-sub) {
    white-space: normal !important;
    overflow: visible !important;
    text-overflow: clip !important;
  }
  /* Each flex item inside the sub strip must be allowed to break, otherwise
   * a long single-word chip ("MEN POLISHED LACE") becomes one unbreakable
   * flex item that overflows the card width. min-width:0 + word-break let
   * flex resolve overflow correctly. */
  .oi-mobile-sub > * {
    min-width: 0;
    max-width: 100%;
    overflow-wrap: anywhere;
    word-break: break-word;
  }
  /* The dot separators must NOT wrap (orphan dots look wrong). */
  .oi-mobile-sub .oi-sub-dot { flex: 0 0 auto; }

  /* ── D-mobile-sub: Order Items title cell carries a synthesized
     subtitle line (customer · design# · yards · type pill). On
     desktop the subtitle is hidden — desktop has dedicated columns.
     On mobile (≤768px) we surface it so the card row shows actual
     useful info beyond just the order number.
     The .oi-mobile-title becomes block + ellipsis, then .oi-mobile-sub
     wraps with smaller muted text and inline pills for UNIFORM/REPEAT. */
  #module-workspace table tbody tr.row-has-checkbox.row-has-image > td.col-checkbox + td + td,
  #module-workspace table tbody tr.row-has-checkbox:not(.row-has-image) > td.col-checkbox + td,
  #module-workspace table tbody tr.row-has-image:not(.row-has-checkbox) > td:first-child + td {
    /* Title cell becomes vertical so subtitle stacks below the order#. */
    display: flex !important;
    flex-direction: column;
    /* min-width:0 lets the inner .oi-mobile-sub (flex-wrap: wrap) actually
       shrink below its intrinsic width. Without it, rows carrying the red
       UNIFORM pill spilled past the right edge of the card on phones (e.g.
       Order Items row 8002). The default flex min-width is `auto` which
       resolves to the content's intrinsic size and prevents wrapping. */
    min-width: 0;
    justify-content: center;
    gap: 3px;
    align-items: flex-start;
    /* Allow text to wrap onto multiple lines so long customer +
       design strings still fit on a phone screen. */
    white-space: normal !important;
    overflow: visible !important;
    line-height: 1.25;
  }
  .oi-mobile-title {
    font-weight: 700;
    font-size: 17px;
    color: var(--text, #1c1917);
    line-height: 1.15;
    letter-spacing: -0.01em;
  }
  .oi-mobile-sub {
    display: flex !important;
    flex-wrap: wrap;
    /* Stretch to title-cell width. Without this, the parent title cell's
       `align-items: flex-start` (styles.css:8186) makes column-flex children
       size to intrinsic content width — so .oi-mobile-sub keeps its full
       unwrapped width and never triggers flex-wrap. The UNIFORM pill then
       spilled past the viewport. align-self overrides parent align-items
       for THIS child only; keeps the title text left-aligned as intended. */
    align-self: stretch;
    width: 100%;
    min-width: 0;
    align-items: center;
    gap: 4px 8px;
    font-size: 12.5px;
    color: var(--text-secondary, #57534e);
    font-weight: 500;
    margin-top: 1px;
    line-height: 1.25;
  }
  /* Dedicated status sub-line — always rendered on its own row so a
   * UNIFORM/REPEAT chip on the main .oi-mobile-sub line cannot squeeze
   * the GH ready status off the wrap. */
  .oi-mobile-status {
    display: flex !important;
    margin-top: 3px;
    line-height: 1.25;
  }
  .oi-mobile-status .oi-sub-pill {
    display: inline-flex;
    align-items: center;
    padding: 2px 8px;
    border-radius: 999px;
    font-size: 10.5px;
    font-weight: 700;
    letter-spacing: .04em;
    border: 1px solid transparent;
  }
  .oi-mobile-sub .oi-sub-customer {
    color: var(--text, #1c1917);
    font-weight: 600;
  }
  .oi-mobile-sub .oi-sub-design {
    font-family: "JetBrains Mono", ui-monospace, monospace;
    font-size: 11.5px;
    color: #6b21a8;
    background: rgba(168,85,247,.10);
    border: 1px solid rgba(168,85,247,.25);
    border-radius: 6px;
    padding: 1px 6px;
  }
  .oi-mobile-sub .oi-sub-yards {
    font-variant-numeric: tabular-nums;
    color: var(--text-secondary, #57534e);
    font-weight: 600;
  }
  .oi-mobile-sub .oi-sub-dot {
    color: var(--line, #d6d3d1);
    font-weight: 700;
  }
  /* Pill base + UNIFORM/REPEAT modifiers — unscoped so the same pill looks
     identical whether it sits in .oi-mobile-sub (export-entries) or in its
     own .oi-mobile-status line (order_items v24.7+). Previously these were
     scoped to `.oi-mobile-sub .oi-sub-pill--uniform` and the order_items
     pill lost its red colour when promoted to its own line. */
  .oi-sub-pill {
    display: inline-flex;
    align-items: center;
    padding: 2px 8px;
    border-radius: 999px;
    font-size: 10.5px;
    font-weight: 700;
    letter-spacing: 0.02em;
    line-height: 1.4;
    white-space: nowrap;
  }
  .oi-sub-pill--uniform {
    background: #dc2626;
    color: #fff;
    box-shadow: 0 1px 2px rgba(220,38,38,.25);
  }
  .oi-sub-pill--repeat {
    background: rgba(99,102,241,.14);
    color: #4338ca;
    border: 1px solid rgba(99,102,241,.25);
  }
  /* Item-name secondary line — small muted text, can wrap on long names
     like "MEN POLISHED LACE". Renders below the metadata strip and above
     the .oi-mobile-status pill line. */
  .oi-mobile-item {
    display: block;
    margin-top: 2px;
    font-size: 11.5px;
    font-weight: 500;
    color: var(--text-secondary, #57534e);
    line-height: 1.25;
    text-transform: uppercase;
    letter-spacing: 0.03em;
    white-space: normal;
    overflow-wrap: anywhere;
    word-break: break-word;
  }

  /* ── E. Trailing slot — keep chevron from legacy ::after; just make
     room and align it vertically inside the flex row. */
  #module-workspace table tbody tr.row-has-checkbox,
  #module-workspace table tbody tr.row-has-image {
    padding-right: 30px;       /* room for the ::after chevron */
  }
  #module-workspace table tbody tr.row-has-checkbox::after,
  #module-workspace table tbody tr.row-has-image::after {
    top: 50%;
    margin-top: -5px;          /* center the 9px caret on the row */
    right: 14px;
  }
  #module-workspace table tbody tr.row-has-checkbox.card-open::after,
  #module-workspace table tbody tr.row-has-image.card-open::after {
    margin-top: -2px;          /* nudge after rotation */
  }

  /* ── F. Pressed/active state for tactile feedback (iOS HIG). */
  #module-workspace table tbody tr.row-has-checkbox:active,
  #module-workspace table tbody tr.row-has-image:active {
    background: var(--surface-sunken, #f5f5f4);
  }

  /* ── G. Card-open: expose remaining cells as a label/value grid below
     the flex row. Because the parent is display:flex, opened cells need
     flex-basis 100% to wrap to their own line. */
  /* G1. checkbox + image → grid starts at 4th td. */
  #module-workspace table tbody tr.row-has-checkbox.row-has-image.card-open > td:nth-child(n+4) {
    display: grid;
    flex: 1 1 100%;
    grid-template-columns: minmax(100px, 40%) 1fr;
    gap: 6px 12px;
    padding: 8px 0;
    margin-top: 6px;
    border-top: 1px dashed var(--line);
    font-size: var(--fs-caption);
  }
  /* G2. checkbox only → grid starts at 3rd td. */
  #module-workspace table tbody tr.row-has-checkbox:not(.row-has-image).card-open > td:nth-child(n+3) {
    display: grid;
    flex: 1 1 100%;
    grid-template-columns: minmax(100px, 40%) 1fr;
    gap: 6px 12px;
    padding: 8px 0;
    margin-top: 6px;
    border-top: 1px dashed var(--line);
    font-size: var(--fs-caption);
  }
  /* G3. image only → grid starts at 3rd td. */
  #module-workspace table tbody tr.row-has-image:not(.row-has-checkbox).card-open > td:nth-child(n+3) {
    display: grid;
    flex: 1 1 100%;
    grid-template-columns: minmax(100px, 40%) 1fr;
    gap: 6px 12px;
    padding: 8px 0;
    margin-top: 6px;
    border-top: 1px dashed var(--line);
    font-size: var(--fs-caption);
  }
  #module-workspace table tbody tr.row-has-checkbox.row-has-image.card-open > td:nth-child(n+4)::before,
  #module-workspace table tbody tr.row-has-checkbox:not(.row-has-image).card-open > td:nth-child(n+3)::before,
  #module-workspace table tbody tr.row-has-image:not(.row-has-checkbox).card-open > td:nth-child(n+3)::before {
    content: attr(data-label);
    font-size: var(--fs-label);
    font-weight: 600;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--muted, #78716c);
    align-self: center;
  }
  /* Cells without labels span full width. */
  #module-workspace table tbody tr.row-has-checkbox.card-open > td:not([data-label]),
  #module-workspace table tbody tr.row-has-image.card-open > td:not([data-label]),
  #module-workspace table tbody tr.row-has-checkbox.card-open > td[data-label=""],
  #module-workspace table tbody tr.row-has-image.card-open > td[data-label=""] {
    grid-template-columns: 1fr;
  }
}

/* ─── Mobile multi-select — explicit "Select" mode ──────────────────────
   Earlier (May-6) we hid every multi-select affordance on mobile PWA
   because batch ops on touch led to accidental bulk edits. Reinstating
   them with an explicit two-step entry: user must tap the header
   "Select" chip → checkboxes appear → row-tap toggles → "Done" exits.
   Same pattern as Apple Mail/Notes/Files + Material 3 selection mode.
   Gated on `body.has-bottom-nav` (covers PWA standalone + mobile browser
   tab — wherever the bottom-nav is rendered, this rule applies). */
@media (max-width: 768px) {
  /* Default mobile (no select mode): hide multi-select chrome to keep
     the "no accidental bulk edits" guarantee from the May-6 fix. */
  body.has-bottom-nav #module-workspace:not([data-select-mode="1"]) #lst-batch-bar,
  body.has-bottom-nav #module-workspace:not([data-select-mode="1"]) th.col-checkbox,
  body.has-bottom-nav #module-workspace:not([data-select-mode="1"]) td.col-checkbox,
  body.has-bottom-nav #module-workspace:not([data-select-mode="1"]) input.row-checkbox,
  body.has-bottom-nav #module-workspace:not([data-select-mode="1"]) input.row-checkbox-all {
    display: none !important;
  }

  /* In select mode: dim filter chips so they can't be tapped mid-select
     (would silently shrink the visible set and lose half the picks). */
  body.has-bottom-nav #module-workspace[data-select-mode="1"] #lst-chips {
    opacity: .55;
    pointer-events: none;
  }

  /* In select mode: floating batch bar sits ABOVE the bottom-nav (76px
     tall + safe-area). z-index 99 = just below nav's 100 so an edge-tap
     can never block the nav exit. Buttons wrap to 2 rows on narrow
     phones (count + select + Apply on row 1, the rest on row 2). */
  body.has-bottom-nav #module-workspace[data-select-mode="1"] #lst-batch-bar {
    position: fixed;
    left: 12px;
    right: 12px;
    bottom: calc(76px + env(safe-area-inset-bottom, 0px) + 6px);
    top: auto;
    z-index: 99;
    margin: 0;
    flex-wrap: wrap;
    gap: 6px;
    padding: 10px 12px;
    border-radius: 14px;
    box-shadow: 0 -2px 12px rgba(0,0,0,.10),
                0 8px 24px rgba(0,0,0,.18);
    background: var(--surface);
    border: 1px solid var(--line);
    max-width: none;
  }

  /* Reserve scroll space so the last list row clears the floating bar.
     v30.3 fix: only apply when the bar is actually visible (has at
     least one selected row). Earlier v30.2 gate on data-list-select-
     mode="1" alone applied 320px padding the instant the user tapped
     "Select" — before any row was picked — leaving a huge blank below
     the list with no bar in sight.
     body is the actual scroll container (overflow:visible everywhere
     inside), so the reserve is mirrored on body / .main / #module-
     workspace to be sure it lands wherever scroll-max maths happens. */
  body.has-bottom-nav[data-list-batch-bar="visible"],
  body.has-bottom-nav[data-list-batch-bar="visible"] .main,
  body.has-bottom-nav[data-list-batch-bar="visible"] #module-workspace {
    padding-bottom: var(--batch-bar-pad-bottom,
                        calc(320px + env(safe-area-inset-bottom, 0px))) !important;
  }
  /* scroll-padding-bottom is the CSS-spec answer for "leave room at the
     bottom of the scroller for elements scrolled into view". */
  body.has-bottom-nav[data-list-batch-bar="visible"] {
    scroll-padding-bottom: var(--batch-bar-pad-bottom,
                                calc(320px + env(safe-area-inset-bottom, 0px)));
  }

  /* Show the "Select" chip in the section header — only when there's a
     bottom-nav (i.e. mobile). Desktop hides it via the parent gate. */
  body.has-bottom-nav .lst-select-toggle { display: inline-flex !important; }

  /* Active-state styling for the chip when select mode is on. */
  body.has-bottom-nav .lst-select-toggle--active {
    background: var(--primary);
    color: #fff;
    border-color: var(--primary);
  }

  /* Compact bar at narrow widths — keep buttons reachable, never clip. */
  #lst-batch-bar > * { flex: 0 0 auto; min-width: 0; }
  #lst-batch-bar .batch-action-bar__count { font-size: 12px; padding: 5px 9px; }
  #lst-batch-bar button,
  #lst-batch-bar select { font-size: 13px; padding: 7px 10px; min-height: 40px; }
}

/* At iPhone-SE-class widths (≤380px), collapse the long Customer/
   Production labels to short ones so the bar stays ≤2 rows. */
.lbl-long { display: inline; }
.lbl-short { display: none; }
@media (max-width: 380px) {
  .lbl-long { display: none; }
  .lbl-short { display: inline; }
}

/* ─── iOS PWA fixed-position safety nets ────────────────────────────────
   Apple Developer Forums #744327: iOS 17+ PWA `position: fixed` elements
   lose their viewport anchor after repeated modal/page-replace cycles.
   Documented community workarounds:
     1. Force html min-height beyond the safe area so WebKit recomputes
        the viewport on the next layout pass.
     2. Ensure no ancestor of .bottom-nav has a transform / filter /
        perspective other than `none` — those create a new containing
        block, and the fixed child latches to the ancestor instead of
        the viewport. */
html {
  min-height: calc(100% + env(safe-area-inset-top, 0px));
}
html, body, .main, #module-workspace {
  transform: none !important;
  filter: none !important;
  perspective: none !important;
  /* Lock the remaining containing-block-creating props on the bottom-nav's
     only possible ancestors (html/body) so nothing can ever re-anchor the
     fixed nav. (The inline-only JS sweep can't catch stylesheet-applied ones.) */
  will-change: auto !important;
  contain: none !important;
  backdrop-filter: none !important;
  -webkit-backdrop-filter: none !important;
}

/* ─── Notifications drawer (v2) ──────────────────────────────────────────
   Right-side overlay panel. Slides in/out via translateX. Built and mounted
   dynamically by app.js (#notif-drawer is appended to <body>).
   */
[data-ui-version="v2"] #notif-scrim {
  position: fixed;
  inset: 0;
  background: rgba(0,0,0,0.18);
  z-index: 9000;
  opacity: 0;
  pointer-events: none;
  transition: opacity 200ms ease;
}
[data-ui-version="v2"] #notif-scrim.open {
  opacity: 1;
  pointer-events: auto;
}
[data-ui-version="v2"] #notif-drawer {
  position: fixed;
  top: 0;
  right: 0;
  bottom: 0;
  width: 380px;
  max-width: 100vw;
  background: var(--surface);
  border-left: 1px solid var(--line);
  box-shadow: -4px 0 24px rgba(0,0,0,0.08);
  z-index: 9001;
  display: flex;
  flex-direction: column;
  transform: translateX(100%);
  transition: transform 200ms ease;
  overscroll-behavior: contain;
}
[data-ui-version="v2"] #notif-drawer.open {
  transform: translateX(0);
}
[data-ui-version="v2"] #notif-drawer .notif-head {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 14px 16px;
  border-bottom: 1px solid var(--line);
  background: var(--surface);
  flex-shrink: 0;
}
[data-ui-version="v2"] #notif-drawer .notif-head h3 {
  margin: 0;
  font-size: 1rem;
  font-weight: 600;
  flex: 1;
}
[data-ui-version="v2"] #notif-drawer .notif-head .btn {
  font-size: .8125rem;
  padding: 5px 10px;
}
[data-ui-version="v2"] #notif-drawer .notif-body {
  flex: 1;
  overflow-y: auto;
  padding: 0;
}
[data-ui-version="v2"] #notif-drawer .notif-empty {
  padding: 48px 16px;
  text-align: center;
  color: var(--muted);
  font-size: .875rem;
}
[data-ui-version="v2"] #notif-drawer .notif-item {
  display: block;
  padding: 12px 16px;
  border-bottom: 1px solid var(--line);
  cursor: pointer;
  position: relative;
  border-left: 3px solid transparent;
  transition: background 120ms ease;
}
[data-ui-version="v2"] #notif-drawer .notif-item:hover {
  background: var(--surface-2, var(--surface-sunken));
}
[data-ui-version="v2"] #notif-drawer .notif-item.unread {
  border-left-color: var(--accent, #2563eb);
  background: color-mix(in srgb, var(--accent, #2563eb) 4%, transparent);
}
[data-ui-version="v2"] #notif-drawer .notif-item-row {
  display: flex;
  align-items: flex-start;
  gap: 8px;
}
[data-ui-version="v2"] #notif-drawer .notif-title {
  font-weight: 600;
  font-size: .875rem;
  line-height: 1.35;
  flex: 1;
  color: var(--text);
}
[data-ui-version="v2"] #notif-drawer .notif-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--accent, #2563eb);
  flex-shrink: 0;
  margin-top: 5px;
}
[data-ui-version="v2"] #notif-drawer .notif-msg {
  font-size: .8125rem;
  color: var(--text-secondary, var(--muted));
  margin-top: 4px;
  line-height: 1.4;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
[data-ui-version="v2"] #notif-drawer .notif-meta {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-top: 6px;
  font-size: .7125rem;
  color: var(--muted);
}
[data-ui-version="v2"] #notif-drawer .notif-chip {
  display: inline-block;
  padding: 1px 7px;
  border-radius: 99px;
  background: var(--surface-sunken);
  border: 1px solid var(--line);
  color: var(--text-secondary, var(--muted));
  font-size: .6875rem;
  font-weight: 500;
  letter-spacing: .01em;
}
[data-ui-version="v2"] #notif-drawer .notif-prefs {
  padding: 8px 0;
}
[data-ui-version="v2"] #notif-drawer .notif-prefs h4 {
  margin: 0;
  padding: 12px 16px 6px;
  font-size: .75rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: .04em;
  color: var(--muted);
}
[data-ui-version="v2"] #notif-drawer .notif-pref-row {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: 8px;
  align-items: center;
  padding: 10px 16px;
  border-bottom: 1px solid var(--line);
  font-size: .875rem;
}
[data-ui-version="v2"] #notif-drawer .notif-pref-row.muted {
  opacity: 0.55;
}
[data-ui-version="v2"] #notif-drawer .notif-pref-row .pref-name {
  font-weight: 500;
  color: var(--text);
  text-transform: capitalize;
}
[data-ui-version="v2"] #notif-drawer .notif-pref-row .pref-toggles {
  display: flex;
  gap: 12px;
  align-items: center;
  font-size: .75rem;
  color: var(--muted);
}
[data-ui-version="v2"] #notif-drawer .notif-pref-row label {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  cursor: pointer;
  user-select: none;
}
@media (max-width: 768px) {
  [data-ui-version="v2"] #notif-drawer {
    width: 100vw;
    border-left: 0;
  }
}

/* ──────────────────────────────────────────────────────────────────────
 * Mobile + PWA polish pass — 2026-05-06
 * Source: 2026 PWA UI research (web.dev, Apple HIG, Material 3,
 * Linear / Shopify / Stripe conventions). Everything here is purely
 * additive — no existing class names overridden, only :root tokens
 * and a few new utility classes. Goals:
 *   1. Honour iOS safe-area on home-bar / notch
 *   2. Min 44×44 tap target on every actionable button
 *   3. Wrap every list table in a horizontal-scroll envelope so
 *      no overflow column eats the delete affordance on phones
 *   4. Card-style packing rows that stack inputs on narrow widths
 *   5. Smoother type / spacing scale (8-grid)
 * ────────────────────────────────────────────────────────────────────── */

:root {
  /* 8-grid spacing tokens */
  --sp-1: 4px; --sp-2: 8px; --sp-3: 12px; --sp-4: 16px;
  --sp-5: 24px; --sp-6: 32px;
  /* radius */
  --rx-sm: 4px; --rx-md: 6px; --rx-lg: 10px;
  /* shadows (soft, layered — Linear / Vercel taste) */
  --shadow-sm: 0 1px 2px rgba(0,0,0,.05);
  --shadow-md: 0 1px 3px rgba(0,0,0,.08), 0 3px 8px rgba(0,0,0,.10);
  --shadow-lg: 0 8px 24px rgba(0,0,0,.12);
}

/* Safe-area for iOS notch + home-bar. Belt-and-suspenders: also
 * keep min 0 padding so non-iOS browsers don't get visual gap. */
@supports (padding: env(safe-area-inset-top)) {
  body {
    padding-top:    env(safe-area-inset-top, 0);
    padding-bottom: env(safe-area-inset-bottom, 0);
  }
}

/* 44×44 minimum tap target for every <button> and link-styled
 * element that's expected to be tapped. Doesn't grow visually
 * (because min- vs explicit) — ensures the *invisible* hit area
 * meets HIG even when the visible icon is small. */
@media (hover: none) and (pointer: coarse) {
  button:not(.row-checkbox-all):not(.chip):not(.row-checkbox):not(.icon-btn-mini),
  [role="button"]:not(.chip),
  .btn,
  .btn-row-action {
    min-height: 44px;
  }
  /* Inputs in modals are fine at default height; bump only the
   * text-style inputs so they're easier to tap into. */
  input[type="text"],
  input[type="number"],
  input[type="search"],
  input[type="date"],
  input[type="tel"],
  input[type="email"],
  select,
  textarea {
    min-height: 40px;
    font-size: 16px;  /* iOS won't auto-zoom inputs ≥16px */
  }
}

/* Every list table → horizontal-scroll envelope on small viewports
 * so off-screen columns (delete, action, last-status) stay reachable
 * without zooming. The list-module already wraps its table in
 * #lst-table-wrap; this rule kicks in below 768px. */
@media (max-width: 768px) {
  #lst-table-wrap,
  .table-scroll {
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
  }
  #lst-table-wrap table { min-width: max-content; }
  /* Section cards on phones — full-bleed, less rounded so they
   * don't waste the limited horizontal real estate. */
  .section-card {
    border-radius: 0;
    border-left-width: 0;
    border-right-width: 0;
    box-shadow: none;
  }
  .section-card__head {
    padding: var(--sp-3) var(--sp-4) !important;
    flex-direction: column;
    align-items: stretch !important;
  }
}

/* ── Packing report — section + card-style rows ─────────────────────
 * Each section ("Adhure Pack — Incomplete" / "Pakka Pack — Final")
 * is now wrapped in its own card so the header, +Add button, and
 * row cards stay visually grouped. Earlier, the section header sat
 * outside any container with the +Add button floating off its
 * right edge — read on mobile as "the box is outside its card".
 */
.pk-section {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--rx-lg);
  box-shadow: var(--shadow-sm);
  padding: var(--sp-4);
}
.pk-section + .pk-section { margin-top: var(--sp-4); }
.pk-section__head {
  margin-bottom: var(--sp-3) !important;
}
@media (max-width: 640px) {
  .pk-section { padding: var(--sp-3); border-radius: var(--rx-md); }
  .pk-section__head {
    /* Stack title row + Add button on phones so the dashed button
     * isn't squeezed to the edge. */
    flex-direction: column !important;
    align-items: stretch !important;
    gap: var(--sp-2) !important;
  }
  .pk-section__head .pk-add-line { margin-left: 0 !important; width: 100%; }
}

.pk-rows .pk-row {
  display: grid;
  grid-template-columns: minmax(0, 1.5fr) 110px minmax(0, 2fr) 44px;
  gap: var(--sp-3);
  align-items: center;
  padding: var(--sp-3);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--rx-md);
  box-shadow: var(--shadow-sm);
}
.pk-rows .pk-row__name {
  font-size: .92rem;
  color: var(--text);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-weight: 500;
}
.pk-rows .pk-row__lbl {
  display: none;  /* shown only on the mobile stacked layout below */
  font-size: .68rem;
  text-transform: uppercase;
  letter-spacing: .04em;
  color: var(--muted);
  font-weight: 700;
  margin-bottom: 2px;
}
.pk-rows .pk-row input[type="number"],
.pk-rows .pk-row input[type="text"] {
  width: 100%;
  padding: 6px 10px;
  border: 1px solid var(--line);
  border-radius: var(--rx-sm);
  background: var(--surface);
  font: inherit;
  font-size: .9rem;
}
.pk-rows .pk-row input[type="number"] {
  text-align: right;
  font-variant-numeric: tabular-nums;
}
.pk-rows .pk-row .pk-line-del {
  width: 44px; height: 44px;
  display: inline-flex; align-items: center; justify-content: center;
  background: rgba(220,38,38,.08);
  border: 1px solid rgba(220,38,38,.24);
  color: var(--danger, #c03);
  font-size: 1.4rem; font-weight: 700;
  line-height: 1;
  border-radius: var(--rx-md);
  cursor: pointer;
  padding: 0;
}
.pk-rows .pk-row .pk-line-del:hover { background: rgba(220,38,38,.15); }
.pk-rows .pk-row .pk-line-del:active { transform: scale(.94); }

/* Mobile stack — name + delete on row 1, pkg+remarks on row 2,
 * with field labels visible so the user knows which input is which. */
@media (max-width: 640px) {
  .pk-rows .pk-row {
    grid-template-columns: minmax(0, 1fr) 44px;
    grid-template-areas:
      "name del"
      "pkg pkg"
      "remarks remarks";
    gap: var(--sp-2);
    padding: var(--sp-3);
  }
  .pk-rows .pk-row__name    { grid-area: name; font-size: 1rem; white-space: normal; }
  .pk-rows .pk-row .pk-line-del { grid-area: del; }
  .pk-rows .pk-row__pkg     { grid-area: pkg; }
  .pk-rows .pk-row__remarks { grid-area: remarks; }
  .pk-rows .pk-row__lbl     { display: block; }
}

/* ── Categories admin — looser layout on mobile so the matrix fits */
@media (max-width: 768px) {
  .categories-matrix,
  .cat-perm-matrix {
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    border-radius: var(--rx-md);
  }
}

/* General modal polish — keep the dialog snug on phones so the
 * keyboard doesn't push half the form off-screen. */
@media (max-width: 640px) {
  .modal-dialog {
    width: calc(100vw - var(--sp-4)) !important;
    max-width: calc(100vw - var(--sp-4)) !important;
    max-height: calc(100vh - var(--sp-5)) !important;
    margin: 0 var(--sp-2);
    overflow-y: auto;
  }
}

/* Skeleton pulse — used for the "Loading…" placeholders to feel
 * snappier than a static muted "Loading…" string. */
@keyframes ord-skel-pulse {
  0%, 100% { opacity: .55; }
  50%      { opacity: 1; }
}
.ord-skel { animation: ord-skel-pulse 1.4s ease-in-out infinite;
            background: var(--surface-sunken, #f1f0ec);
            border-radius: var(--rx-sm);
            display: inline-block; }

/* ── Subtle motion polish (prefers-reduced-motion respected) ──────
 * The user asked for "smooth animations" specifically as a polish
 * hint, not flashy. Goals:
 *   • 150 ms ease-out on hover / focus / active states
 *   • Card press feedback so the user knows the tap registered
 *   • Honour OS-level reduced-motion preference (Apple HIG / WCAG)
 *
 * Anti-accidental-edit posture: every destructive action goes
 * through `_confirmDelete` (typed-DELETE modal) — so smooth
 * transitions on the delete button are safe; the actual delete
 * still requires the typed confirm.
 */
@media (prefers-reduced-motion: no-preference) {
  .pk-rows .pk-row,
  .section-card,
  .btn,
  .btn-row-action,
  .chip,
  .row-action,
  .pk-line-del {
    transition: background-color .15s ease-out,
                border-color .15s ease-out,
                box-shadow .15s ease-out,
                transform .12s ease-out;
  }
  /* Card lifts on hover (desktop), depresses on tap (mobile). */
  @media (hover: hover) and (pointer: fine) {
    .pk-rows .pk-row:hover { box-shadow: var(--shadow-md); }
  }
  .pk-rows .pk-row:focus-within {
    border-color: var(--primary, #6366f1);
    box-shadow: 0 0 0 3px rgba(99,102,241,.12);
  }
  /* Subtle press feedback — same idea as iOS button highlight. */
  .btn:active,
  .btn-row-action:active,
  .pk-line-del:active,
  .row-action:active { transform: scale(.97); }
  /* Modal fade-in (only when prefs allow). */
  .modal-backdrop { animation: ord-fade-in .14s ease-out; }
  .modal-dialog   { animation: ord-pop-in  .18s ease-out; }
}
@keyframes ord-fade-in { from { opacity: 0; } to { opacity: 1; } }
@keyframes ord-pop-in  { from { opacity: 0; transform: translateY(6px) scale(.985); } to { opacity: 1; transform: none; } }

/* Reduced-motion fallback — instant transitions / no animations.
 * Required by WCAG 2.3.3 and respected automatically by iOS
 * "Reduce Motion" accessibility setting. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}

/* ──────────────────────────────────────────────────────────────────────
 * Global PWA refresh sweep — 2026-05-06
 * Goal: every module's list view feels like the same app. Targets the
 * shared listModule shell (#module-workspace > .section-card) and the
 * standard chip-row / pager so modules don't drift visually.
 * Additive only; no class names redefined.
 * ────────────────────────────────────────────────────────────────────── */

#module-workspace .section-card {
  border-radius: var(--rx-lg);
  box-shadow: var(--shadow-sm);
}
#module-workspace .section-card__head {
  padding: var(--sp-4) var(--sp-5);
  border-bottom: 1px solid var(--line);
  background: linear-gradient(180deg,
    color-mix(in srgb, var(--surface) 100%, transparent),
    color-mix(in srgb, var(--surface-sunken,#f3f0eb) 30%, var(--surface) 70%));
}
#module-workspace .section-card__head h3 {
  font-weight: 650;
  letter-spacing: -.005em;
  margin: 0;
}
#module-workspace .section-card__head + * {
  /* breathing room between the head and the chip row */
  padding-top: var(--sp-2);
}

/* Chip row — refined spacing + subtle press feedback. */
#lst-chips,
#lst-subchips {
  padding: var(--sp-2) var(--sp-4) var(--sp-3) !important;
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-2);
}
@media (max-width: 768px) {
  #lst-chips,
  #lst-subchips {
    padding: var(--sp-2) var(--sp-3) var(--sp-3) !important;
    /* Horizontal scroll on phones — never wrap chips onto a second
     * row. Two-row walls are unreadable + un-tappable on a 375px
     * viewport. (Was 640px breakpoint; bumped to 768 to match the
     * rest of the mobile layout.) */
    flex-wrap: nowrap !important;
    overflow-x: auto !important;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
    touch-action: pan-x pan-y;
  }
  #lst-chips::-webkit-scrollbar,
  #lst-subchips::-webkit-scrollbar { display: none; }
  #lst-chips .chip,
  #lst-subchips .chip { flex-shrink: 0; }
}

/* List row hover/press — only on desktop. Phones already
 * have :active shrink for buttons. */
@media (hover: hover) and (pointer: fine) {
  #lst-table-wrap tbody tr:hover {
    background: color-mix(in srgb, var(--primary, #6366f1) 4%, transparent);
  }
}

/* Pager — better breathing room. */
#lst-pager {
  padding: var(--sp-3) var(--sp-4);
  border-top: 1px solid var(--line);
  font-size: .85rem;
  color: var(--muted);
  display: flex; align-items: center; gap: var(--sp-3);
  flex-wrap: wrap;
}

/* List form panel (the +New Order Item slide-down) — softer card
 * treatment. */
#lst-form-panel {
  background: color-mix(in srgb, var(--surface-sunken,#f3f0eb) 60%, var(--surface) 40%);
  border-radius: var(--rx-md);
  margin: var(--sp-3) var(--sp-4);
  padding: var(--sp-4) !important;
  border: 1px solid var(--line);
}

/* Bell drawer + push pill — line them up with the rest of the
 * header gap rhythm. */
.app-header,
header.app-header {
  padding-left: env(safe-area-inset-left);
  padding-right: env(safe-area-inset-right);
}

/* Inputs across the app — unified focus ring (iOS Safari likes
 * bigger ring than the default). */
input:focus-visible,
textarea:focus-visible,
select:focus-visible {
  outline: 2px solid color-mix(in srgb, var(--primary, #6366f1) 70%, transparent);
  outline-offset: 1px;
  border-color: var(--primary, #6366f1);
}

/* Cards across the app — subtle hover lift on desktop. */
@media (hover: hover) and (pointer: fine) {
  .section-card,
  .pk-section,
  .a4-toolbar + .a4 {
    transition: box-shadow .18s ease-out;
  }
}

/* Mobile list table — every cell breathes a little more so taps
 * land where the eye expects them. */
@media (max-width: 768px) {
  #lst-table-wrap td,
  #lst-table-wrap th {
    padding: 10px 12px !important;
  }
  /* Truncate-with-ellipsis on cells so the cell width stays
   * predictable instead of wrapping into multi-line walls. */
  #lst-table-wrap td {
    max-width: 220px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
  /* …except the title-style cells, which can wrap because they
   * carry the mobile-card subtitle. */
  #lst-table-wrap td.cell-title,
  #lst-table-wrap td:has(.oi-mobile-sub) {
    white-space: normal;
    max-width: none;
  }
}

/* Status pills — slightly more breathing room + uppercase letter-
 * spacing for legibility. */
.pill,
.chip--variant-status,
.status-pill {
  letter-spacing: .03em;
}

/* ──────────────────────────────────────────────────────────────────────
 * Mobile PWA modernization pass — focused (2026-05-06)
 *
 * Scope per user direction: ONLY items C (motion polish), layout
 * improvements (sticky headers + first column), F (anti-accidental
 * edit kept intact via existing typed-DELETE confirms — nothing new
 * here interferes), plus left-edge swipe-to-open menu, with full
 * dark-mode parity. Wraps everything in `(max-width: 768px)` so the
 * desktop UI is untouched.
 *
 * Anti-pattern guards (research-confirmed):
 *   • All transitions ≤200ms (so they never mask user intent)
 *   • Modal exit (100ms) faster than enter (200ms) — Apple/Linear taste
 *   • prefers-reduced-motion: reduce → all instant
 * ────────────────────────────────────────────────────────────────────── */

/* Award-winning motion curves (Linear / Vercel taste). */
:root {
  --ease-snappy:   cubic-bezier(0.34, 1.56, 0.64, 1);   /* 150ms button taps */
  --ease-out-soft: cubic-bezier(0.16, 1, 0.3, 1);       /* modal/drawer enter */
  --ease-in-soft:  cubic-bezier(0.4, 0, 0.6, 0.2);      /* modal/drawer exit */
}

/* ── C. Mobile-only motion polish ──────────────────────────────────── */
@media (max-width: 768px) {
  /* Bottom-sheet modals. Override the centered .modal-dialog only on
   * phones — desktop modals stay centered (per scope). Pull-handle is
   * a purely visual cue that this is a sheet, NOT a tap-to-close
   * affordance (which would risk accidental dismiss with unsaved
   * edits — we keep the existing Cancel/Close buttons as the only
   * way to dismiss). */
  .modal-dialog {
    position: fixed !important;
    bottom: 0 !important;
    left: 0 !important;
    right: 0 !important;
    top: auto !important;
    transform: none !important;
    width: 100vw !important;
    max-width: 100vw !important;
    max-height: 88vh !important;
    border-radius: 16px 16px 0 0 !important;
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
    animation: ord-sheet-up 200ms var(--ease-out-soft);
  }
  .modal-dialog::before {
    content: "";
    display: block;
    width: 36px; height: 4px;
    background: var(--muted, #a3a3a3);
    border-radius: 2px;
    margin: 8px auto 0;
    opacity: .4;
    pointer-events: none;
  }
  /* Keyframes — slide up from bottom, no fade. Per agent: avoid
   * fade-in alone, ALWAYS pair with a transform vector. */
  @keyframes ord-sheet-up {
    from { transform: translateY(100%); }
    to   { transform: translateY(0); }
  }
  /* Backdrop matches the same 200ms — but a quicker fade. */
  .modal-backdrop {
    animation: ord-backdrop-in 160ms var(--ease-out-soft);
  }
  @keyframes ord-backdrop-in {
    from { opacity: 0; }
    to   { opacity: 1; }
  }

  /* List-row stagger on first paint. Capped at row 30 so a 5000-row
   * page doesn't take 50 s to fully appear. The animation is purely
   * visual sugar — rows are real DOM at frame 0. */
  #lst-table-wrap tbody tr {
    animation: ord-row-fade-in 220ms var(--ease-out-soft) backwards;
  }
  /* Stagger 12ms × index for first 30 rows. */
  #lst-table-wrap tbody tr:nth-child(1)  { animation-delay:   0ms; }
  #lst-table-wrap tbody tr:nth-child(2)  { animation-delay:  12ms; }
  #lst-table-wrap tbody tr:nth-child(3)  { animation-delay:  24ms; }
  #lst-table-wrap tbody tr:nth-child(4)  { animation-delay:  36ms; }
  #lst-table-wrap tbody tr:nth-child(5)  { animation-delay:  48ms; }
  #lst-table-wrap tbody tr:nth-child(6)  { animation-delay:  60ms; }
  #lst-table-wrap tbody tr:nth-child(7)  { animation-delay:  72ms; }
  #lst-table-wrap tbody tr:nth-child(8)  { animation-delay:  84ms; }
  #lst-table-wrap tbody tr:nth-child(9)  { animation-delay:  96ms; }
  #lst-table-wrap tbody tr:nth-child(10) { animation-delay: 108ms; }
  #lst-table-wrap tbody tr:nth-child(11) { animation-delay: 120ms; }
  #lst-table-wrap tbody tr:nth-child(12) { animation-delay: 132ms; }
  #lst-table-wrap tbody tr:nth-child(13) { animation-delay: 144ms; }
  #lst-table-wrap tbody tr:nth-child(14) { animation-delay: 156ms; }
  #lst-table-wrap tbody tr:nth-child(15) { animation-delay: 168ms; }
  #lst-table-wrap tbody tr:nth-child(16) { animation-delay: 180ms; }
  #lst-table-wrap tbody tr:nth-child(17) { animation-delay: 192ms; }
  #lst-table-wrap tbody tr:nth-child(18) { animation-delay: 204ms; }
  #lst-table-wrap tbody tr:nth-child(19) { animation-delay: 216ms; }
  #lst-table-wrap tbody tr:nth-child(20) { animation-delay: 228ms; }
  #lst-table-wrap tbody tr:nth-child(n+21) { animation-delay: 240ms; }
  /* Beyond row 30, skip the animation entirely to keep big lists snappy. */
  #lst-table-wrap tbody tr:nth-child(n+30) {
    animation: none !important;
  }
  @keyframes ord-row-fade-in {
    from { opacity: 0; }
    to   { opacity: 1; }
  }

  /* Sidebar drawer slide — refine the existing .sidebar-open
   * transition with the new ease curve so swipe gestures and click-
   * to-open both feel the same. */
  .sidebar {
    transition: transform 240ms var(--ease-out-soft);
  }
  /* While the user's finger is dragging, JS sets data-dragging="1" to
   * disable the transition so the drawer follows the finger 1:1. */
  .sidebar[data-dragging="1"] { transition: none !important; }
  #sidebar-overlay {
    transition: opacity 240ms var(--ease-out-soft);
    opacity: 0;
  }
  #sidebar-overlay.active { opacity: 1; }
}

/* ── D. Mobile-only layout improvements ────────────────────────────── */
@media (max-width: 768px) {
  /* Sticky table header — works inside the existing #lst-table-wrap's
   * overflow-x:auto envelope. Header stays pinned vertically; first
   * column stays pinned horizontally. */
  #lst-table-wrap thead th {
    position: sticky;
    top: 0;
    z-index: 5;
    background: var(--surface-sunken, #f3f0eb);
  }
  #lst-table-wrap td:first-child,
  #lst-table-wrap th:first-child {
    position: sticky;
    left: 0;
    z-index: 4;
    background: var(--surface, #fff);
  }
  /* Header AND first column intersect — bump z to keep header on top. */
  #lst-table-wrap thead th:first-child { z-index: 6; }
}

/* ── Dark-mode parity (manual toggle: html[data-theme="dark"]) ─────── */
html[data-theme="dark"] {
  /* Make sure the new sticky background colors match dark surfaces. */
}
@media (max-width: 768px) {
  html[data-theme="dark"] #lst-table-wrap thead th {
    background: var(--surface-sunken, #1f1d1a);
  }
  html[data-theme="dark"] #lst-table-wrap td:first-child,
  html[data-theme="dark"] #lst-table-wrap th:first-child {
    background: var(--surface, #16140f);
  }
  html[data-theme="dark"] .modal-dialog::before {
    opacity: .55;
  }
}

/* ── F. Anti-accidental-edit posture (carry-forward verification) ─── */
/* No new CSS rules here — the existing typed-DELETE confirm modal
 * (window._confirmDelete) is the gate. The new bottom-sheet modal
 * intentionally has NO tap-to-dismiss-on-backdrop affordance for any
 * dialog that holds unsaved edits — closing requires the explicit
 * Cancel / Close button. The pull-handle at the top of bottom sheets
 * is purely visual; no swipe-down-to-close is wired. This deliberate
 * friction is the anti-accidental-dismiss safety net. */

/* ────────────────────────────────────────────────────────────────────────
 * Mobile overflow & scroll fixes — 2026-05-06
 *
 * Two bugs flagged from the field:
 *   1. List tables: only 3 of ~88 rows visible per page on phone, can't
 *      scroll past them. Root cause: #lst-table-wrap has overflow-y:hidden
 *      (added for desktop sticky-col bg layering) — on phones the rows
 *      render as block cards, the parent grows naturally, and there's no
 *      horizontal table to scroll, so we MUST allow vertical visibility.
 *   2. Forms (e.g. New Fabric Issue): inline `grid-template-columns:1fr 1fr`
 *      and inline `width:NNNpx` on inputs ignore mobile breakpoints, so
 *      the second column / Cancel button slides off the viewport edge.
 *
 * Fix without touching JS: high-specificity attribute selectors that
 * neutralise the offending inline styles strictly inside @media phone.
 * ──────────────────────────────────────────────────────────────────── */
@media (max-width: 768px) {
  /* ── Bug 1: Table wrapper must not clip vertically on phones ── */
  #lst-table-wrap {
    overflow-x: visible !important;
    overflow-y: visible !important;
    /* Phones use the card layout (display:block rows), no horizontal
       table scroll exists, so let the parent page own the scroll. */
    touch-action: auto !important;
  }

  /* ── Bug 2a: any inline 2-col form grid collapses to single column ── */
  [style*="grid-template-columns:1fr 1fr"],
  [style*="grid-template-columns: 1fr 1fr"],
  [style*="grid-template-columns:1fr 1fr 1fr"],
  [style*="grid-template-columns: 1fr 1fr 1fr"],
  [style*="grid-template-columns:repeat(2"],
  [style*="grid-template-columns: repeat(2"] {
    grid-template-columns: 1fr !important;
  }
  /* Form-specific minmax 2-col grids (scoped to <form> only — the
   * detail modal and other read-only screens use minmax grids
   * intentionally and must keep their multi-column layout where
   * the viewport allows). */
  form [style*="grid-template-columns:minmax"],
  form [style*="grid-template-columns: minmax"] {
    grid-template-columns: 1fr !important;
  }

  /* ── Orders mobile cards: the title-cell formatHtml already shows the
   * order date inside its coloured metadata strip (see app.js:5295+).
   * The standalone `date` column at column-position 2 was rendering AGAIN
   * as a muted subtitle below the item-name (per the default position-2
   * rule at styles.css:1822). Hide it explicitly on mobile to remove the
   * duplicate. data-col-key is stamped on every <td> by the cell renderer
   * (app.js:7388-7392). Desktop is unaffected. ── */
  #module-workspace table tbody td[data-col-key="date"] {
    display: none !important;
  }

  /* ── Order Detail page Line Items table: collapse the inline 8-column
   * fixed grid (~600 px wide) to 5 columns that fit a 390 px phone:
   * image | sr | design # chip | status pill | yards. Edit/Delete/Ready
   * columns hidden on mobile — users tap the row to drill into brief
   * entries which surfaces those actions. Without this, the design #
   * purple chip and status pill collided into the same cell on iPhone
   * (visible "V" / "BALANCE" mash-up in the user's screenshot). ── */
  .od-line-row,
  .od-line-head {
    grid-template-columns: 48px 32px minmax(0,1fr) auto auto !important;
    gap: 8px !important;
    padding: 10px 12px !important;
  }
  .od-line-row > div:nth-child(6),       /* ready pkg */
  .od-line-row > button.od-line-edit,
  .od-line-row > button.od-line-del,
  .od-line-head > div:nth-child(6),
  .od-line-head > div:nth-child(7),
  .od-line-head > div:nth-child(8) {
    display: none !important;
  }

  /* ── Bug 2c: collapse EVERY direct-child grid row inside any <form> —
   *           covers ratios the existing rules above don't catch
   *           (`280px 1fr`, `2fr 1fr`, `auto 1fr`, etc.). The New Export
   *           Entry form had 5 such rows overflowing past the viewport
   *           on iPhone (Sr No / Re[set] / Date columns clipped). The
   *           selector is broad on purpose: any inline-grid form row
   *           collapses to a single column on phones. ── */
  form > [style*="grid-template-columns"],
  form [style*="display:grid"][style*="grid-template-columns"],
  form [style*="display: grid"][style*="grid-template-columns"] {
    grid-template-columns: 1fr !important;
  }

  /* The form must never exceed the modal/dialog width on mobile. The
   * inline `max-width: 720px` on buildNewExportEntryForm's wrap div
   * (app.js:11410) was winning over the parent .modal-dialog's 460 px,
   * clipping everything past the viewport edge with no scrollbar. */
  .modal-dialog form,
  .modal-dialog .lst-form,
  #module-workspace form,
  #module-workspace .lst-form {
    max-width: 100% !important;
    width: 100%;
  }

  /* Long info hints (e.g. "Pick an order to see customer · mark · item …")
   * wrap inside their flex container instead of pushing past the edge.
   * word-break covers iOS ≤15.3 (no `overflow-wrap: anywhere` support);
   * overflow-wrap kicks in on iOS 15.4+ / Chrome 80+ for finer control. */
  #ee-order-chips,
  #ee-order-chips > span,
  .lst-form .form-hint {
    flex-wrap: wrap;
    white-space: normal;
    word-break: break-word;
    overflow-wrap: anywhere;
  }

  /* ── Bug 2b: inputs / search boxes with hardcoded inline pixel widths
   *          must shrink to the available column width on phones. ── */
  input[style*="width:280px"],
  input[style*="width: 280px"],
  input[style*="width:320px"],
  input[style*="width: 320px"],
  input[style*="width:240px"],
  input[style*="width: 240px"],
  input[style*="width:200px"],
  input[style*="width: 200px"],
  input[style*="width:360px"],
  input[style*="width: 360px"],
  input[type="search"],
  input[type="text"][id^="lst-search"] {
    width: 100% !important;
    max-width: 100% !important;
    min-width: 0 !important;
  }

  /* ── Bug 2c: the typical search-row (input + "+ New X" button packed
   *          into a flex row) must wrap on phones so the button drops
   *          to the next line instead of clipping off-screen.
   *
   *          IMPORTANT: do NOT use the broad attribute selector
   *          `div[style*="display:flex"][style*="gap"]` — it matches
   *          dozens of unrelated layouts (board cards, kb columns,
   *          chip rows) and causes layout thrash + flicker on every
   *          click in board view. Target only the actual search rows
   *          via :has() (Safari 15.4+, iOS 15.4+). ── */
  .main-header .toolbar {
    flex-wrap: wrap !important;
    max-width: 100%;
    min-width: 0;
  }
  /* Wrap any flex row that hosts a list/section search input. */
  div:has(> #lst-search),
  div:has(> input[type="search"]) {
    flex-wrap: wrap !important;
    max-width: 100%;
    min-width: 0;
  }
  /* The "+ New …" button now wraps to its own row — full-width is the
   * cleanest reflow (the sticky toolbar in lists already keeps it
   * reachable). */
  .panel-header .btn,
  .panel-header button.btn,
  .section-card-header .btn,
  .section-card-header button.btn {
    flex-shrink: 0;
  }

  /* ── Bug 2d: belt-and-suspenders — clip ANY accidental horizontal
   *          overflow at the document root so a single rogue inline
   *          width can't push the whole page off-screen. ── */
  html, body {
    overflow-x: clip;
    max-width: 100vw;
  }

  /* ── Bug 2e: form labels / inputs must always be min-width:0 inside
   *          a grid column so long placeholders ("Search shade no, fabric,
   *          issued-to…") don't push their column wider than 100%. ── */
  form label,
  form input,
  form select,
  form textarea,
  .lst-form input,
  .lst-form select,
  .lst-form textarea,
  #lst-form-panel input,
  #lst-form-panel select,
  #lst-form-panel textarea {
    min-width: 0;
    max-width: 100%;
    box-sizing: border-box;
  }
  form input[type="text"],
  form input[type="search"],
  form input[type="number"],
  form input[type="date"],
  form input[type="email"],
  form input[type="tel"],
  form select,
  form textarea {
    width: 100%;
  }
}

/* ────────────────────────────────────────────────────────────────────────
 * Mobile header compaction — 2026-05-06
 *
 * Field report: the header was eating ~50% of the viewport on iPhone —
 * page title + long subtitle + a wrapped 5-button toolbar (theme,
 * "Add to Home Screen", bell, density, "Sign out"). On a 375px screen,
 * "Sign out" wrapped to its own line, pushing real content below the
 * fold.
 *
 * Strategy: keep only what's essential on mobile.
 *   • Hide subtitle (#module-description) — the title is plenty.
 *   • Smaller h2, tighter header padding.
 *   • Hide #density-toggle (table-density toggle is a desktop feature
 *     — the mobile card layout has fixed density already).
 *   • Hide #logout-btn — Sign out lives in the sidebar (More tab).
 *   • Push-subscribe button: icon-only (the verbose "Add to Home Screen"
 *     copy stays in the title attribute / the tap-action toast).
 *   • Toolbar: nowrap + horizontal-scroll fallback — never wrap
 *     onto a second line.
 * ──────────────────────────────────────────────────────────────────── */
@media (max-width: 768px) {
  #module-description { display: none !important; }
  #module-title {
    font-size: 22px !important;
    line-height: 1.15 !important;
  }
  .main-header {
    padding: 10px 14px !important;
    gap: 8px !important;
  }
  /* The toolbar must not wrap — its buttons stay on a single line with
   * horizontal-scroll fallback if the user adds more buttons later. */
  .main-header .toolbar {
    gap: 6px !important;
    flex-wrap: nowrap !important;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
    margin-left: auto;
  }
  .main-header .toolbar::-webkit-scrollbar { display: none; }
  /* Hide low-priority toolbar buttons (already reachable from the
   * sidebar / More tab on mobile). */
  #density-toggle,
  #logout-btn { display: none !important; }
  /* Push-subscribe button: icon-only on phones. */
  #push-subscribe-btn {
    padding: 6px 8px !important;
    font-size: 0 !important;        /* hides the "Add to Home Screen" text */
    line-height: 1 !important;
    flex: 0 0 auto;
  }
  #push-subscribe-btn::before {
    content: "🔔";
    font-size: 16px;
    line-height: 1;
  }
  /* Buttons that remain (theme + bell): consistent compact size. */
  #theme-toggle,
  #notif-btn {
    padding: 6px 8px !important;
    flex: 0 0 auto;
  }
}

/* ════════════════════════════════════════════════════════════════════════
   Shared 3-step status confirmation component (.ord-status-*) — 2026-05-06
   ────────────────────────────────────────────────────────────────────────
   A reusable 3-step (tap → arm → slide) status-change confirmation card.
   Prevents accidental status mutations by requiring two taps + a slide.
   Designed to be reused across modules wherever a status pill needs a
   safe, mobile-first commit gesture (export status, lot status, QC, etc.).

   States/modifiers:
     .ord-status-card                        — base card
     .ord-status-card.is-arming              — State 2 (after first tap)
     .ord-status-card.is-final               — State 3 (slide-to-confirm)
     .ord-status-card.is-saving              — State 4 (network in flight)
     .ord-status-card.is-error               — State 5 (save failed)

   Source: mock-status-confirm.html (5 example states + interactive demo).
   ════════════════════════════════════════════════════════════════════════ */

/* ── Component-local tokens (additive — main :root tokens still apply) ── */
:root {
  --amber-bg:     #fef3c7;
  --amber-fg:     #92400e;
  --amber-bd:     #fde68a;
  --red-bg:       #fee2e2;
  --red-fg:       #991b1b;
  --red-bd:       #fecaca;
  --green-bg:     #dcfce7;
  --green-fg:     #166534;
  --green-bd:     #bbf7d0;
  --primary-soft: #eef2ff;
}

/* ── card ──────────────────────────────────────────────────────────────── */
.ord-status-card {
  background: var(--surface-sunken);
  border: 1px solid var(--line);
  border-radius: 12px;
  padding: 14px 16px;
  margin: 0 0 16px;
  transition: background 200ms ease, border-color 200ms ease;
}
.ord-status-card.is-arming {
  background: color-mix(in oklch, var(--amber-bg) 35%, var(--surface));
  border-color: var(--amber-bd);
}
.ord-status-card.is-final {
  background: color-mix(in oklch, var(--primary-soft) 60%, var(--surface));
  border-color: var(--primary);
}
.ord-status-card.is-saving { opacity: .92; }
.ord-status-card.is-error {
  border-color: var(--red-fg);
  background: color-mix(in oklch, var(--red-bg) 30%, var(--surface));
}

.ord-status-card__head {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 10px;
}
.ord-status-card__label {
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-secondary);
}
.ord-status-card__meta {
  font-size: 11px;
  color: var(--muted);
}

/* ── current pill (State 1) ────────────────────────────────────────────── */
.ord-status-card__current {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 12px;
}

/* ── from→to preview (States 2, 3, 4) ──────────────────────────────────── */
.ord-status-card__transition {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 12px;
  flex-wrap: wrap;
}
.ord-status-card__arrow {
  color: var(--muted);
  font-weight: 700;
  font-size: 18px;
}

/* ── pill ──────────────────────────────────────────────────────────────── */
.ord-status-pill {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 6px 12px;
  border-radius: 999px;
  font-size: 14px;
  font-weight: 700;
  letter-spacing: 0.02em;
  border: 1px solid transparent;
  line-height: 1.2;
}
.ord-status-pill--not-ready {
  background: var(--amber-bg);
  color: var(--amber-fg);
  border-color: var(--amber-bd);
}
.ord-status-pill--hold {
  background: var(--red-bg);
  color: var(--red-fg);
  border-color: var(--red-bd);
}
.ord-status-pill--complete {
  background: var(--green-bg);
  color: var(--green-fg);
  border-color: var(--green-bd);
}
.ord-status-pill.is-from   { opacity: .55; }
.ord-status-pill.is-fading { opacity: .35; }

/* ── hint + options grid (State 1) ─────────────────────────────────────── */
.ord-status-card__hint {
  font-size: 12px;
  color: var(--muted);
  margin-bottom: 8px;
}
.ord-status-card__options {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
}
.ord-status-opt {
  flex: 1;
  min-width: 100px;
  min-height: 44px;
  padding: 10px 12px;
  border-radius: 10px;
  border: 1.5px solid var(--line);
  background: var(--surface);
  color: var(--text);
  font: inherit;
  font-size: 14px;
  font-weight: 500;
  cursor: pointer;
  transition: transform 120ms ease, background 150ms ease, border-color 150ms ease, box-shadow 150ms ease;
}
.ord-status-opt:active { transform: scale(0.97); }
.ord-status-opt.is-current {
  background: var(--surface-sunken);
  color: var(--muted);
  cursor: default;
  font-weight: 600;
}
.ord-status-opt--not-ready { border-color: var(--amber-bd); }
.ord-status-opt--hold      { border-color: var(--red-bd); }
.ord-status-opt--complete  { border-color: var(--green-bd); }
.ord-status-opt--not-ready.is-current {
  background: var(--amber-bg);
  color: var(--amber-fg);
}
.ord-status-opt--hold.is-current {
  background: var(--red-bg);
  color: var(--red-fg);
}
.ord-status-opt--complete.is-current {
  background: var(--green-bg);
  color: var(--green-fg);
}

/* ── State 2 — armed actions ───────────────────────────────────────────── */
.ord-status-card__armed-actions {
  display: flex;
  gap: 8px;
  align-items: stretch;
}
.ord-status-cancel {
  flex: 0 0 auto;
  min-height: 44px;
  padding: 10px 16px;
  border-radius: 10px;
  border: 1px solid var(--line);
  background: var(--surface);
  color: var(--text);
  font: inherit;
  font-size: 14px;
  font-weight: 500;
  cursor: pointer;
}
.ord-status-cancel:active { transform: scale(0.97); }
.ord-status-confirm {
  flex: 1;
  position: relative;
  overflow: hidden;
  min-height: 44px;
  padding: 10px 14px;
  border-radius: 10px;
  border: 0;
  background: var(--amber-fg);
  color: white;
  font: inherit;
  font-size: 14px;
  font-weight: 700;
  letter-spacing: 0.02em;
  cursor: pointer;
}
.ord-status-confirm__bar {
  position: absolute;
  inset: 0;
  background: rgba(255,255,255,.18);
  transform-origin: right center;
  transform: scaleX(1);
  animation: confirm-countdown 4s linear forwards;
}
@keyframes confirm-countdown {
  from { transform: scaleX(1); }
  to   { transform: scaleX(0); }
}
.ord-status-confirm__label {
  position: relative;
  z-index: 1;
}

/* ── State 3 — final-check slider ──────────────────────────────────────── */
.ord-status-card__warn {
  margin: 0 0 12px;
  font-size: 13px;
  color: var(--text-secondary);
  line-height: 1.4;
}
.ord-status-card__warn strong { color: var(--text); }

.ord-status-slider {
  position: relative;
  height: 56px;
  margin-bottom: 10px;
  border-radius: 14px;
  background: var(--surface);
  border: 1.5px solid var(--line);
  overflow: hidden;
  user-select: none;
  -webkit-user-select: none;
  touch-action: pan-y;
}
.ord-status-slider__fill {
  position: absolute;
  inset: 0 100% 0 0;
  background: linear-gradient(
    90deg,
    color-mix(in oklch, var(--red-fg) 22%, transparent),
    color-mix(in oklch, var(--red-fg) 38%, transparent)
  );
  transition: inset 120ms cubic-bezier(0.16, 1, 0.3, 1);
  pointer-events: none;
}
.ord-status-slider.is-target-hold .ord-status-slider__fill {
  background: linear-gradient(
    90deg,
    color-mix(in oklch, var(--red-fg) 22%, transparent),
    color-mix(in oklch, var(--red-fg) 38%, transparent)
  );
}
.ord-status-slider.is-target-complete .ord-status-slider__fill {
  background: linear-gradient(
    90deg,
    color-mix(in oklch, var(--green-fg) 22%, transparent),
    color-mix(in oklch, var(--green-fg) 38%, transparent)
  );
}
.ord-status-slider.is-target-not-ready .ord-status-slider__fill {
  background: linear-gradient(
    90deg,
    color-mix(in oklch, var(--amber-fg) 22%, transparent),
    color-mix(in oklch, var(--amber-fg) 38%, transparent)
  );
}

.ord-status-slider__hint {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  font-size: 13px;
  font-weight: 600;
  color: var(--muted);
  letter-spacing: 0.04em;
  text-transform: uppercase;
  pointer-events: none;
  transition: opacity 120ms ease;
}
.ord-status-slider.is-dragging      .ord-status-slider__hint,
.ord-status-slider.is-progress-40   .ord-status-slider__hint { opacity: .55; }
.ord-status-slider.is-progress-70   .ord-status-slider__hint { opacity: .25; }
.ord-status-slider.is-progress-90   .ord-status-slider__hint { opacity: 0; }
.ord-status-slider__hint-text { /* no rules — element is part of hint group */ }
.ord-status-slider__hint-arrow {
  animation: hint-arrows 1.4s ease-in-out infinite;
}
@keyframes hint-arrows {
  0%, 100% { opacity: .35; transform: translateX(0); }
  50%      { opacity: 1;   transform: translateX(4px); }
}

.ord-status-slider__thumb {
  position: absolute;
  top: 4px;
  bottom: 4px;
  left: 4px;
  width: 48px;
  border-radius: 12px;
  background: var(--text);
  color: var(--surface);
  border: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 22px;
  font-weight: 700;
  cursor: grab;
  box-shadow: 0 2px 8px rgba(0,0,0,.12);
  transition: transform 120ms ease, background 150ms ease;
  touch-action: none;
}
.ord-status-slider__thumb:active,
.ord-status-slider.is-dragging .ord-status-slider__thumb {
  cursor: grabbing;
  transform: scale(1.05);
}
.ord-status-slider__thumb-icon { /* hook for optional inner icon */ }
.ord-status-slider.is-snapping .ord-status-slider__thumb {
  transition: left 200ms cubic-bezier(0.34, 1.56, 0.64, 1);
}

.ord-status-cancel--final {
  width: 100%;
  background: transparent;
  border-color: transparent;
  color: var(--muted);
  font-weight: 500;
}

/* ── State 4 — saving ──────────────────────────────────────────────────── */
.ord-status-spinner {
  width: 14px;
  height: 14px;
  border: 2px solid currentColor;
  border-top-color: transparent;
  border-radius: 50%;
  animation: spin 700ms linear infinite;
}
@keyframes spin {
  to { transform: rotate(360deg); }
}

/* ── State 5 — error ───────────────────────────────────────────────────── */
.ord-status-card__error {
  margin-top: 10px;
  padding: 10px 12px;
  border-radius: 8px;
  background: var(--red-bg);
  color: var(--red-fg);
  border: 1px solid var(--red-bd);
  font-size: 13px;
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 8px;
}
.ord-status-retry {
  background: var(--red-fg);
  color: white;
  border: 0;
  padding: 6px 12px;
  border-radius: 6px;
  font-weight: 600;
  cursor: pointer;
  font-size: 12px;
}
/* ════════ end shared 3-step status confirmation component ═══════════════ */

/* Briefly flash a row green after a status save so the user can re-verify
 * the change after the table re-renders. Keyed by `[data-row-id]` which
 * `renderTableVirtual` already sets on every <tr>. */
@keyframes ord-row-flash {
  0%   { background: rgba(22,163,74,.16); }
  60%  { background: rgba(22,163,74,.16); }
  100% { background: transparent; }
}
.row-just-updated {
  animation: ord-row-flash 1.4s cubic-bezier(0.16, 1, 0.3, 1) forwards;
}

/* ════════ Shipment Planner saved-plan rows — mobile stacking ════════════
 * On narrow viewports (PWA / phone) the inline-flex row with a totals span
 * carrying margin-left:auto blows past the viewport. Stack vertically and
 * left-align everything below 600px so the row hugs the card width. The
 * action buttons sit on their own line at the bottom-right. */
@media (max-width: 600px) {
  .sp-row {
    flex-direction: column !important;
    align-items: stretch !important;
    gap: 6px !important;
  }
  .sp-row .sp-row-link {
    flex-direction: column !important;
    align-items: flex-start !important;
    gap: 4px !important;
    width: 100%;
  }
  .sp-row .sp-row-totals {
    margin-left: 0 !important;
    width: 100%;
  }
  .sp-row .sp-row-actions {
    align-self: flex-end;
  }
}
/* ════════ end shipment planner mobile fixes ═══════════════════════════ */


/* ════════════════════════════════════════════════════════════════════════
   AI Reports — daily-report drawer (Phase 4a UI, 2026-05-10)

   Two surfaces:
   - .ai-reports-grid       module list view, dept cards
   - .ai-rep-drawer         per-dept full report viewer (also used for
                            the auto-popup at 08:00 IST)

   PWA-friendly: full-screen overlay on iOS / Android phone (max-width
   768px), centered modal with backdrop on tablet/desktop. Dismiss X
   is 44pt minimum tap target per Apple HIG, top-right, padded by
   safe-area-inset-top so it stays clear of the iPhone notch / Android
   status bar. Body scrolls independently with momentum on iOS.
   ──────────────────────────────────────────────────────────────────── */

.ai-reports-wrap { padding: 12px 16px 24px; }
.ai-reports-toolbar {
  display: flex; gap: 10px; align-items: center;
  margin-bottom: 16px; flex-wrap: wrap;
}
.ai-reports-toolbar .lst-form-input { max-width: 180px; }

.ai-reports-grid {
  display: grid; gap: 14px;
  grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
}
.ai-rep-card {
  background: var(--surface, #fff);
  border: 1px solid var(--line, #e5e7eb);
  border-radius: 12px;
  padding: 14px 16px;
  cursor: pointer;
  transition: transform .12s ease, box-shadow .12s ease, border-color .12s;
  display: flex; flex-direction: column; gap: 8px;
  -webkit-tap-highlight-color: transparent;
}
.ai-rep-card:hover { transform: translateY(-1px); border-color: var(--primary, #2563eb); box-shadow: 0 6px 16px rgba(0,0,0,.06); }
.ai-rep-card:active { transform: translateY(0); }
.ai-rep-card--empty { background: var(--surface-muted, #f9fafb); }
.ai-rep-card__head { display: flex; justify-content: space-between; align-items: baseline; gap: 8px; }
.ai-rep-card__title { font-weight: 700; font-size: 1.02rem; color: var(--text, #111827); }
.ai-rep-card__cron  { font-size: .78rem; color: var(--text-secondary, #6b7280); white-space: nowrap; }
.ai-rep-card__preview {
  font-size: .9rem; color: var(--text, #1f2937); line-height: 1.5;
  display: -webkit-box; -webkit-line-clamp: 4; -webkit-box-orient: vertical;
  overflow: hidden; min-height: 4.5em;
}
.ai-rep-card__empty-text { font-size: .88rem; color: var(--text-secondary, #6b7280); font-style: italic; }
.ai-rep-card__meta { display: flex; justify-content: space-between; align-items: center; gap: 8px; margin-top: auto; padding-top: 6px; }
.ai-rep-card__when { font-size: .76rem; color: var(--text-secondary, #6b7280); }
.ai-rep-card-regen { font-size: .82rem; padding: 6px 12px; }

.ai-rep-empty {
  padding: 32px 18px; text-align: center;
  color: var(--text-secondary, #6b7280); font-size: .92rem;
  border: 1px dashed var(--line, #d1d5db); border-radius: 12px;
  background: var(--surface-muted, #f9fafb);
}
.ai-rep-loading { padding: 28px 14px; text-align: center; color: var(--text-secondary, #6b7280); }


/* ── Drawer — per-dept full report ─────────────────────────────────── */

.ai-rep-drawer {
  position: fixed; inset: 0; z-index: 9000;
  display: flex; justify-content: center; align-items: stretch;
  pointer-events: none;
}
.ai-rep-drawer__scrim {
  position: absolute; inset: 0;
  background: rgba(15, 23, 42, 0);
  transition: background .22s ease;
  pointer-events: auto;
}
.ai-rep-drawer--open .ai-rep-drawer__scrim { background: rgba(15, 23, 42, .54); }

.ai-rep-drawer__panel {
  position: relative;
  background: var(--surface, #fff);
  display: flex; flex-direction: column;
  width: 100%; max-width: 640px;
  margin: auto;
  max-height: 92vh;
  border-radius: 16px;
  box-shadow: 0 18px 48px rgba(0, 0, 0, .24);
  transform: translateY(40px);
  opacity: 0;
  transition: transform .26s cubic-bezier(.2,.8,.2,1), opacity .22s ease;
  pointer-events: auto;
  overflow: hidden;
}
.ai-rep-drawer--open .ai-rep-drawer__panel { transform: translateY(0); opacity: 1; }

.ai-rep-drawer__head {
  display: flex; align-items: flex-start;
  padding:
    max(14px, env(safe-area-inset-top)) max(16px, env(safe-area-inset-right))
    14px max(20px, env(safe-area-inset-left));
  gap: 12px;
  border-bottom: 1px solid var(--line, #e5e7eb);
  background: var(--surface, #fff);
}
.ai-rep-drawer__title {
  flex: 1; min-width: 0;
  font-size: 1.18rem; font-weight: 700;
  color: var(--text, #111827);
  line-height: 1.35;
  word-break: break-word;
}
/* 44×44 tap target per Apple HIG; visual icon centered inside.
   Focus ring respects keyboard users; -webkit-tap-highlight removed
   to avoid the gray flash on iOS taps. */
.ai-rep-drawer__close {
  flex-shrink: 0;
  width: 44px; height: 44px;
  border-radius: 12px;
  border: 1px solid transparent;
  background: var(--surface-muted, #f3f4f6);
  color: var(--text, #111827);
  display: inline-flex; align-items: center; justify-content: center;
  cursor: pointer;
  transition: background .12s, transform .08s;
  -webkit-tap-highlight-color: transparent;
}
.ai-rep-drawer__close:hover  { background: var(--line, #e5e7eb); }
.ai-rep-drawer__close:active { transform: scale(.94); }
.ai-rep-drawer__close:focus-visible { outline: 2px solid var(--primary, #2563eb); outline-offset: 2px; }

.ai-rep-drawer__meta {
  display: flex; flex-wrap: wrap; gap: 6px;
  padding: 10px 16px 4px;
  border-bottom: 1px solid var(--line, #e5e7eb);
  background: var(--surface, #fff);
}
.ai-rep-pill {
  font-size: .72rem; padding: 3px 8px;
  border-radius: 999px;
  background: var(--surface-muted, #f3f4f6);
  color: var(--text-secondary, #4b5563);
  border: 1px solid var(--line, #e5e7eb);
  white-space: nowrap;
}

.ai-rep-drawer__body {
  flex: 1; min-height: 0;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;       /* iOS momentum scroll */
  overscroll-behavior: contain;
  padding: 16px max(18px, env(safe-area-inset-right))
           20px max(18px, env(safe-area-inset-left));
  background: var(--surface, #fff);
}
.ai-rep-content {
  white-space: pre-wrap;
  font-family: -apple-system, "Helvetica Neue", "Segoe UI", Roboto, sans-serif;
  font-size: .98rem;
  line-height: 1.65;
  color: var(--text, #1f2937);
  word-wrap: break-word;
  overflow-wrap: anywhere;     /* prevents horizontal scroll on long words */
}
.ai-rep-content + .ai-rep-content { margin-top: 22px; padding-top: 18px; border-top: 1px dashed var(--line, #e5e7eb); }
.ai-rep-content-block + .ai-rep-content-block { margin-top: 22px; padding-top: 18px; border-top: 1px dashed var(--line, #e5e7eb); }
.ai-rep-section-head {
  font-size: .78rem; font-weight: 700; letter-spacing: .04em;
  text-transform: uppercase; color: var(--text-secondary, #6b7280);
  margin-bottom: 6px;
}

.ai-rep-drawer__foot {
  display: flex; gap: 10px; justify-content: flex-end;
  padding: 12px max(16px, env(safe-area-inset-right))
           max(14px, env(safe-area-inset-bottom)) max(16px, env(safe-area-inset-left));
  border-top: 1px solid var(--line, #e5e7eb);
  background: var(--surface, #fff);
}
.ai-rep-drawer__foot .btn { min-height: 44px; padding-left: 18px; padding-right: 18px; }

/* Play button (drawer footer) — speech synthesis toggle */
.ai-rep-play { display: inline-flex; align-items: center; gap: 8px; }
.ai-rep-play__icon {
  display: inline-flex; align-items: center; justify-content: center;
  width: 22px; height: 22px;
  font-size: .9rem; line-height: 1;
}
.ai-rep-play--active {
  background: var(--primary, #2563eb);
  color: #fff;
  border-color: var(--primary, #2563eb);
}
.ai-rep-play--active .ai-rep-play__icon { color: #fff; }

/* Per-card play button (small, top-right corner) */
.ai-rep-card-play {
  display: inline-flex; align-items: center; justify-content: center;
  width: 36px; height: 36px;
  margin-right: 8px;
  border-radius: 50%;
  border: 1px solid var(--line, #e5e7eb);
  background: var(--surface, #fff);
  cursor: pointer;
  transition: background .12s, transform .08s;
  -webkit-tap-highlight-color: transparent;
  vertical-align: middle;
}
.ai-rep-card-play:hover  { background: var(--surface-muted, #f3f4f6); }
.ai-rep-card-play:active { transform: scale(.92); }
.ai-rep-card-play--active {
  background: var(--primary, #2563eb);
  border-color: var(--primary, #2563eb);
  color: #fff;
}
.ai-rep-card-play--active .ai-rep-play__icon { color: #fff; }


/* ── Report content typography ────────────────────────────────────────
   Things 3 minimalism + Spotify accent rhythm. Generous whitespace,
   restrained color, bold hierarchy from typography weight not borders.
   Pills use soft-tinted backgrounds with their own family hue rather
   than competing accent colors. The model's plain-text output stays
   unchanged — _aiRepRender() in app.js wraps spans/headings.
   Pure presentation layer. */

.ai-rep-content {
  font-family: -apple-system, "SF Pro Text", "Helvetica Neue", "Segoe UI", Roboto, system-ui, sans-serif;
  font-size: 1rem;
  line-height: 1.7;
  color: var(--text, #1f2937);
  letter-spacing: -.005em;
}
.ai-rep-content > * + * { margin-top: 18px; }

/* Things-3-style heading — refined weight (600 not 700), small accent
   bar to the left, generous breathing room above. No underline; the
   weight + spacing carry the hierarchy. */
.ai-rep-heading {
  font-size: 1.16rem;
  font-weight: 600;
  color: var(--text, #0f172a);
  letter-spacing: -.012em;
  line-height: 1.35;
  position: relative;
  padding: 0 0 0 14px;
  margin: 28px 0 6px !important;
}
.ai-rep-heading::before {
  content: "";
  position: absolute;
  left: 0; top: .3em; bottom: .3em;
  width: 3px; border-radius: 3px;
  background: var(--primary, #4f46e5);
}
.ai-rep-content > .ai-rep-heading:first-child { margin-top: 4px !important; }

.ai-rep-para { margin: 0; line-height: 1.7; }
.ai-rep-para--empty { color: var(--text-secondary, #9ca3af); font-style: italic; }

.ai-rep-list {
  margin: 8px 0 0;
  padding: 0;
  list-style: none;
}
.ai-rep-list li {
  position: relative;
  padding: 6px 0 6px 22px;
  line-height: 1.6;
}
.ai-rep-list li + li { border-top: 1px solid var(--line, rgba(15, 23, 42, .06)); }
.ai-rep-list li::before {
  content: "";
  position: absolute;
  left: 6px; top: .85em;
  width: 6px; height: 6px;
  border-radius: 50%;
  background: var(--primary, #4f46e5);
}

/* Numbers + units — Spotify-style: large bold tabular numerals,
   no highlight box, just typographic weight. Tabular nums keep
   columns aligned in card previews. */
.ai-rep-num {
  font-weight: 700;
  color: var(--text, #0f172a);
  font-variant-numeric: tabular-nums;
  letter-spacing: -.005em;
  white-space: nowrap;
}

/* Pills — softer tint, no border, slight bg only. Smaller than
   before so they feel inline-with-text, not blocky. */
.ai-rep-chip {
  display: inline-block;
  padding: 1px 8px;
  margin: 0 2px;
  border-radius: 999px;
  font-size: .74rem;
  font-weight: 600;
  letter-spacing: .02em;
  vertical-align: 1px;
  white-space: nowrap;
  line-height: 1.5;
  border: none;
}
.ai-rep-chip--country { background: rgba(79,70,229,.10);  color: #4338ca; }
.ai-rep-chip--gh      { background: rgba(217,119,6,.12);  color: #b45309; }
.ai-rep-chip--status  { background: rgba(16,185,129,.12); color: #047857; }
.ai-rep-chip--urgent  { background: rgba(220,38,38,.12);  color: #b91c1c; }

/* Dates — tabular nums, softest accent */
.ai-rep-date {
  display: inline-block;
  padding: 0 5px;
  font-variant-numeric: tabular-nums;
  color: var(--text-secondary, #475569);
  background: rgba(15, 23, 42, .04);
  border-radius: 5px;
  font-size: .92em;
  font-weight: 500;
}

/* Generous body padding inside the drawer body */
.ai-rep-drawer__body { padding: 22px max(20px, env(safe-area-inset-right)) 28px max(20px, env(safe-area-inset-left)); }

/* Spotify-green Play button — replaces the muted .ai-rep-play look */
.ai-rep-play {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  background: rgba(29,185,84,.12);
  color: #15803d;
  border: 1px solid rgba(29,185,84,.2);
  font-weight: 600;
}
.ai-rep-play:hover  { background: rgba(29,185,84,.18); }
.ai-rep-play:active { transform: scale(.97); }
.ai-rep-play--active {
  background: #1db954;
  color: #fff;
  border-color: #1db954;
  box-shadow: 0 4px 12px rgba(29,185,84,.30);
}
.ai-rep-play--active:hover { background: #1ed760; }
.ai-rep-play__icon {
  display: inline-flex;
  align-items: center; justify-content: center;
  width: 22px; height: 22px;
  font-size: .92rem;
  line-height: 1;
}

/* Per-card play button — round, Spotify green */
.ai-rep-card-play {
  background: rgba(29,185,84,.10);
  border: 1px solid rgba(29,185,84,.18);
  color: #15803d;
}
.ai-rep-card-play:hover { background: rgba(29,185,84,.18); }
.ai-rep-card-play--active {
  background: #1db954;
  border-color: #1db954;
  color: #fff;
  box-shadow: 0 4px 10px rgba(29,185,84,.32);
}

/* Cards — Things-3 softer */
.ai-rep-card {
  border-radius: 14px;
  border-color: var(--line, rgba(15,23,42,.08));
  box-shadow: 0 1px 3px rgba(15,23,42,.04);
  padding: 16px 18px;
}
.ai-rep-card:hover {
  border-color: var(--primary-light, #e0e7ff);
  box-shadow: 0 6px 20px rgba(15,23,42,.07);
}
.ai-rep-card__title { font-size: 1.06rem; font-weight: 600; letter-spacing: -.01em; }
.ai-rep-card__cron  { display: flex; align-items: center; gap: 6px; }
.ai-rep-card__preview {
  font-size: .92rem;
  color: var(--text-secondary, #4b5563);
  line-height: 1.55;
}

/* Drawer panel — Things-3 soft */
.ai-rep-drawer__panel {
  border-radius: 18px;
  box-shadow: 0 24px 64px rgba(15, 23, 42, .22);
}
.ai-rep-drawer__title { font-size: 1.22rem; font-weight: 700; letter-spacing: -.018em; }
.ai-rep-drawer__head { gap: 14px; }
.ai-rep-drawer__close { border-radius: 14px; }

/* Section-head label inside auto-popup combined drawer */
.ai-rep-section-head {
  font-size: .72rem;
  font-weight: 700;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--primary, #4f46e5);
  margin-bottom: 10px;
}

/* Mobile tightening */
@media (max-width: 768px) {
  .ai-rep-heading        { font-size: 1.08rem; margin-top: 22px !important; }
  .ai-rep-content        { font-size: .96rem; line-height: 1.65; }
  .ai-rep-chip           { font-size: .7rem; padding: 1px 7px; }
  .ai-rep-drawer__title  { font-size: 1.1rem; }
  .ai-rep-drawer__body   { padding: 18px 16px 24px; }
  .ai-rep-card           { padding: 14px 16px; }
  .ai-rep-card__title    { font-size: 1.0rem; }
}

/* AI Reports dark mode is gated solely on the app's html[data-theme="dark"]
   selector (added below) per user direction 2026-05-10 — we explicitly do
   NOT auto-flip on OS prefers-color-scheme. The drawer should track the
   header toggle, not the system. */

/* ── Mobile: full-screen, edge-to-edge, no border-radius on the panel
       so the dismiss X looks intentional at the top-right corner. ── */
@media (max-width: 768px) {
  .ai-rep-drawer__panel {
    max-width: 100%; max-height: 100%;
    height: 100dvh;        /* uses dynamic vh — accounts for iOS bottom bar */
    margin: 0;
    border-radius: 0;
    transform: translateY(100%);
  }
  .ai-rep-drawer--open .ai-rep-drawer__panel { transform: translateY(0); }
  .ai-rep-drawer__head { padding-top: max(18px, env(safe-area-inset-top)); }
  .ai-rep-drawer__title { font-size: 1.06rem; }
  .ai-reports-grid { grid-template-columns: 1fr; gap: 12px; }
}
/* iOS PWA standalone: account for the home-indicator strip. */
@media (display-mode: standalone) and (max-width: 768px) {
  .ai-rep-drawer__foot { padding-bottom: max(20px, env(safe-area-inset-bottom)); }
}

/* ── Dark mode (manual toggle ONLY: html[data-theme="dark"]) ─────────
   Per user direction 2026-05-10, AI Reports dark theme tracks the app's
   header toggle and NOT OS prefers-color-scheme. The earlier @media
   block was removed for that reason — these data-theme rules are the
   single source of truth. Stone-toned surfaces (#0f172a / #1e293b) +
   #f8fafc text — visually consistent with the existing
   html[data-theme="dark"] block at the top of this file. */
html[data-theme="dark"] .ai-rep-num            { color: #f3f4f6; }
html[data-theme="dark"] .ai-rep-heading        { color: #f9fafb; }
html[data-theme="dark"] .ai-rep-heading::before{ background: #818cf8; }
html[data-theme="dark"] .ai-rep-chip--country  { background: rgba(129,140,248,.18); color: #c7d2fe; }
html[data-theme="dark"] .ai-rep-chip--gh       { background: rgba(245,158,11,.20);  color: #fde68a; }
html[data-theme="dark"] .ai-rep-chip--status   { background: rgba(16,185,129,.18);  color: #6ee7b7; }
html[data-theme="dark"] .ai-rep-chip--urgent   { background: rgba(220,38,38,.20);   color: #fecaca; }
html[data-theme="dark"] .ai-rep-date           { background: rgba(255,255,255,.06); color: #94a3b8; }
html[data-theme="dark"] .ai-rep-list li + li   { border-top-color: rgba(255,255,255,.08); }

/* Cards — surface-2 (#1e293b) with subtle slate border */
html[data-theme="dark"] .ai-rep-card {
  background: #1e293b;
  border-color: #334155;
  box-shadow: 0 1px 3px rgba(0,0,0,.32);
}
html[data-theme="dark"] .ai-rep-card:hover {
  border-color: #818cf8;
  box-shadow: 0 6px 20px rgba(0,0,0,.40);
}
html[data-theme="dark"] .ai-rep-card--empty   { background: #0f172a; }
html[data-theme="dark"] .ai-rep-card__title   { color: #f8fafc; }
html[data-theme="dark"] .ai-rep-card__cron    { color: #94a3b8; }
html[data-theme="dark"] .ai-rep-card__preview { color: #cbd5e1; }
html[data-theme="dark"] .ai-rep-card__when    { color: #94a3b8; }
html[data-theme="dark"] .ai-rep-card__empty-text { color: #94a3b8; }

/* Drawer — surface-1 (#0f172a) panel, surface-2 (#1e293b) chrome rows */
html[data-theme="dark"] .ai-rep-drawer__panel {
  background: #0f172a;
  box-shadow: 0 24px 64px rgba(0, 0, 0, .56);
}
html[data-theme="dark"] .ai-rep-drawer__head {
  background: #1e293b;
  border-bottom-color: #334155;
}
html[data-theme="dark"] .ai-rep-drawer__title { color: #f8fafc; }
html[data-theme="dark"] .ai-rep-drawer__meta {
  background: #1e293b;
  border-bottom-color: #334155;
}
html[data-theme="dark"] .ai-rep-drawer__body {
  background: #0f172a;
}
html[data-theme="dark"] .ai-rep-drawer__close {
  background: #334155;
  color: #f8fafc;
  border-color: transparent;
}
html[data-theme="dark"] .ai-rep-drawer__close:hover { background: #475569; }
html[data-theme="dark"] .ai-rep-drawer__foot {
  background: #1e293b;
  border-top-color: #334155;
}

/* Body content text */
html[data-theme="dark"] .ai-rep-content { color: #e2e8f0; }
html[data-theme="dark"] .ai-rep-content + .ai-rep-content,
html[data-theme="dark"] .ai-rep-content-block + .ai-rep-content-block {
  border-top-color: rgba(255,255,255,.08);
}
html[data-theme="dark"] .ai-rep-section-head { color: #94a3b8; }
html[data-theme="dark"] .ai-rep-para--empty { color: #64748b; }

/* Meta pills inside drawer head */
html[data-theme="dark"] .ai-rep-pill {
  background: #334155;
  color: #cbd5e1;
  border-color: #475569;
}

/* Empty state */
html[data-theme="dark"] .ai-rep-empty {
  background: #1e293b;
  border-color: #334155;
  color: #94a3b8;
}
html[data-theme="dark"] .ai-rep-loading { color: #94a3b8; }

/* Per-card play button — keep Spotify green tint, just lift the
   resting background so it reads on dark surface */
html[data-theme="dark"] .ai-rep-card-play {
  background: rgba(29,185,84,.14);
  border-color: rgba(29,185,84,.28);
  color: #4ade80;
}
html[data-theme="dark"] .ai-rep-card-play:hover { background: rgba(29,185,84,.22); }

/* Drawer-foot Play button */
html[data-theme="dark"] .ai-rep-play {
  background: rgba(29,185,84,.14);
  color: #4ade80;
  border-color: rgba(29,185,84,.28);
}
html[data-theme="dark"] .ai-rep-play:hover { background: rgba(29,185,84,.22); }

/* ════════ end AI Reports drawer ═══════════════════════════════════════ */

/* ════════ Export Entries Summary (Reports → Export Summary) ════════
   Per-country yards & packages by GH ready status.
   Mock at mocks/export-summary.html. PWA-safe: no fixed positioning,
   no transforms, ≥44px tap targets, 16px text on inputs (none here),
   wraps to single column on phones. */
.es-wrap{ max-width:720px; margin:0 auto; padding:0 4px 60px; }
.es-head{
  display:flex; justify-content:space-between; align-items:center;
  gap:12px; flex-wrap:wrap; margin:6px 0 18px;
}
.es-share{
  display:inline-flex; align-items:center; gap:6px;
  padding:9px 14px; border-radius:999px;
  background:var(--primary, #2f6f4f); color:#fff;
  border:none; cursor:pointer; font:inherit; font-size:.92rem;
  min-height:44px;
}
.es-share:hover, .es-share:focus-visible{
  filter:brightness(1.05); outline:none;
  box-shadow:0 0 0 3px var(--primary-glow, rgba(47,111,79,.15));
}

.es-card{
  background:var(--surface);
  border:1px solid var(--line);
  border-radius:14px;
  padding:14px 16px 16px;
  margin-bottom:12px;
}
.es-card--total{
  background:linear-gradient(180deg,#fffdf6,var(--surface));
  border-color:#d6cfb8;
}
.es-card__head{
  display:flex; justify-content:space-between; align-items:baseline;
  gap:10px; flex-wrap:wrap; margin-bottom:10px;
}
.es-card__sub{
  color:var(--text-secondary); font-size:.78rem;
  font-weight:600;                       /* ~30% bolder than the prior regular weight */
  font-variant-numeric:tabular-nums;
}

/* Country chip — same palette as the Export Entries page chips */
.es-country{
  display:inline-flex; align-items:center; gap:6px;
  padding:5px 12px; border-radius:999px;
  font-weight:700; letter-spacing:.04em; text-transform:uppercase;
  font-size:.78rem;
  border:1px solid var(--line);
  background:var(--surface-sunken); color:var(--text-secondary);
  white-space:nowrap;
}
.es-country__flag{ font-size:1rem; line-height:1; }
.es-country--lagos  { background:rgba(14,165,233,.12); color:#0369a1; border-color:rgba(14,165,233,.3); }
.es-country--kano   { background:rgba(245,158,11,.12); color:#92400e; border-color:rgba(245,158,11,.3); }
.es-country--cj     { background:rgba(16,185,129,.12); color:#065f46; border-color:rgba(16,185,129,.3); }
.es-country--sn     { background:var(--surface-sunken); color:var(--muted); border-color:var(--line); }
.es-country--aa     { background:rgba(168,85,247,.12); color:#6b21a8; border-color:rgba(168,85,247,.3); }
.es-country--austria{ background:rgba(99,102,241,.12); color:#3730a3; border-color:rgba(99,102,241,.3); }
.es-country--uk     { background:rgba(220,38,38,.10); color:#991b1b; border-color:rgba(220,38,38,.3); }
.es-country--open   { background:var(--surface-sunken); color:var(--muted); border-color:var(--line); }
.es-country--total  { background:rgba(47,111,79,.12); color:var(--primary, #2f6f4f); border-color:rgba(47,111,79,.3); }

.es-grid{ display:grid; grid-template-columns:1fr 1fr 1fr; gap:8px; }
.es-cell{
  background:var(--surface-sunken);
  border:1px solid var(--line);
  border-radius:10px;
  padding:10px 8px 8px;
  text-align:center;
  cursor:pointer;
  min-height:88px;
  display:flex; flex-direction:column; justify-content:center;
  transition:background .15s, border-color .15s;
}
.es-cell:hover, .es-cell:focus-visible{
  background:#ebe8e1; outline:none;
}
.es-cell__label{
  font-size:.66rem; font-weight:700; letter-spacing:.05em;
  text-transform:uppercase; margin-bottom:4px;
}
.es-cell--nr   .es-cell__label{ color:#b46a00; }
.es-cell--hold .es-cell__label{ color:#1a6cb0; }
.es-cell--lc   .es-cell__label{ color:#1f8a4c; }

.es-metric{
  font-size:1.15rem; font-weight:700; letter-spacing:-.01em;
  font-variant-numeric:tabular-nums; line-height:1.15;
  display:flex; align-items:baseline; justify-content:center; gap:3px;
}
.es-metric__unit{
  font-size:.62rem; font-weight:600; letter-spacing:.04em;
  text-transform:uppercase; opacity:.75;
}
.es-cell__yards{ color:var(--text); }
.es-cell__pkgs { color:var(--primary, #2f6f4f); margin-top:4px; }

/* Phone tighten — keep 3 columns down to ~340px width */
@media (max-width: 380px){
  .es-grid{ gap:6px; }
  .es-cell{ padding:8px 4px 6px; min-height:84px; }
  .es-metric{ font-size:1rem; }
  .es-metric__unit{ font-size:.58rem; }
}

/* Shortcut pill on Export Entries page → Summary */
.es-shortcut{
  display:inline-flex; align-items:center; gap:6px;
  padding:8px 14px; border-radius:999px;
  background:rgba(47,111,79,.12); color:var(--primary, #2f6f4f);
  border:1px solid rgba(47,111,79,.3); font:inherit; font-size:.85rem;
  font-weight:600;
  cursor:pointer; min-height:44px;
  margin:6px 0 12px; align-self:flex-start;
  transition:background .15s;
}
.es-shortcut:hover, .es-shortcut:focus-visible{
  background:rgba(47,111,79,.20); outline:none;
}
/* ════════ end Export Summary ═══════════════════════════════════════ */

/* ════════ Planning module ══════════════════════════════════════════ */
/* Native <select> dropdown wouldn't open in Chrome/Safari while Fabric
   Planning was the active module (2026-05-25 debug). Root cause: the
   focus-driven opacity transition on .fab (`body:has(select:focus) .fab
   { transition: opacity ... }` at styles.css:3559) was racing the native
   picker open. Globally disabling transitions in the live console
   instantly fixed it. v40.8 scoped the disable to .pl-controls only —
   wrong scope, .fab lives elsewhere in the DOM. v41.0 widens to .fab
   itself whenever the planning page is mounted (any descendant body
   has .pl-controls). The pl-pills also still need transition:none
   because they were sometimes hit by the same race. */
body:has(.pl-controls) .fab,
.pl-controls, .pl-controls *,
.section-card:has(.pl-controls) .section-card__head,
.section-card:has(.pl-controls) .section-card__head * {
  transition: none !important;
  animation: none !important;
}
/* Force .fab to stay visible — overrides the focus-driven opacity-0 rule
   at styles.css:3559 — so there's no opacity transition at all when the
   user focuses the planning dropdown. */
html[data-ui-version="v2"] body:has(.pl-controls .pl-select) .fab {
  opacity: 1 !important;
  pointer-events: auto !important;
}
.pl-controls{ display:flex; gap:8px; align-items:center; flex-wrap:wrap; }
.pl-select{
  font:inherit; font-size:.85rem; font-weight:600; padding:7px 10px;
  border-radius:9px; border:1px solid var(--line); background:var(--surface);
  color:var(--text); min-width:170px;
}
.pl-live{
  font-size:.78rem; color:var(--muted); margin:2px 2px 12px; display:flex;
  align-items:center; gap:6px; flex-wrap:wrap;
}
.pl-dot{
  width:8px; height:8px; border-radius:50%; background:#059669;
  display:inline-block; animation:pl-pulse 2s infinite;
}
@keyframes pl-pulse{
  0%{ box-shadow:0 0 0 0 rgba(5,150,105,.45); }
  70%{ box-shadow:0 0 0 6px rgba(5,150,105,0); }
  100%{ box-shadow:0 0 0 0 rgba(5,150,105,0); }
}
.pl-stale{ color:#b45309; font-weight:600; }

/* AI panel */
.pl-ai-panel{
  background:linear-gradient(135deg,rgba(79,70,229,.07),rgba(129,140,248,.07));
  border:1px solid rgba(99,102,241,.32); border-radius:12px;
  padding:12px 14px; margin-bottom:14px;
}
.pl-ai-loading{ color:var(--muted); font-size:.86rem; }
.pl-ai-head{
  font-weight:700; font-size:.9rem; margin-bottom:6px; display:flex;
  align-items:center; gap:8px; flex-wrap:wrap;
}
.pl-ai-time{ font-weight:500; font-size:.74rem; color:var(--muted); }
.pl-ai-stale{
  font-weight:700; font-size:.72rem; color:#b45309;
  background:rgba(180,83,9,.12); padding:2px 8px; border-radius:999px;
}
.pl-ai-body{ font-size:.88rem; line-height:1.6; color:var(--text); }
.pl-ai-body p{ margin:0 0 8px; }
.pl-ai-body p:last-child{ margin-bottom:0; }
.pl-ai-body strong{ color:var(--primary); font-weight:700; }
.pl-ai-list{
  margin:6px 0 10px; padding:0; list-style:none;
  display:flex; flex-direction:column; gap:7px;
}
.pl-ai-list li{
  position:relative; padding:7px 11px 7px 26px; border-radius:9px;
  background:rgba(99,102,241,.06); border:1px solid rgba(99,102,241,.16);
}
.pl-ai-list li::before{
  content:"▸"; position:absolute; left:10px; top:7px;
  color:var(--primary); font-weight:800;
}
.pl-ai-num{
  font-weight:700; color:#b91c1c; font-variant-numeric:tabular-nums;
  background:rgba(220,38,38,.09); padding:0 5px; border-radius:5px;
}
.pl-ai-err{ color:var(--danger); font-size:.85rem; }

/* summary tiles */
.pl-tiles{
  display:grid; grid-template-columns:repeat(5,1fr); gap:10px; margin-bottom:14px;
}
.pl-tile{
  background:var(--surface); border:1px solid var(--line); border-radius:12px;
  padding:12px 14px;
}
.pl-tile.hot{ background:rgba(220,38,38,.06); border-color:rgba(220,38,38,.25); }
.pl-tile-v{
  font-size:1.4rem; font-weight:800; letter-spacing:-.02em;
  font-variant-numeric:tabular-nums;
}
.pl-tile.hot .pl-tile-v{ color:#dc2626; }
.pl-tile-l{
  font-size:.68rem; color:var(--muted); margin-top:2px; font-weight:700;
  text-transform:uppercase; letter-spacing:.04em;
}

/* filter pills */
.pl-pills{ display:flex; gap:8px; margin-bottom:10px; }
.pl-pill{
  font:inherit; font-size:.8rem; font-weight:600; padding:6px 14px;
  border-radius:999px; border:1px solid var(--line); background:var(--surface);
  color:var(--muted); cursor:pointer;
}
.pl-pill.on{ background:var(--primary); border-color:var(--primary); color:#fff; }

/* table */
.pl-tablewrap{
  overflow-x:auto; border:1px solid var(--line); border-radius:12px;
  background:var(--surface);
}
.pl-table{ width:100%; border-collapse:collapse; font-size:.84rem; }
.pl-table th,.pl-table td{
  padding:9px 12px; text-align:right; white-space:nowrap;
  font-variant-numeric:tabular-nums;
}
.pl-table th.l,.pl-table td.l{ text-align:left; }
.pl-table thead th{
  background:rgba(99,102,241,.08); color:var(--muted); font-size:.68rem;
  font-weight:700; text-transform:uppercase; letter-spacing:.04em;
  border-bottom:1px solid var(--line);
}
.pl-table tbody tr{ border-bottom:1px solid var(--line); }
.pl-table tbody tr:last-child{ border-bottom:0; }
.pl-table tr.pl-short td{ background:rgba(220,38,38,.045); }
.pl-table tr.pl-short td.l{ box-shadow:inset 3px 0 0 #dc2626; }
.pl-table tr.pl-group td{
  background:rgba(99,102,241,.10); font-size:.8rem; text-align:left;
  color:var(--text); padding:7px 12px;
}
.pl-group-req{ color:#dc2626; font-weight:700; }
.pl-shade{ font-weight:700; font-family:"JetBrains Mono",ui-monospace,monospace; }
.pl-yd{ display:block; font-size:.68rem; color:var(--muted); }
.pl-req{ color:#dc2626; font-weight:800; }
.pl-ok{ color:var(--muted); }
.pl-nolink{
  font-size:.66rem; color:#b45309; background:rgba(180,83,9,.12);
  padding:1px 6px; border-radius:6px; font-weight:600;
}
.pl-cov{ display:flex; align-items:center; gap:7px; justify-content:flex-end; }
.pl-bar{
  width:74px; height:7px; border-radius:5px; background:var(--line);
  overflow:hidden; flex:0 0 auto;
}
.pl-bar i{ display:block; height:100%; border-radius:5px; }
.pl-empty{
  padding:28px 12px; text-align:center; color:var(--muted); font-size:.85rem;
}

@media (max-width:760px){
  .pl-tiles{ grid-template-columns:repeat(2,1fr); }
  .pl-controls{ width:100%; }
  .pl-select{ flex:1; }
}
/* ════════ end Planning module ══════════════════════════════════════ */

