:root {
  --bg:        #0b0d10;
  --bg1:       #0f1217;
  --bg2:       #14181e;
  --bg3:       #1a1f26;
  --bg4:       #232932;
  --brd:       #232a32;
  --brd2:      #2d353e;
  --teal:      #2eb8a4;
  --teal2:     #239585;
  --teal3:     #1a6f63;
  --tealdim:   rgba(46,184,164,.09);
  --tealglw:   rgba(46,184,164,.15);
  --grn:       #4cb87f;
  --grndim:    rgba(76,184,127,.1);
  --red:       #e8607a;
  --reddim:    rgba(232,96,122,.1);
  --yel:       #d6a93f;
  --yeldim:    rgba(214,169,63,.1);
  /* v2.21.362: --gold was consumed ~40x (nav "pushes" tags, admin nav, warn borders)
     but never defined — every use silently fell back to inherited color. */
  --gold:      #e0a23a;
  --blu:       #5096db;
  --bludim:    rgba(80,150,219,.1);
  --pur:       #9485d8;
  --purdim:    rgba(148,133,216,.1);
  --txt:       #e6edf3;
  --txt2:      #9aa4af;
  --txt3:      #7d8894; /* v2.21.362: was #616c77 (~3.3:1 on bg2, failed contrast at 9-10px); now ~4.9:1 */
  --txt4:      #313a43;
  /* v2.21.435 — Customize needs these reachable, or a tenant theme can't stay readable.
     --ontl  = foreground ON the accent fill (.btn-t etc). Was hard-coded near-black, which
               is invisible the moment a tenant picks a dark primary.
     --sbtxt* = SIDEBAR ink, defaulted to the page ink. Split out because the sidebar has its
               own background knob (--bg1): a tenant can pick a light sidebar on a dark page
               (or the reverse) and each side has to stay legible on its own. */
  --ontl:      #020a09;
  --ontl2:     #020a09;   /* .btn-t:hover fills from --teal2, a DIFFERENT colour to --teal */
  --sbtxt:     #e6edf3;
  --sbtxt2:    #9aa4af;
  --sbtxt3:    #7d8894;
  --fd:        -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, system-ui, sans-serif;
  --fm:        'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, monospace;
  --fb:        -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, system-ui, sans-serif;
}

*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }

/* v2.20.0: Customize color-swatch inputs */
input.cz-sw { height: 34px; padding: 2px 4px; cursor: pointer; }

/* v2.20.7: widgets (composable pages) — host-scoped widget chrome + 12-col grid */
.ll-grid { display: grid; grid-template-columns: repeat(12, 1fr); gap: 12px; }
.ll-grid-cell { min-width: 0; }
.ll-w { background: var(--bg2); border: 1px solid var(--brd); border-radius: 8px; overflow: hidden; display: flex; flex-direction: column; }
.ll-w-head { padding: 8px 12px; font-weight: 600; font-size: 12px; color: var(--txt2); border-bottom: 1px solid var(--brd); background: var(--bg1); }
.ll-w-body { padding: 8px 12px; overflow: auto; max-height: 440px; }
.ll-w-empty { padding: 18px; text-align: center; color: var(--txt3); font-size: 12px; }
.ll-kpi-v { font-family: var(--fd); font-size: 30px; color: var(--teal); line-height: 1; }
.ll-kpi-l { font-size: 11px; color: var(--txt3); margin-top: 4px; text-transform: uppercase; letter-spacing: .04em; }
/* v2.21.32: stack the widget grid on tablets / small windows (≤1024px) — side-by-side
   real tabs are cramped below a laptop width; desktops (>1024px) keep the multi-column layout. */
@media (max-width: 1024px) { .ll-grid { grid-template-columns: 1fr; } .ll-grid-cell { grid-column: 1 / -1 !important; } }

/* v2.20.1: collapsible admin modules — click a module header to minimize its body */
#admin-content .tw.collapsed > :not(.th) { display: none !important; }
#admin-content .tw > .th { cursor: pointer; user-select: none; }
#admin-content .tw > .th:hover .tw-chev { color: var(--teal); }
.tw-chev { margin-left: 8px; color: var(--txt3); font-size: 11px; transition: color .15s; }

html { overflow-x: hidden; }  /* v2.9.2: defensive — ensure no horizontal page scroll regardless of child widths */
body {
  background: var(--bg);
  color: var(--txt);
  font-family: var(--fb);
  /* v2.20.0: text-size knob — tenant Customize sets --ll-font-scale (default 1). */
  font-size: calc(13px * var(--ll-font-scale, 1));
  min-height: 100vh;
  overflow-x: hidden;
}

/* ── SIDEBAR ── */
.sb {
  width: 220px;
  background: var(--bg1);
  border-right: 1px solid var(--brd);
  position: fixed; top: 0; left: 0; height: 100vh;
  display: flex; flex-direction: column; z-index: 100;
  overflow-y: auto;
  /* v2.9.3: animate slide-out for collapsible behavior */
  transition: transform 0.25s ease;
}
/* v2.9.3: hide sidebar (off-canvas) when .collapsed is added */
.sb.collapsed { transform: translateX(-100%); }
.main.expanded { margin-left: 0 !important; max-width: 100vw !important; }

/* v2.9.3: Hamburger button — always visible, top-left, toggles sidebar */
.mobile-hamburger {
  position: fixed; top: 14px; left: 14px;
  width: 36px; height: 36px;
  background: var(--bg2);
  border: 1px solid var(--brd2);
  border-radius: 6px;
  cursor: pointer;
  z-index: 200;
  display: flex;  /* v2.9.4: always visible so user can hide sidebar to free up space */
  align-items: center; justify-content: center;
  color: var(--teal);
  font-size: 18px;
  transition: background 0.15s, border-color 0.15s, opacity 0.15s;
  /* slight transparency when sidebar is open so it doesn't block the logo */
  opacity: 0.55;
}
.mobile-hamburger:hover { background: var(--bg3); border-color: var(--teal); opacity: 1; }
.mobile-hamburger.always-visible { display: flex; opacity: 1; }
.sb.collapsed ~ * .mobile-hamburger,
.mobile-hamburger.always-visible { opacity: 1; }

/* v2.9.3: Backdrop dim when sidebar open on mobile (off-canvas mode) */
.mobile-overlay {
  position: fixed; inset: 0;
  background: rgba(0,0,0,0.5);
  z-index: 90;
  display: none;
  opacity: 0;
  transition: opacity 0.2s;
}
.mobile-overlay.show { display: block; opacity: 1; }

/* v2.9.3: At narrow viewports, sidebar starts collapsed and content takes full width */
@media (max-width: 900px) {
  .sb { transform: translateX(-100%); }                 /* hidden by default */
  .sb.open { transform: translateX(0); }                /* shown when toggled */
  .main { margin-left: 0 !important; max-width: 100vw !important; padding-left: 60px; }
  .mobile-hamburger { display: flex; }                  /* always visible on mobile */
  .ph { padding-left: 0; }
  .ph-r { gap: 6px; }
  .pt { font-size: 19px !important; letter-spacing: .2px !important; }
}

.sb-logo {
  padding: 22px 18px 18px;
  border-bottom: 1px solid var(--brd);
  position: relative;
}
.sb-logo::after {
  content: '';
  position: absolute; bottom: 0; left: 0; right: 0; height: 1px;
  background: var(--brd);
}
.logo-mark {
  display: flex; align-items: center; gap: 10px; margin-bottom: 4px;
}
.logo-dot {
  width: 8px; height: 8px; border-radius: 50%;
  background: var(--teal);
}
@keyframes pulse { 0%,100%{opacity:1;transform:scale(1)} 50%{opacity:.7;transform:scale(.9)} }
.logo-text { font-family: var(--fd); font-size: 18px; font-weight: 700; letter-spacing: .3px; color: var(--sbtxt); line-height: 1; }
.logo-text span { color: var(--teal); }
.logo-sub { font-size: 9px; letter-spacing: 3px; color: var(--sbtxt3); text-transform: uppercase; }

.sb-nav { padding: 10px 8px; flex: 1; }
.nav-sec { font-size: 10px; letter-spacing: .6px; color: var(--sbtxt3); text-transform: uppercase; padding: 14px 10px 5px; }
/* v2.12.6: collapsible nav sections — click .nav-sec to toggle .nav-sec-items below */
.nav-sec.clickable { cursor: pointer; user-select: none; display: flex; align-items: center; gap: 6px; transition: color 0.15s; }
.nav-sec.clickable:hover { color: var(--sbtxt2); }
.nav-sec-chevron { display: inline-block; font-size: 8px; line-height: 1; transition: transform 0.15s ease; opacity: 0.6; }
.nav-sec.collapsed .nav-sec-chevron { transform: rotate(-90deg); }
.nav-sec.collapsed + .nav-sec-items { display: none; }
.nav-sec-items { display: block; }

.ni {
  display: flex; align-items: center; gap: 9px;
  padding: 8px 10px; border-radius: 6px;
  cursor: pointer; color: var(--sbtxt2);
  font-size: 12px; font-weight: 500;
  transition: all .15s; margin-bottom: 1px;
  border: 1px solid transparent;
  position: relative;
}
.ni:hover { background: var(--bg3); color: var(--sbtxt); }
.ni.on {
  background: var(--tealdim);
  color: var(--teal);
  border-color: rgba(0,229,192,.2);
}
.ni.on::before {
  content: '';
  position: absolute; left: 0; top: 50%; transform: translateY(-50%);
  width: 2px; height: 60%; background: var(--teal);
  border-radius: 0 2px 2px 0;
}
/* Clean, typography-led nav — drop the per-row decorative glyphs (the strongest "generated" tell). */
.ni-ic { display: none; }
.ni-badge {
  margin-left: auto; background: var(--teal3);
  color: var(--teal); font-family: var(--fm); font-size: 10px;
  padding: 1px 5px; border-radius: 3px;
}
/* v2.16.46: low-friction sidebar pulse. Apply .ni-badge-pulse to a badge to
   surface threshold-crossed state (TCG drift > 100, Set Lots bucket ≥ 90%). */
@keyframes ni-badge-pulse {
  0%,100% { box-shadow: 0 0 0 0 rgba(0,229,192,0.6); }
  50%     { box-shadow: 0 0 0 4px rgba(0,229,192,0); }
}
.ni-badge-pulse {
  animation: ni-badge-pulse 1.8s ease-in-out infinite;
  background: var(--teal); color: var(--bg); font-weight: 700;
}

/* v2.21.466: notification bell — red count badge + action-item panel. */
.notif-badge-red {
  position: absolute; top: -6px; right: -6px;
  background: var(--red); color: #fff; font-family: var(--fm);
  font-size: 9px; font-weight: 700; padding: 1px 4px; min-width: 15px;
  border-radius: 8px; text-align: center; line-height: 1.4;
}
#notif-bell:hover { border-color: var(--teal); color: var(--txt); }
/* v2.21.470: FIXED-position dropdown (escapes the 220px sidebar that was squishing labels
   into vertical text), fixed width, its own scroll. */
#notif-panel {
  position: fixed; top: 60px; left: 14px; width: 330px; max-width: calc(100vw - 28px); z-index: 9998;
  background: var(--bg2); border: 1px solid var(--brd); border-radius: 12px;
  box-shadow: 0 12px 40px rgba(0,0,0,0.45); padding: 6px; max-height: 70vh; overflow-y: auto;
}
#notif-panel .notif-head {
  display: flex; align-items: center; justify-content: space-between;
  padding: 8px 10px 6px; font-size: 11px; color: var(--txt3); text-transform: uppercase; letter-spacing: .4px;
}
#notif-panel .notif-head .notif-clear { cursor: pointer; color: var(--txt3); text-transform: none; letter-spacing: 0; }
#notif-panel .notif-head .notif-clear:hover { color: var(--teal); }
.notif-item {
  display: flex; align-items: center; gap: 10px; width: 100%;
  padding: 9px 10px; border: none; background: none; border-radius: 8px;
  cursor: pointer; text-align: left; color: var(--txt); font-size: 12px; line-height: 1.35;
}
.notif-item:hover { background: var(--bg3); }
.notif-item .notif-n {
  font-family: var(--fm); font-weight: 700; font-size: 15px; color: var(--red);
  min-width: 40px; text-align: right; flex-shrink: 0;
}
.notif-item .notif-n.notif-n-info { color: var(--teal); font-size: 13px; }
.notif-item .notif-lbl { flex: 1; min-width: 0; }
.notif-item .notif-x {
  color: var(--txt3); font-size: 15px; line-height: 1; padding: 2px 6px; border-radius: 6px; flex-shrink: 0;
}
.notif-item .notif-x:hover { color: var(--red); background: var(--bg1); }
.notif-empty { padding: 20px 10px; text-align: center; color: var(--txt3); font-size: 12px; }

.sb-status {
  padding: 12px 14px; border-top: 1px solid var(--brd);
}
.status-row { display: flex; align-items: center; gap: 8px; margin-bottom: 6px; }
.status-dot {
  width: 6px; height: 6px; border-radius: 50%;
  background: var(--grn);
  box-shadow: 0 0 6px var(--grn);
}
.status-dot.warn { background: var(--yel); box-shadow: 0 0 6px var(--yel); }
.status-dot.off { background: var(--txt3); box-shadow: none; }
.status-label { font-size: 11px; color: var(--txt2); }
.status-label span { color: var(--grn); font-family: var(--fm); font-size: 10px; }

/* ── MAIN ── */
.main {
  margin-left: 220px;
  flex: 1;
  padding: 24px 28px;
  /* v2.8: prevent flex blowout — without this, wide children can force the
     main column wider than its share, sliding content under the fixed sidebar */
  min-width: 0;
  max-width: calc(100vw - 220px);
}
.page { display: none; }
.page.on { display: block; }

/* ── PAGE HEADER ── */
.ph { display: flex; align-items: flex-end; justify-content: space-between; margin-bottom: 22px; flex-wrap: wrap; gap: 12px; }
.pt { font-family: var(--fd); font-size: 21px; font-weight: 650; letter-spacing: .2px; line-height: 1.15; }
.pt em { color: inherit; font-style: normal; font-weight: inherit; }
.ps { font-size: 12px; color: var(--txt2); letter-spacing: 0; text-transform: none; margin-top: 4px; }
.ph-r { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }

/* v2.9: Sync status pill — shows last-sync time + clickable to trigger sync now */
.sync-pill {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 4px 10px; border-radius: 12px;
  background: var(--bg3); border: 1px solid var(--brd);
  font-size: 11px; color: var(--txt2);
  cursor: pointer; user-select: none;
  transition: background .15s, border-color .15s;
}
.sync-pill:hover { background: var(--bg2); border-color: var(--brd2); }
.sync-pill.fresh { border-color: rgba(0,232,122,.3); }
.sync-pill.fresh .sync-dot { background: var(--grn); box-shadow: 0 0 4px var(--grn); }
.sync-pill.stale { border-color: rgba(255,196,0,.3); }
.sync-pill.stale .sync-dot { background: var(--yel); box-shadow: 0 0 4px var(--yel); }
.sync-pill.old { border-color: rgba(255,69,102,.3); }
.sync-pill.old .sync-dot { background: var(--red); box-shadow: 0 0 4px var(--red); }
.sync-pill.syncing { border-color: rgba(0,229,192,.5); }
.sync-pill.syncing .sync-dot {
  background: var(--teal); box-shadow: 0 0 4px var(--teal);
  animation: sync-pulse 1s ease-in-out infinite;
}
@keyframes sync-pulse { 0%,100%{opacity:1;transform:scale(1)} 50%{opacity:.5;transform:scale(.8)} }
.sync-dot { width: 6px; height: 6px; border-radius: 50%; background: var(--txt3); flex-shrink: 0; }
.sync-pill.flash { animation: sync-flash 1s ease-out; }
@keyframes sync-flash {
  0% { background: var(--grndim); border-color: var(--grn); }
  100% { background: var(--bg3); }
}

/* ── KPI CARDS ── */
.kg { display: grid; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); gap: 12px; margin-bottom: 20px; }
.kpi {
  background: var(--bg2);
  border: 1px solid var(--brd);
  border-radius: 8px;
  padding: 16px;
  position: relative; overflow: hidden;
  transition: border-color .2s, transform .15s;
}
.kpi:hover { border-color: var(--brd2); transform: translateY(-1px); }
.kpi::before { content: ''; position: absolute; top: 0; left: 0; right: 0; height: 2px; background: var(--teal3); }
.kpi.t::before { background: var(--teal); box-shadow: 0 0 8px var(--teal); }
.kpi.g::before { background: var(--grn); }
.kpi.r::before { background: var(--red); }
.kpi.y::before { background: var(--yel); }
.kpi.b::before { background: var(--blu); }
.kpi.p::before { background: var(--pur); }
.kpi-corner {
  position: absolute; top: 8px; right: 8px;
  font-size: 18px; opacity: .15;
}
.kl { font-size: 10px; letter-spacing: 1.5px; color: var(--txt3); text-transform: uppercase; margin-bottom: 8px; }
.kv {
  font-family: var(--fm); font-size: 20px; font-weight: 600;
  color: var(--txt); line-height: 1;
  /* v2.8: long values like "$12,345.67" don't burst out of narrow KPI columns */
  word-break: break-word;
  overflow-wrap: anywhere;
}
.kv.t { color: var(--teal); }
.kv.g { color: var(--grn); }
.kv.r { color: var(--red); }
.kv.y { color: var(--yel); }
.ks { font-size: 11px; color: var(--txt3); margin-top: 5px; }

/* ── TABLES ── */
.tw {
  background: var(--bg2);
  border: 1px solid var(--brd);
  border-radius: 8px;
  /* v2.8: was `overflow: hidden` which clipped wide tables.
     Allow horizontal scroll on overflow but keep vertical clipped so
     rounded corners stay clean. Inner divs with overflow-y:auto still work. */
  overflow-x: auto;
  overflow-y: hidden;
  margin-bottom: 18px;
}
.th {
  display: flex; align-items: center; justify-content: space-between;
  padding: 12px 16px; border-bottom: 1px solid var(--brd);
  flex-wrap: wrap; gap: 8px;
  background: var(--bg3);
}
.tt { font-weight: 600; font-size: 13px; display: flex; align-items: center; gap: 8px; }
.tt-dot { width: 6px; height: 6px; border-radius: 50%; background: var(--teal); box-shadow: 0 0 6px var(--teal); flex-shrink: 0; }

/* v2.9.90: title-expand mode for mobile (and desktop on demand).
   When body has .title-expand, cells/divs with .expand-target class drop
   their ellipsis truncation and wrap to multiple lines. Applied to the
   four matching pages: Similar New, Exact New, Pick List, Approval Queue.
   Other pages keep ellipsis (info density preserved on desktop).

   Strategy: CSS class on body + marker class on target cells.
   No per-render JS overhead — CSS handles everything.
   max-width:none overrides the inline `max-width:240px` etc. on the cells. */

/* v2.9.96: REAL fix for the mobile-toggle-off-screen issue.
   Root cause: flex items default to `min-width: auto` (= their min-content).
   The inner button div in .th has 7 buttons with combined min-content ~700px;
   on a 400px viewport, the inner div CANNOT shrink below 700px, forcing .th
   (and therefore the surrounding .tw via the overflow:auto containment) to
   grow wider than viewport. Result: rightmost buttons (notably the
   title-expand toggle) sit off-screen and the user has to horizontally
   scroll the table area to reach them.

   Fix: explicitly allow the inner button container to shrink below its
   min-content width. flex-wrap:wrap (already set on each page's inner div)
   then takes effect and the buttons wrap onto multiple rows naturally.

   Targets `.th > div` only — preserves .tt (title span) natural width so
   the title doesn't get unexpectedly truncated. Applies uniformly to all
   four pages with the toggle (Exact, Similar, Queue, Pick List). */
.th > div { min-width: 0; }

.title-expand-toggle { flex-shrink: 0; }

body.title-expand .expand-target {
  white-space: normal !important;
  overflow: visible !important;
  text-overflow: clip !important;
  max-width: none !important;
  word-break: break-word;
}
/* Re-add a sensible max-width on mobile so titles wrap inside the card
   instead of expanding the whole row. */
@media (max-width: 768px) {
  body.title-expand .expand-target {
    max-width: 60vw !important;
  }
}

table { width: 100%; border-collapse: collapse; }
thead th {
  background: var(--bg3);
  padding: 8px 14px; text-align: left;
  font-size: 10px; letter-spacing: 1px; text-transform: uppercase;
  color: var(--txt3); font-weight: 500;
  border-bottom: 1px solid var(--brd);
  white-space: nowrap; cursor: pointer;
  transition: color .15s;
  /* v2.20.9: lock the header row (incl. select-all checkboxes) while scrolling.
     Tables inside max-height/overflow-y:auto wrappers get a pinned header; tables
     in non-scrolling wrappers are unaffected (sticky is inert there). */
  position: sticky; top: 0; z-index: 2;
}
thead th:hover { color: var(--teal); }
tbody tr { border-bottom: 1px solid var(--brd); transition: background .1s; }
tbody tr:last-child { border-bottom: none; }
tbody tr:hover { background: rgba(0,229,192,.03); }
td { padding: 8px 14px; font-size: 12px; color: var(--txt2); }
td.em { color: var(--txt); font-weight: 500; }
td.mn { font-family: var(--fm); font-size: 12px; }
/* v2.21.516 (Sam, on the live Profit page: "doesn't match properly with lines"). The Profit
   tables' money headers are `text-align:right` but `td.mn` sets FONT ONLY and never alignment,
   so every figure sat left of its own header. Pre-existing; the platform filter just made people
   read that table closely.
   ⚠ SCOPED TO THE PAGE ON PURPOSE. `.mn` is shared by 226 cells across the whole app — a global
   `td.mn { text-align:right }` would silently re-align every table in Leumas. Style scope is an
   interface (the v2.21.508 bulk-window lesson, in reverse).
   ⚠ Keyed to #page-profit, which travels WITH the node: phase 7's Money hub borrows the real
   #page-profit element into a tab, so the id — and this rule — go with it. */
#page-profit td.mn { text-align: right; }

/* ── BADGES ── */
.bge {
  display: inline-flex; align-items: center;
  padding: 2px 7px; border-radius: 4px;
  font-size: 11px; font-weight: 600;
  font-family: var(--fm); letter-spacing: .5px;
}
.bg-t { background: var(--tealdim); color: var(--teal); border: 1px solid rgba(0,229,192,.2); }
.bg-g { background: var(--grndim); color: var(--grn); border: 1px solid rgba(0,232,122,.2); }
.bg-r { background: var(--reddim); color: var(--red); border: 1px solid rgba(255,69,102,.2); }
.bg-y { background: var(--yeldim); color: var(--yel); border: 1px solid rgba(255,200,48,.2); }
.bg-p { background: var(--purdim); color: var(--pur); border: 1px solid rgba(167,139,250,.2); }
.bg-x { background: rgba(61,90,100,.25); color: var(--txt3); border: 1px solid var(--brd); }

/* ── BUTTONS ── */
.btn {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 8px 14px; border-radius: 6px;
  font-family: var(--fb); font-size: 12px; font-weight: 600;
  cursor: pointer; border: none; transition: all .15s;
  white-space: nowrap; letter-spacing: .3px;
}
.btn-t { background: var(--teal); color: var(--ontl); }
.btn-t:hover { background: var(--teal2); color: var(--ontl2); }
.btn-o { background: transparent; color: var(--txt2); border: 1px solid var(--brd2); }
.btn-o:hover { border-color: var(--teal); color: var(--teal); }
.btn-g { background: var(--grndim); color: var(--grn); border: 1px solid rgba(0,232,122,.25); }
.btn-g:hover { background: rgba(0,232,122,.18); }
.btn-r { background: var(--reddim); color: var(--red); border: 1px solid rgba(255,69,102,.25); }
.btn-sm { padding: 5px 10px; font-size: 11px; }
.btn-xs { padding: 3px 8px; font-size: 11px; }
/* v2.21.362: gold = "this writes to a LIVE marketplace (eBay/TCGplayer)" — matches the
   gold "pushes" nav tags. Reads stay teal (.btn-t); destructive stays red (.btn-r). */
.btn-live { background: rgba(224,162,58,.08); color: var(--gold); border: 1px solid rgba(224,162,58,.45); }
.btn-live:hover { background: rgba(224,162,58,.18); border-color: var(--gold); }

/* ── INPUTS ── */
.si {
  background: var(--bg3); border: 1px solid var(--brd2);
  border-radius: 6px; padding: 7px 12px;
  color: var(--txt); font-family: var(--fb); font-size: 12px;
  outline: none; transition: border-color .15s;
}
.si:focus { border-color: var(--teal); box-shadow: 0 0 0 2px var(--tealdim); }
.si::placeholder { color: var(--txt3); }
select.si option { background: var(--bg3); }

/* ── CHART ── */
.cw { background: var(--bg2); border: 1px solid var(--brd); border-radius: 8px; padding: 18px; margin-bottom: 18px; }
.ct { font-weight: 600; font-size: 13px; margin-bottom: 16px; display: flex; align-items: center; gap: 8px; }
.ct::before { content: ''; width: 3px; height: 16px; background: var(--teal); border-radius: 2px; box-shadow: 0 0 8px var(--teal); }
.bc { display: flex; align-items: flex-end; gap: 8px; height: 140px; padding-bottom: 24px; border-bottom: 1px solid var(--brd); }
.bg2c { flex: 1; display: flex; flex-direction: column; align-items: center; height: 100%; justify-content: flex-end; }
.bbs { display: flex; gap: 2px; align-items: flex-end; width: 100%; }
.bar { border-radius: 3px 3px 0 0; flex: 1; min-width: 6px; cursor: pointer; transition: opacity .2s, filter .2s; }
.bar:hover { opacity: .8; filter: brightness(1.2); }
.bl { font-size: 10px; color: var(--txt3); margin-top: 5px; font-family: var(--fm); }
.cl { display: flex; gap: 16px; margin-top: 10px; flex-wrap: wrap; }
.cli { display: flex; align-items: center; gap: 6px; font-size: 10px; color: var(--txt2); }
.cld { width: 8px; height: 8px; border-radius: 2px; }

/* ── PL HERO ── */
.pl-hero {
  background: var(--bg2);
  border: 1px solid var(--brd);
  border-radius: 8px;
  padding: 24px 28px;
  margin-bottom: 20px;
  display: flex; align-items: center; justify-content: space-between;
  gap: 20px; flex-wrap: wrap;
  position: relative; overflow: hidden;
}
.pl-hero::before, .pl-hero::after { display: none; }
.plh-label { font-size: 10px; letter-spacing: .8px; text-transform: uppercase; color: var(--txt3); margin-bottom: 8px; }
.plh-val {
  font-family: var(--fm); font-size: 38px; letter-spacing: 0; line-height: 1.05;
  /* v2.9.2: prevent very large dollar values from forcing the page wider
     than the viewport (which slid Singles/Master content under the sidebar) */
  overflow-wrap: anywhere;
  word-break: break-word;
  min-width: 0;
}
.pl-hero > div { min-width: 0; }      /* let flex children shrink */
.kg > * { min-width: 0; }              /* let kpi cards shrink */
/* v2.21.528 (phase 8, flag dash_kpi_trim): group headings inside the KPI grid. Scoped to a CLASS,
   never to #d-kpis — the moment a second strip adopts this vocabulary an id-scoped block would
   leave it completely unstyled while every text assertion still passed (the .508 bulk-window bug).
   grid-column:1/-1 makes the heading its own full-width row inside the existing auto-fit grid, so
   the tile columns are untouched and mobile's 2-col / 1-col overrides keep working unchanged. */
.kg-h { grid-column: 1 / -1; display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap;
        margin: 6px 0 -2px; padding-bottom: 6px; border-bottom: 1px solid var(--brd); }
.kg-h:first-child { margin-top: 0; }
.kg-h-t { font-size: 11px; font-weight: 600; letter-spacing: .6px; text-transform: uppercase; color: var(--txt2); }
.kg-h-v { font-size: 13px; font-weight: 600; color: var(--red); }
.kg-h-s { font-size: 11px; color: var(--txt3); }
.plh-val.pos { color: var(--grn); }
.plh-val.neg { color: var(--red); }
.plh-sub { font-size: 10px; color: var(--txt3); margin-top: 6px; font-family: var(--fm); }
.plh-stats { display: flex; gap: 24px; flex-wrap: wrap; }
.plh-stat .v { font-family: var(--fm); font-size: 16px; font-weight: 600; }
.plh-stat .l { font-size: 10px; color: var(--txt3); letter-spacing: 1.5px; text-transform: uppercase; margin-top: 3px; }

/* ── SHARING MODE (v2.18.6) ──
   One toggle (body.sharing-mode) blurs every sensitive value so a tenant can
   screen-share / livestream without exposing money, P&L, order amounts,
   expenses or buyer names. Card titles, labels and structure stay readable so
   they can still talk through their workflow. Blur is unreadable at this size
   and reverses instantly when toggled off. .mn covers table money/qty/dates AND
   the buyer column; .kv covers KPI values; .ks covers KPI subtitles (which embed
   $ fees/shipping/ad-spend/refunds — v2.19.2); .plh-* covers the P&L hero. */
body.sharing-mode .kv,
body.sharing-mode .ks,
body.sharing-mode .mn,
body.sharing-mode .plh-sub,
body.sharing-mode .plh-stat .v,
body.sharing-mode [data-sensitive] {
  filter: blur(9px) !important;
  -webkit-filter: blur(9px) !important;
  user-select: none !important;
  cursor: default !important;
}
/* The P&L hero headline is ~52px — needs much heavier blur to be unreadable
   even on a paused stream frame. Blur scales with font size, so small text is
   fully obscured at 9px but the headline needs ~24px. */
/* v2.21.518: #yoy-final is the same shape — a ~60px headline figure. It carries [data-sensitive]
   (so the generic rule above already catches it), but 9px of blur is readable at that size, so it
   joins the headline tier. Adding it here rather than giving it .plh-val because renderYOY sets
   className on that element and would wipe the class. */
body.sharing-mode .plh-val,
body.sharing-mode #yoy-final {
  filter: blur(24px) !important;
  -webkit-filter: blur(24px) !important;
  user-select: none !important;
  cursor: default !important;
}

/* ── MOBILE MODE (v2.21.252) ──
   A user-toggled phone layout for on-the-go picking. Keyed on body.mobile-mode
   (NOT the existing .mobile-* off-canvas tokens) so it applies at ANY viewport —
   an admin can test it on desktop. Additive over the #48 responsive @media pass.
   Flag-gated: the class is only ever applied by _applyMobileMode when
   featureOn('mobile_mode') is true, so with no flag row every rule below is inert. */

/* Fixed bottom nav bar — hidden until mobile mode is active. */
#ll-botnav { display: none; }
body.mobile-mode #ll-botnav {
  display: flex;
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 9000;
  background: #10141c; border-top: 1px solid var(--brd, #2a3441);
  box-shadow: 0 -2px 12px rgba(0,0,0,.5);
  padding: 4px 2px calc(4px + env(safe-area-inset-bottom, 0px));
}
#ll-botnav .ll-bnav-item {
  flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 3px; background: none; border: none; color: var(--txt3, #8b98a9);
  font-size: 10px; padding: 6px 2px; min-height: 54px; cursor: pointer; border-radius: 8px;
}
#ll-botnav .ll-bnav-ic { font-size: 19px; line-height: 1; }
#ll-botnav .ll-bnav-item.active { color: var(--teal, #00e5c0); }
#ll-botnav .ll-bnav-item:active { background: rgba(255,255,255,.06); }

/* Layout: sidebar off-canvas, single centered column, room for the bottom bar. */
body.mobile-mode .sb { transform: translateX(-100%); }
body.mobile-mode .sb.open { transform: translateX(0); }
body.mobile-mode .mobile-hamburger { display: none !important; }   /* replaced by the bottom "More" button */
body.mobile-mode .mobile-overlay.show { display: block; }
body.mobile-mode .main {
  margin: 0 auto !important; max-width: 480px !important; width: 100%;
  padding: 16px 12px calc(74px + env(safe-area-inset-bottom, 0px)) !important;
}
body.mobile-mode .ph { flex-direction: column; align-items: flex-start; gap: 8px; }
body.mobile-mode .ph-r { flex-wrap: wrap; }
body.mobile-mode .kg { grid-template-columns: repeat(2, 1fr) !important; }
body.mobile-mode .cw, body.mobile-mode .tw { overflow-x: auto; -webkit-overflow-scrolling: touch; }

/* Pick list → stacked cards (the hero picking screen). CSS-only reflow over the
   existing pull-sheet markup; every checkbox keeps its data-act="wMarkPicked", so
   there is no render fork. */
body.mobile-mode #page-picklist table { display: block; }
body.mobile-mode #page-picklist thead { display: none; }
body.mobile-mode #pick-tbody { display: block; }
body.mobile-mode #pick-tbody tr {
  display: flex; flex-wrap: wrap; align-items: center; gap: 4px 8px;
  border: 1px solid #ddd !important; border-radius: 10px;
  padding: 10px !important; margin: 0 0 8px !important;
  background: #fff !important;   /* keep the render's inline opacity (0.5 = already picked) as the "grabbed" cue */
}
body.mobile-mode #pick-tbody td {
  display: inline-block; width: auto !important; max-width: 100%;
  border: none !important; padding: 0 !important; font-size: 13px; white-space: normal;
}
body.mobile-mode #pick-tbody td img { max-width: 46px; height: auto; border-radius: 4px; }
body.mobile-mode #pick-tbody td:last-child { margin-left: auto; }
body.mobile-mode #pick-tbody input[type=checkbox] { width: 26px !important; height: 26px !important; }
body.mobile-mode #pick-tbody tr:has(> td[colspan]) { background: #f1f5f9 !important; font-weight: 700; }

/* v2.21.260: Card Matching → tappable cards on mobile so you can match on the go (photos + big
   Match/Dismiss targets). CSS-only reflow over the existing table markup — no render fork. */
body.mobile-mode #page-card-match table { display: block; }
body.mobile-mode #page-card-match thead { display: none; }
body.mobile-mode #cardmatch-list { display: block; }
body.mobile-mode #cardmatch-list tr {
  display: flex; flex-wrap: wrap; align-items: center; gap: 6px 12px;
  border: 1px solid var(--brd) !important; border-radius: 10px;
  padding: 10px !important; margin: 0 0 8px !important;
}
body.mobile-mode #cardmatch-list td {
  display: inline-block; width: auto !important; max-width: 100%;
  border: none !important; padding: 0 !important; white-space: normal;
}
body.mobile-mode #cardmatch-list td:first-child { flex: 1 1 100%; }
body.mobile-mode #cardmatch-list td.mn { font-size: 14px; color: var(--txt2); }
body.mobile-mode #cardmatch-list .cm-thumb { width: 56px !important; height: 76px !important; }
body.mobile-mode #cardmatch-list td:last-child { flex: 1 1 100%; display: flex; gap: 10px; }
body.mobile-mode #cardmatch-list td:last-child .btn { flex: 1; padding: 12px !important; font-size: 14px !important; min-height: 44px; }

/* Match modal → full-width sheet, taller scroll, bigger tap rows + photos on mobile. */
body.mobile-mode #invbind-modal .modal { max-width: 96vw !important; width: 96vw; }
body.mobile-mode #invbind-results { max-height: 58vh !important; }
body.mobile-mode #invbind-results > div { padding: 12px 8px !important; }
body.mobile-mode #invbind-results img,
body.mobile-mode #invbind-existing img { width: 48px !important; height: 66px !important; }

/* ── LIVE INDICATOR ── */
.live-pill {
  display: inline-flex; align-items: center; gap: 5px;
  background: var(--grndim); border: 1px solid rgba(0,232,122,.25);
  border-radius: 20px; padding: 3px 10px;
  font-size: 10px; color: var(--grn); font-family: var(--fm);
}
.live-dot { width: 5px; height: 5px; border-radius: 50%; background: var(--grn); box-shadow: 0 0 6px var(--grn); animation: pulse 1.5s ease-in-out infinite; }
.pending-pill {
  display: inline-flex; align-items: center; gap: 5px;
  background: var(--yeldim); border: 1px solid rgba(255,200,48,.25);
  border-radius: 20px; padding: 3px 10px;
  font-size: 10px; color: var(--yel); font-family: var(--fm);
}

/* ── MODAL ── */
.modal-bg { position: fixed; inset: 0; background: rgba(0,0,0,.8); z-index: 500; display: flex; align-items: center; justify-content: center; padding: 20px; backdrop-filter: blur(4px); }
.modal { background: var(--bg2); border: 1px solid var(--brd2); border-radius: 10px; padding: 26px; width: 100%; max-width: 540px; max-height: 90vh; overflow-y: auto; position: relative; }
.modal::before { display: none; }
.modal h2 { font-family: var(--fd); font-size: 17px; font-weight: 650; letter-spacing: .2px; color: var(--txt); margin-bottom: 16px; }
.modal-hidden { display: none; }
.fg { display: flex; flex-direction: column; gap: 5px; margin-bottom: 12px; }
.fg label { font-size: 11px; letter-spacing: 1px; text-transform: uppercase; color: var(--txt3); }
.fg-row { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 10px; }

/* ── ASSIGN MODAL SKU LIST ── */
.asgn-list-wrap { max-height: 280px; overflow-y: auto; background: var(--bg3); border: 1px solid var(--brd); border-radius: 6px; }
.asgn-row { display: flex; align-items: center; gap: 10px; padding: 7px 12px; border-bottom: 1px solid var(--brd); cursor: pointer; transition: background .1s; }
.asgn-row:last-child { border-bottom: none; }
.asgn-row:hover { background: var(--tealdim); }
.asgn-row.sel { background: var(--tealdim); }
.asgn-chk { font-size: 14px; flex-shrink: 0; color: var(--teal); }
.asgn-sku { font-family: var(--fm); font-size: 11px; color: var(--pur); min-width: 130px; flex-shrink: 0; }
.asgn-title { font-size: 11px; color: var(--txt2); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; flex: 1; }

/* ── CHILD PANEL ── */
.child-panel { background: var(--bg3); border: 1px solid var(--brd); border-radius: 6px; margin-top: 4px; display: none; }
.child-panel.open { display: block; }
.child-hdr { padding: 7px 12px; border-bottom: 1px solid var(--brd); display: flex; justify-content: space-between; align-items: center; }
.child-hdr-txt { font-size: 10px; letter-spacing: 1.5px; text-transform: uppercase; color: var(--txt3); }
.child-list { max-height: 220px; overflow-y: auto; }
.child-item { display: flex; align-items: center; gap: 10px; padding: 6px 12px; border-bottom: 1px solid var(--brd); }
.child-item:last-child { border-bottom: none; }
.child-item input[type=checkbox] { accent-color: var(--teal); width: 13px; height: 13px; cursor: pointer; flex-shrink: 0; }
.child-sku { font-family: var(--fm); font-size: 10px; color: var(--pur); min-width: 150px; }
.child-status { font-size: 10px; color: var(--txt3); margin-left: auto; }
.child-status.pulled { color: var(--grn); }
.picked-ct { font-family: var(--fm); font-size: 10px; color: var(--txt3); }

/* ── PAGINATION ── */
.pag { display: flex; align-items: center; justify-content: space-between; padding: 10px 14px; border-top: 1px solid var(--brd); }
.pag-info { font-family: var(--fm); font-size: 11px; color: var(--txt3); }
.pag-btns { display: flex; gap: 6px; }

/* ── DROP ZONE ── */
.dz { border: 2px dashed var(--brd2); border-radius: 8px; padding: 24px; text-align: center; cursor: pointer; transition: all .15s; }
.dz:hover, .dz.dv { border-color: var(--teal); background: var(--tealdim); }
.dz-icon { font-size: 24px; margin-bottom: 6px; }
.dz-txt { font-size: 11px; color: var(--txt3); }
.dz-txt strong { color: var(--teal); }

/* ── AGE COLORS ── */
.age-warn td { color: var(--yel)!important; }
.age-warn td.em { color: var(--yel)!important; }
.age-old td { color: var(--red)!important; }
.age-old td.em { color: var(--red)!important; }
.age-badge { display: inline-block; font-size: 10px; padding: 1px 5px; border-radius: 3px; margin-left: 4px; font-family: var(--fm); }
.age-warn-b { background: var(--yeldim); color: var(--yel); }
.age-old-b { background: var(--reddim); color: var(--red); }

/* ── LOT CARD ── retired v2.21.360 (Card Lots is a table of rows now; .lc/.lot-num/.lot-meta/.lot-mi/.lprog/.lpb removed) */

/* ── EXPENSE TYPE CHIP ── */
.exp-chip { display: inline-flex; align-items: center; gap: 6px; background: var(--bg3); border: 1px solid var(--brd2); border-radius: 20px; padding: 4px 10px; font-size: 11px; margin: 3px; cursor: pointer; transition: all .15s; }
.exp-chip:hover { border-color: var(--teal); }
.exp-chip .del { color: var(--txt3); font-size: 10px; }
.exp-chip .del:hover { color: var(--red); }

/* ── SELL MODAL INFO ── */
.sell-info { background: var(--bg3); border: 1px solid var(--brd); border-radius: 6px; padding: 12px; margin-bottom: 14px; font-size: 11px; color: var(--txt2); }

/* ── ORDER LOT BADGE ── */
.ord-lot { display: flex; align-items: center; gap: 4px; }

/* ── SEARCH PAGE ── */
.sr-hero { max-width: 640px; margin: 0 auto 28px; text-align: center; }
.sr-title { font-family: var(--fd); font-size: 24px; font-weight: 650; letter-spacing: .2px; margin-bottom: 10px; }
.sr-title em { color: inherit; font-style: normal; }

/* ── YEAR TABS ── */
.ytabs { display: inline-flex; gap: 2px; background: var(--bg3); border: 1px solid var(--brd); border-radius: 7px; padding: 3px; margin-bottom: 16px; }
.ytab { padding: 6px 16px; border-radius: 5px; font-size: 12px; font-weight: 600; cursor: pointer; color: var(--txt3); transition: all .15s; }
.ytab.on { background: var(--tealdim); color: var(--teal); border: 1px solid rgba(0,229,192,.2); }

/* ── COMBINE TABS ── */
.ctabs { display: flex; gap: 8px; margin-bottom: 18px; flex-wrap: wrap; }
.ctab { padding: 7px 16px; border-radius: 6px; font-size: 12px; font-weight: 600; cursor: pointer; color: var(--txt3); background: var(--bg3); border: 1px solid var(--brd); transition: all .15s; }
.ctab.on { background: var(--tealdim); color: var(--teal); border-color: rgba(0,229,192,.25); }
.ctab-panel { display: none; }
.ctab-panel.on { display: block; }

/* ── IMPORT PAGE ── */
.imp-grid { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 16px; margin-bottom: 20px; }
.ic { background: var(--bg2); border: 1px solid var(--brd); border-radius: 8px; padding: 20px; }
.ic h3 { font-size: 13px; font-weight: 600; margin-bottom: 5px; }
.ic p { font-size: 11px; color: var(--txt3); margin-bottom: 12px; line-height: 1.6; }
.col-map { margin-top: 8px; font-size: 10px; color: var(--txt3); line-height: 1.9; }
.col-map code { background: var(--bg4); padding: 1px 5px; border-radius: 3px; font-family: var(--fm); font-size: 9px; color: var(--blu); }

/* ── API STATUS CARD ── */
.api-card {
  background: var(--bg2); border: 1px solid var(--brd); border-radius: 8px;
  padding: 20px; margin-bottom: 16px;
  display: flex; align-items: center; gap: 16px;
}
.api-icon { font-size: 28px; }
.api-name { font-weight: 600; font-size: 14px; margin-bottom: 4px; }
.api-desc { font-size: 11px; color: var(--txt3); }

/* ── MASTER DASH ── */
.two-col { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 16px; margin-bottom: 16px; }
.sec-hdr { font-size: 10px; letter-spacing: 1.5px; text-transform: uppercase; font-weight: 600; margin-bottom: 10px; padding-bottom: 6px; border-bottom: 1px solid var(--brd); display: flex; align-items: center; gap: 6px; }
.sec-hdr::before { content: ''; width: 3px; height: 12px; border-radius: 2px; }
.sec-hdr.t::before { background: var(--teal); }
.sec-hdr.g::before { background: var(--grn); }
.sec-hdr.p::before { background: var(--pur); }

/* ── CSP refactor (v2.17.9): row-hover backgrounds — replace inline onmouseover/onmouseout ── */
.hover-bg2:hover { background: var(--bg2); }
.hover-bg4:hover { background: var(--bg4); }

/* ── TOAST ── */
.toast { position: fixed; bottom: 20px; right: 20px; background: var(--bg3); border: 1px solid var(--brd2); border-radius: 8px; padding: 11px 16px; font-size: 13px; z-index: 1000; transform: translateY(60px); opacity: 0; transition: all .3s; display: flex; align-items: center; gap: 8px; max-width: 480px; }
.toast.show { transform: translateY(0); opacity: 1; }
.toast.s { border-color: rgba(0,232,122,.4); color: var(--grn); }
.toast.e { border-color: rgba(255,69,102,.4); color: var(--red); }

/* ── MISC ── */
.row { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
.empty { text-align: center; padding: 48px 20px; color: var(--txt3); }
.empty-ic { font-size: 36px; margin-bottom: 10px; opacity: .3; }
.empty h3 { color: var(--txt2); font-size: 14px; margin-bottom: 4px; }

/* v2.12.0: diagnostic output containers in the Diagnostics tab.
   Each diagnostic button renders results into one of these. Compact code-style
   formatting with color cues for verdict status. */
.diag-out {
  padding: 12px 14px;
  background: var(--bg3);
  border: 1px solid var(--brd);
  border-radius: 6px;
  font-family: var(--fm);
  font-size: 11px;
  line-height: 1.6;
  margin-top: 4px;
}
.diag-out.diag-ok { background: rgba(0, 232, 122, 0.05); border-color: var(--grn); }
.diag-out.diag-warn { background: rgba(255, 209, 102, 0.05); border-color: var(--gold); }
.diag-out.diag-err { background: rgba(239, 68, 68, 0.05); border-color: var(--red); }
.diag-out h4 { font-size: 12px; margin-bottom: 6px; font-family: var(--fb); }
.diag-out .diag-stat { display: flex; gap: 8px; font-family: var(--fm); font-size: 11px; }
.diag-out .diag-stat-key { color: var(--txt3); }
.diag-out .diag-stat-val { color: var(--txt1); font-weight: 600; }
.diag-out table { width: 100%; margin-top: 8px; font-size: 10px; }
.diag-out th, .diag-out td { padding: 4px 6px; text-align: left; border-bottom: 1px solid var(--brd); }
.diag-out th { color: var(--txt2); font-weight: 600; font-family: var(--fb); }
.gap { margin-bottom: 18px; }
details summary { cursor: pointer; user-select: none; font-size: 11px; color: var(--txt3); padding: 6px 0; list-style: none; }
details summary::before { content: '▸ '; font-size: 10px; }
details[open] summary::before { content: '▾ '; }
/* v2.21.39: larger, visible scrollbars (Sam) — bigger track + a teal thumb that
   reads against the dark bg. Applies everywhere; Firefox via scrollbar-* below. */
::-webkit-scrollbar { width: 14px; height: 14px; }
::-webkit-scrollbar-track { background: rgba(255,255,255,0.05); border-radius: 7px; }
::-webkit-scrollbar-thumb { background: var(--teal); border-radius: 7px; border: 3px solid var(--bg); background-clip: padding-box; }
::-webkit-scrollbar-thumb:hover { background: var(--teal2, var(--teal)); border: 2px solid var(--bg); background-clip: padding-box; }
::-webkit-scrollbar-corner { background: transparent; }
* { scrollbar-width: auto; scrollbar-color: var(--teal) rgba(255,255,255,0.05); }
/* v2.21.42 (#2): hover popup of top-5 scan candidates over the review scan photo */
/* v2.21.43: open the popup OVERLAPPING the image (left:78 < 84px image) so there's NO dead gap for
   the cursor to cross — fixes "popup disappears / hard to reach". Cap to the viewport so it stays
   within the window; it scrolls if tall. */
.scan-scanwrap .scan-cand-pop { display: none; position: absolute; top: 0; left: 78px; z-index: 30; width: 320px; max-width: min(340px, 84vw); max-height: 60vh; overflow: auto; background: var(--bg2); border: 1px solid var(--brd2); border-radius: 8px; padding: 8px; box-shadow: 0 8px 24px rgba(0,0,0,.55); }
.scan-scanwrap:hover .scan-cand-pop { display: block; }
textarea.si { resize: vertical; min-height: 80px; }
/* v2.21.43: Import-data modal — roomier, grouped source tiles (was a cramped 720px box) */
.imp-grp { font-size: 11px; letter-spacing: .5px; text-transform: uppercase; color: var(--txt3); margin: 16px 0 8px; }
.imp-grp:first-child { margin-top: 0; }
.imp-tiles { display: grid; grid-template-columns: repeat(auto-fill, minmax(225px, 1fr)); gap: 10px; }
.imp-tile { text-align: left; padding: 12px 14px; border: 1px solid var(--brd2); border-radius: 10px; background: var(--bg1); cursor: pointer; transition: border-color .12s, background .12s; display: flex; flex-direction: column; gap: 3px; }
.imp-tile:hover { border-color: var(--teal); background: var(--bg2); }
.imp-tile.on { border-color: var(--teal); box-shadow: inset 0 0 0 1px var(--teal); }
.imp-tile b { font-size: 13px; }
.imp-tile .d { font-size: 11px; color: var(--txt3); line-height: 1.3; }
.imp-opt { display: flex; align-items: flex-start; gap: 8px; font-size: 12px; color: var(--txt2); cursor: pointer; padding: 4px 0; }
.imp-opt input { margin-top: 2px; flex: 0 0 auto; }
input[type=checkbox] { accent-color: var(--teal); }

/* ═══ Vending (card-show) module — mobile-first. v2.17.39 ═══ */
.vend-dash { display: grid; grid-template-columns: repeat(4, 1fr); gap: 10px; margin-bottom: 14px; }
.vend-tile { background: var(--bg2); border: 1px solid var(--brd); border-radius: 8px; padding: 12px; }
.vend-tile-l { font-size: 10px; color: var(--txt3); text-transform: uppercase; letter-spacing: 1px; margin-bottom: 4px; }
.vend-tile-v { font-size: 20px; font-weight: 600; }
.vend-actions { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 14px; }
.vend-exports { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 14px; }
.vend-filters { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }
.vend-result { display: flex; gap: 10px; align-items: center; padding: 9px 6px; border-bottom: 1px solid var(--brd); cursor: pointer; }
.vend-result-body { display: flex; flex-direction: column; gap: 2px; }
.vend-card { display: flex; justify-content: space-between; align-items: center; gap: 10px; background: var(--bg2); border: 1px solid var(--brd); border-radius: 8px; padding: 12px; margin-bottom: 8px; }
.vend-card.vend-sold { opacity: 0.6; }
.vend-card-main { min-width: 0; flex: 1; }
.vend-card-title { font-size: 13px; font-weight: 500; color: var(--txt1); overflow: hidden; text-overflow: ellipsis; }
.vend-card-meta { font-size: 11px; color: var(--txt3); font-family: var(--fm); margin-top: 2px; }
.vend-children { font-size: 10px; color: var(--txt3); margin-top: 3px; }
.vend-card-actions { display: flex; gap: 6px; flex-shrink: 0; }
.vend-event-card { display: flex; justify-content: space-between; align-items: center; gap: 10px; background: var(--bg2); border: 1px solid var(--brd); border-radius: 8px; padding: 14px; margin-bottom: 8px; cursor: pointer; }
.vend-event-card:hover { border-color: var(--teal); }

/* Mobile: stack dashboard tiles 2-up, make Sold/Trade tap targets big */
@media (max-width: 720px) {
  .vend-dash { grid-template-columns: repeat(2, 1fr); }
  .vend-actions .btn, .vend-exports .btn { flex: 1 1 44%; justify-content: center; }
  .vend-card-actions .btn { padding: 9px 14px; font-size: 13px; }
  .vend-filters .si { flex: 1 1 100%; width: 100% !important; }
}

/* ═══ What's New home (v2.17.41) ═══ */
.wn-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 14px; }
.wn-box { background: var(--bg2); border: 1px solid var(--brd); border-radius: 8px; padding: 16px; }
.wn-box-h { font-size: 14px; font-weight: 600; margin-bottom: 10px; }
.wn-box-body { font-size: 12px; color: var(--txt2); line-height: 1.6; }
@media (max-width: 900px) { .wn-grid { grid-template-columns: 1fr; } }

/* v2.19.11: global eBay-op lock visual cue. While any eBay API op runs, dim + disable
   every push/sync/list trigger. The JS mutex in the dispatcher is the real guard; this
   just makes the blocked state obvious (pointer-events:none is belt-and-suspenders). */
body.ebay-op-busy [data-act="syncAllFromEbay"],
body.ebay-op-busy [data-act="syncOrdersNow"],
body.ebay-op-busy [data-act="syncFinances"],
body.ebay-op-busy [data-act="pushApprovedToEbay"],
body.ebay-op-busy [data-act="endPendingChildren"],
body.ebay-op-busy [data-act="approveManualCombine"],
body.ebay-op-busy [data-act="listAllImports"],
body.ebay-op-busy [data-act="listSelectedImports"],
body.ebay-op-busy [data-act="listSingleImport"],
body.ebay-op-busy [data-act="listSelectedPreCombines"] {
  opacity: 0.45 !important;
  pointer-events: none !important;
  cursor: not-allowed !important;
}

/* v2.19.40: import-stepper "click me" pulse on the Push button (combine step done → push). */
@keyframes ll-strobe-green {
  0%, 100% { box-shadow: 0 0 0 0 rgba(34,197,94,0.0); background: var(--grn, #22c55e); }
  50%      { box-shadow: 0 0 14px 4px rgba(34,197,94,0.85); background: #16a34a; }
}
.ll-strobe {
  animation: ll-strobe-green 1s ease-in-out infinite;
  color: #fff !important;
  border-color: var(--grn, #22c55e) !important;
}

/* ════════════════════════════════════════════════════════════════════════
   #48 RESPONSIVE PASS (2026-06-18) — laptop / tablet / phone usability.
   PURELY ADDITIVE: every rule is inside a max-width media query, so the
   desktop layout (above each breakpoint) is byte-for-byte unchanged.
   Builds on the existing breakpoints (1024 widget-grid, 900 sidebar+hamburger,
   768 title-expand, 720 vending). No DOM/class/handler changes.
   ════════════════════════════════════════════════════════════════════════ */

/* ── Make EVERY data table survive narrow widths ──
   .tw already scrolls (overflow-x:auto) and most big tables live in .tw or an
   explicit overflow-x:auto inner div. This is a belt-and-suspenders net: any
   bare <table> sitting directly in a card scrolls horizontally instead of
   forcing the page wide. -webkit-overflow-scrolling gives momentum scroll on
   iOS. Only applies at/below tablet width so desktop tables are untouched. */
@media (max-width: 1024px) {
  .tw, .cw { -webkit-overflow-scrolling: touch; }
  /* a card whose direct child is a wide table: let the card scroll it */
  .cw > table, .tw > table { min-width: max-content; }
  /* explicit inner scroll wrappers (overflow-x:auto divs) keep momentum too */
  [style*="overflow-x:auto"], [style*="overflow-x: auto"] { -webkit-overflow-scrolling: touch; }
}

/* ── TABLET (≤900px): tighten main padding once the sidebar is off-canvas,
   stack the two-column form/import grids so they don't overflow. ── */
@media (max-width: 900px) {
  .main { padding: 18px 14px 18px 56px; }   /* leave room for the hamburger (left:~12px) */
  .ph { margin-bottom: 16px; }
  /* two-column layouts → single column so inputs/cards never overflow */
  .imp-grid, .two-col { grid-template-columns: 1fr; }
  .imp-tiles { grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); }
}

/* ── PHONE (≤640px): comfortable tap targets, stacked grids, slimmer chrome ── */
@media (max-width: 640px) {
  .main { padding: 14px 10px 14px 50px; }
  .pt { font-size: 18px !important; }

  /* Tap targets — eBay-seller often works one-handed on a phone.
     min-height ~40px without changing the visual style on desktop. */
  .btn { min-height: 40px; padding: 9px 14px; }
  .si  { min-height: 40px; }
  /* inline-edit cells / small selects in tables stay compact (they're not .btn/.si) */

  /* KPI tiles: 2-up on a phone instead of demanding 160px each (which forces
     1-up + lots of scroll). 2 columns reads far better at a glance. */
  .kg { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px; margin-bottom: 14px; }
  .ll-kpi-v { font-size: 22px; }   /* widget KPI tiles shrink to fit 2-up */

  /* Stacked form rows + 2-col grids inside modals */
  .fg-row { grid-template-columns: 1fr; }

  /* Modal: less padding, fill more of the screen on a phone */
  .modal-bg { padding: 10px; }
  .modal { padding: 18px; max-height: 92vh; }
  .modal h2 { font-size: 16px; }

  /* Combine/section tabs + filter rows wrap with a bit more breathing room */
  .ctabs { gap: 6px; }

  /* Charts: a touch shorter so a phone screen isn't all chart */
  .bc { height: 120px; }
}

/* ── VERY NARROW (≤400px): drop KPI tiles to 1-up so values aren't squeezed ── */
@media (max-width: 400px) {
  .kg { grid-template-columns: 1fr; }
  .imp-tiles { grid-template-columns: 1fr; }
}


/* ═══ scan review v2 (2026-07-04) — quad-corner condition view, confidence bands,
   high-value alert, keyboard cursor, Fix-modal variant strip ═══ */
/* #1 Quad-corner tiles: lazy <img> crops (verifier fix 2026-07-04 — background-image loads
   EAGERLY, so 500-card batches pulled every raw scan at render; loading=lazy imgs fetch near the
   viewport, 4 tiles share one src = one request). The img is 300% of the tile (3× zoom), anchored
   per corner via sc-* so each tile frames one physical corner. NO canvas (cross-origin taint). */
.scan-corners { display: grid; grid-template-columns: repeat(2, 72px); grid-auto-rows: 72px; gap: 4px; }
.scan-corner { background-color: var(--bg3); border: 1px solid var(--brd2); border-radius: 6px; cursor: zoom-in; position: relative; overflow: hidden; transition: transform .12s ease; }
/* width:300% + height:auto keeps the card's aspect ratio so the tiles reach the TRUE top/bottom
   corners. The old height:300%+object-fit:cover squashed a portrait (~0.74) scan into a square tile,
   center-cropping the card's top & bottom away → the "corner" tiles never showed the real corners. */
.scan-corner img { position: absolute; width: 300%; height: auto; max-width: none; pointer-events: none; }
.scan-corner img.sc-tl { left: 0; top: 0; }
.scan-corner img.sc-tr { right: 0; top: 0; }
.scan-corner img.sc-bl { left: 0; bottom: 0; }
.scan-corner img.sc-br { right: 0; bottom: 0; }
.scan-corner:hover { transform: scale(2.1); z-index: 26; border-color: var(--teal); box-shadow: 0 6px 18px rgba(0,0,0,.6); }

/* ── Custom crop templates (v2.21.345) ──
   Review tiles: one lazy <img> per region, framed by inline width/height/left/top percentages
   (see js _scanCropStyle). Both dims are set, so the img FILLS the tile — never object-fit:cover,
   which is exactly what silently cropped the card's top/bottom out of the corner tiles. */
.scan-crops { display: flex; flex-wrap: wrap; gap: 6px; justify-content: center; max-width: 172px; }
.scan-crop-cell { width: 80px; }
/* min-height keeps a very wide/thin region (e.g. a full-width bottom-edge strip) from collapsing to an
   unreadable sliver. It stretches such a crop vertically — the region shown is still exactly right. */
.scan-crop { position: relative; overflow: hidden; width: 80px; min-height: 26px; background: var(--bg3); border: 1px solid var(--brd2); border-radius: 6px; cursor: zoom-in; transition: transform .12s ease; }
.scan-crop img { position: absolute; max-width: none; pointer-events: none; }
.scan-crop:hover { transform: scale(2.1); z-index: 26; border-color: var(--teal); box-shadow: 0 6px 18px rgba(0,0,0,.6); }
.scan-crop-cap { font-size: 8px; line-height: 1.3; color: var(--txt3); text-align: center; margin-top: 2px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

/* Editor stage: the stage box is exactly the reference image box, so a pointer position as a % of
   the stage IS the same % of the image — that's what makes the stored percentages resolution-free. */
.scan-crop-stage { position: relative; display: inline-block; line-height: 0; background: var(--bg3); border-radius: 8px; touch-action: none; user-select: none; }
.scan-crop-stage img { display: block; max-width: 100%; max-height: 520px; width: auto; height: auto; border-radius: 8px; }
.scan-crop-layer { position: absolute; inset: 0; }
.scan-crop-rect { position: absolute; border: 1.5px solid var(--teal); background: rgba(20,184,166,.14); cursor: move; box-sizing: border-box; }
.scan-crop-rect.sel { border-color: var(--amber, #e0a23a); background: rgba(224,162,58,.18); z-index: 2; }
.scan-crop-rect-tag { position: absolute; top: -1px; left: -1px; font-size: 9px; line-height: 1.4; padding: 0 4px; background: var(--teal); color: #04201d; border-radius: 3px 0 3px 0; pointer-events: none; white-space: nowrap; max-width: 100%; overflow: hidden; }
.scan-crop-rect.sel .scan-crop-rect-tag { background: var(--amber, #e0a23a); color: #201804; }
.scan-crop-h { position: absolute; width: 11px; height: 11px; background: #fff; border: 1.5px solid var(--teal); border-radius: 2px; }
.scan-crop-rect.sel .scan-crop-h { border-color: var(--amber, #e0a23a); }
.scan-crop-h-nw { left: -6px; top: -6px; cursor: nwse-resize; }
.scan-crop-h-ne { right: -6px; top: -6px; cursor: nesw-resize; }
.scan-crop-h-sw { left: -6px; bottom: -6px; cursor: nesw-resize; }
.scan-crop-h-se { right: -6px; bottom: -6px; cursor: nwse-resize; }
.scan-crop-row { border: 1px solid var(--brd2); border-radius: 8px; padding: 8px; margin-bottom: 8px; cursor: pointer; }
.scan-crop-row.sel { border-color: var(--teal); background: var(--bg1); }
/* #3 Confidence-band chips — thresholds mirror the matcher's accept ceilings (js: _scanConfBand) */
.scan-conf { font-size: 11px; font-weight: 700; border-radius: 4px; padding: 1px 7px; white-space: nowrap; }
.scan-conf-g { color: var(--grn); border: 1px solid var(--grn); }
.scan-conf-o { color: var(--yel); border: 1px solid var(--yel); }
.scan-conf-r { color: var(--red); border: 1px solid var(--red); }
/* #5 Keyboard cursor — the current card's outline */
.scan-panel.scan-cur { outline: 2px solid var(--teal); outline-offset: 2px; }
.scan-kbd-hint { font-size: 11px; color: var(--txt3); border: 1px dashed var(--brd2); border-radius: 4px; padding: 2px 7px; cursor: help; white-space: nowrap; }
/* #4 High-value alert — gold badge + subtle panel glow (box-shadow so it coexists with the
   selection border) */
.scan-panel.scan-hv { box-shadow: 0 0 0 1px #c9a13b, 0 0 12px rgba(224, 162, 58, .15); }
.scan-hv-badge { font-size: 10px; font-weight: 700; color: #e0a23a; border: 1px solid #c9a13b; border-radius: 4px; padding: 1px 6px; white-space: nowrap; }
/* v2.21.388 — per-card price lock (excluded from auto-pricing) */
.scan-lock-badge { font-size: 10px; font-weight: 700; color: #e0a23a; border: 1px solid #c9a13b; border-radius: 4px; padding: 1px 6px; white-space: nowrap; background: rgba(224,162,58,.10); }
.btn.scan-lock-on { border-color: #c9a13b !important; color: #e0a23a !important; }
/* #7 Fix-modal one-click printing strip */
.scan-var-strip { display: flex; gap: 6px; flex-wrap: wrap; }
.scan-var-chip { font-size: 11px; border: 1px solid var(--brd2); border-radius: 12px; padding: 3px 10px; cursor: pointer; color: var(--txt2); background: var(--bg1); }
.scan-var-chip:hover { border-color: var(--teal); color: var(--txt); }
.scan-var-chip.on { border-color: var(--teal); color: var(--teal); font-weight: 700; }
/* ── Phase 2 (2026-07-04): destinations bar · pre-export ⚠ chip · listing-settings strip ── */
.scan-dest-bar { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-top: 10px; padding-top: 8px; border-top: 1px dashed var(--brd2); }
.scan-dest-lbl { font-size: 10px; letter-spacing: .5px; text-transform: uppercase; color: var(--txt3); white-space: nowrap; }
.scan-dest-seg { font-size: 12px; border: 1px solid var(--brd2); background: var(--bg1); color: var(--txt2); border-radius: 6px; padding: 5px 11px; cursor: pointer; white-space: nowrap; }
.scan-dest-seg small { font-size: 9px; color: var(--txt3); text-transform: uppercase; letter-spacing: .4px; }
.scan-dest-seg:hover { border-color: var(--teal); color: var(--txt); }
.scan-dest-seg.live { border-color: var(--teal); color: var(--teal); font-weight: 600; }
.scan-dest-seg.live small { color: var(--teal); opacity: .75; }
.scan-dest-selonly { font-size: 11px; color: var(--txt2); white-space: nowrap; }
.scan-issues-chip { font-size: 11px; font-weight: 700; color: var(--yel); border: 1px solid var(--yel); background: transparent; border-radius: 12px; padding: 2px 9px; cursor: pointer; white-space: nowrap; }
.scan-issues-chip:hover { background: rgba(224, 162, 58, .12); }
.scan-settings-strip { margin-top: 8px; border: 1px solid var(--brd2); border-radius: 8px; padding: 8px 12px; background: var(--bg1); display: flex; flex-direction: column; gap: 4px; }
.scan-set-row { display: flex; align-items: center; gap: 10px; font-size: 12px; flex-wrap: wrap; }
.scan-set-lbl { width: 110px; flex: 0 0 auto; font-size: 10px; letter-spacing: .5px; text-transform: uppercase; color: var(--txt3); }
.scan-set-val { flex: 1; min-width: 200px; color: var(--txt2); }

/* v2.21.386 TEAM ACCESS — fixed top banner while working inside another account.
   body.delegate-mode pads the page down so the banner never covers the header. */
/* v2.21.519: 36px was a hard-coded ONE-LINE banner height. The permissions list wraps to 2-3 lines
   on a narrow window, so the fixed banner grew past this compensation and covered the page tabs —
   Sam #1. The offset is now MEASURED by _syncDelegateOffset into --delegate-h; the 36px literal
   survives only as the fallback for the instant before the first measurement lands. */
body.delegate-mode { padding-top: var(--delegate-h, 36px); }
body.delegate-mode #sync-bar { top: var(--delegate-h, 36px); }

/* ═══════════════ v2.21.387 — Scan & Identify: mobile reflow of the batch-edit/review screen ═══════════════
   The per-card review panels + toolbar were pure desktop density (inline-styled 4-column flex with a
   non-shrinking photo column + a 300px fields floor → horizontal scroll on a phone; ~13px checkboxes
   + ~28px buttons below the touch floor; a sticky toolbar that walls a short viewport). This block adds
   the responsive layer scan never had. It fires ONLY ≤640px, so desktop is byte-identical; the panel
   markup only gained additive class hooks (no visual change while these rules are inactive). Display /
   layout only — never touches matching, pricing, or listing logic. `!important` is required because the
   load-bearing widths live as inline styles (same reason the mobile-mode reflows above use it). */
@media (max-width: 640px) {
  #page-scan .scan-panel { padding: 12px !important; }
  /* stack the 4 panel columns */
  #page-scan .scan-pbody { flex-direction: column !important; gap: 12px !important; }
  /* photos: own row, wrap the crop grid below scan+match instead of forcing a ~400px non-shrinking column */
  #page-scan .scan-pmedia { flex: 0 0 auto !important; width: 100% !important; flex-wrap: wrap !important; justify-content: flex-start !important; }
  /* kill the 300px fields floor + the fixed 240px variant block */
  #page-scan .scan-pfields { min-width: 0 !important; width: 100% !important; }
  #page-scan .scan-pvariant { flex: 1 1 100% !important; }
  /* corners + condition side-by-side, wrap, full usable width (drop the 160/240 min/max pins) */
  #page-scan .scan-pside { align-self: stretch !important; flex-direction: row !important; flex-wrap: wrap !important; align-items: flex-start !important; }
  #page-scan .scan-pcond { min-width: 0 !important; max-width: none !important; flex: 1 1 200px !important; }
  /* action buttons: from a tall thin column to a tappable 2-up row */
  #page-scan .scan-pactions { align-self: stretch !important; flex-direction: row !important; flex-wrap: wrap !important; }
  #page-scan .scan-pactions .btn { flex: 1 1 44% !important; }
  /* touch targets — the select checkbox drives the whole bulk workflow; buttons + inputs to ≥40px */
  #page-scan .scan-selbox { width: 22px !important; height: 22px !important; flex: 0 0 auto !important; }
  #page-scan .scan-panel .btn-sm, #page-scan .scan-panel .btn { min-height: 40px !important; }
  #page-scan .scan-panel .si { min-height: 38px !important; }
  /* toolbar: don't let the ~13-control sticky wall eat the viewport; surface the batch actions full-width */
  #page-scan .scan-toolbar { position: static !important; }
  #page-scan .scan-toolactions { margin-left: 0 !important; width: 100% !important; }
  #page-scan .scan-toolactions .btn { flex: 1 1 44% !important; min-height: 44px !important; }
  #page-scan .scan-toolrow .btn-sm, #page-scan .scan-toolrow .si { min-height: 40px !important; }
  #page-scan .scan-kbd-hint { display: none !important; }   /* no keyboard shortcuts on a phone */
  /* bulk-edit dropdown can't spill off the right edge */
  #page-scan #scan-bulk-menu { max-width: calc(100vw - 24px) !important; }
  /* destinations bar: full-width tappable segments (these fire REAL eBay listings — no mis-taps) */
  #page-scan .scan-dest-seg { flex: 1 1 100% !important; min-height: 44px !important; white-space: normal !important; }
  #page-scan .scan-dest-bar { gap: 6px !important; }
}

/* ═══════════════════════════════════════════════════════════════════════════
   v2.21.413 NAV V3 — photo-tile navigation (flag `nav_photo_tiles`, admin dogfood).
   When the flag is ON, body carries .nav-v3: the sidebar shows ONLY the big
   section headings; clicking a heading opens a photo-tile panel of that
   section's pages (tiles derived from the live nav DOM, so every existing
   admin/flag/tcg/delegate gate is inherited). Every rule is scoped under
   body.nav-v3, or nav3-* classes only renderNavSec emits → zero effect while the flag is off.
   ═══════════════════════════════════════════════════════════════════════════ */
body.nav-v3 .sb-nav .nav-sec-items { display: none !important; }
body.nav-v3 .sb-nav .nav-sec {
  font-size: 17px; font-weight: 800; letter-spacing: .3px; text-transform: none;
  color: var(--txt); padding: 13px 12px; margin: 2px 4px; border-radius: 9px; line-height: 1.2;
}
body.nav-v3 .sb-nav .nav-sec.clickable:hover { background: var(--bg2); color: var(--teal); }
body.nav-v3 .sb-nav .nav-sec-chevron { display: none; }
/* the two unsectioned top items (Back, What's New) keep working, styled to match */
body.nav-v3 .sb-nav > .ni { font-size: 15px; font-weight: 700; padding: 11px 12px; margin: 2px 4px; border-radius: 9px; }

/* ── section tile PAGE (#page-navsec — v2.21.414, replaces the .413 popup) ─ */
.nav3-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(230px, 1fr)); gap: 20px 18px; align-items: start; }
.nav3-tile { cursor: pointer; }
.nav3-thumb {
  position: relative; aspect-ratio: 16 / 10; border-radius: 10px; overflow: hidden;
  border: 1px solid var(--brd2); background: #10151c; transition: border-color .12s, transform .12s;
}
.nav3-thumb img { width: 100%; height: 100%; object-fit: cover; object-position: top; display: block; }
.nav3-tile:hover .nav3-thumb { border-color: var(--teal); transform: translateY(-2px); }
.nav3-tile-title { font-size: 14px; font-weight: 700; color: var(--txt); margin-top: 8px; text-align: center; }
.nav3-tile:hover .nav3-tile-title { color: var(--teal); }
.nav3-pushes {
  position: absolute; top: 6px; right: 6px; font-size: 9px; letter-spacing: .4px;
  text-transform: uppercase; color: var(--gold); background: rgba(0,0,0,.6);
  padding: 2px 6px; border-radius: 5px; pointer-events: none;
}
/* sub-page strip under a parent tile ("Inside this page") */
.nav3-sub-label { font-size: 10px; text-transform: uppercase; letter-spacing: .5px; color: var(--txt3); margin-top: 10px; }
.nav3-subrow { display: flex; gap: 10px; flex-wrap: wrap; margin-top: 6px; }
.nav3-sub-tile { width: 104px; }
.nav3-sub-tile .nav3-tile-title { font-size: 11px; font-weight: 600; margin-top: 4px; }
/* the all-sections site-map view (no section chosen) */
.navsec-sec { margin-bottom: 36px; }
.navsec-sec-title { font-size: 13px; font-weight: 800; text-transform: uppercase; letter-spacing: .7px; color: var(--txt3); margin: 0 0 12px; }

@media (max-width: 640px) {
  .nav3-grid { grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 14px 12px; }
  .nav3-sub-tile { width: 88px; }
}

/* ═══════════════════════════════════════════════════════════════════════════
   v2.21.416 — LOT BACKFILL per-card assign view (admin tab; base theme).
   Row-card layout: photo · title/meta · price · assign controls. Not flag-
   gated (the whole Lot Backfill tab is admin-only), but it inherits the ui-v4
   variables automatically when that flag is on.
   ═══════════════════════════════════════════════════════════════════════════ */
.lbc-row { display: flex; align-items: center; gap: 14px; padding: 14px 16px; border-bottom: 1px solid var(--brd); }
.lbc-row:last-child { border-bottom: none; }
.lbc-row:hover { background: rgba(46,184,164,.03); }
.lbc-img { width: 56px; height: 78px; border-radius: 6px; border: 1px solid var(--brd); object-fit: cover; flex-shrink: 0; background: var(--bg3); }
.lbc-noimg { display: flex; align-items: center; justify-content: center; font-size: 9px; color: var(--txt3); text-align: center; }
.lbc-main { flex: 1; min-width: 0; }
.lbc-title { font-size: 13px; font-weight: 600; color: var(--txt); line-height: 1.4; margin-bottom: 4px; }
.lbc-meta { font-size: 11px; color: var(--txt3); display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.lbc-sku { font-family: var(--fm); color: var(--teal); background: var(--bg3); border: 1px solid var(--brd2); padding: 0 6px; border-radius: 4px; font-size: 10px; }
.lbc-right { text-align: right; flex-shrink: 0; min-width: 88px; }
.lbc-price-lab { font-size: 9px; letter-spacing: 1.4px; text-transform: uppercase; color: var(--txt3); }
.lbc-price { font-family: var(--fm); font-size: 16px; font-weight: 700; color: var(--yel); }
.lbc-acts { display: flex; flex-direction: column; gap: 6px; align-items: stretch; flex-shrink: 0; width: 150px; }
.lbc-lot { width: 100%; font-size: 11px; }
@media (max-width: 700px) {
  .lbc-row { flex-wrap: wrap; }
  .lbc-acts { width: 100%; flex-direction: row; }
  .lbc-acts .btn { flex: 1; justify-content: center; }
}

/* ── v2.21.416: sidebar feature editor (navcfg modal) ── */
.navcfg-sec { border: 1px solid var(--brd); border-radius: 8px; margin-bottom: 10px; overflow: hidden; }
.navcfg-head { display: flex; align-items: center; gap: 8px; padding: 8px 10px; background: var(--bg3); }
.navcfg-sec-label { font-weight: 700; font-size: 11px; letter-spacing: .8px; text-transform: uppercase; color: var(--txt2); }
.navcfg-item { display: flex; align-items: center; gap: 8px; padding: 6px 10px 6px 24px; border-top: 1px solid var(--brd); }
.navcfg-item-label { font-size: 12px; color: var(--txt); }
.navcfg-arrows { margin-left: auto; display: flex; gap: 4px; flex-shrink: 0; }
.navcfg-sec.navcfg-off .navcfg-sec-label { opacity: .4; text-decoration: line-through; }
.navcfg-item.navcfg-off .navcfg-item-label { opacity: .4; text-decoration: line-through; }

/* ═══════════════════════════════════════════════════════════════════════════
   v2.21.416 — UI V4 reskin (flag `ui_v4`).
   ⚠ v2.21.435: the header used to say "admin dogfood — Sam only". That was never
   true in production — the flag row was INSERTED at enabled_for='all' on
   2026-07-17 15:30:30 UTC, so every tenant has carried body.ui-v4 since. That
   false assumption is what let this block silently shadow the tenant Customize
   colours for four days (see applyTenantTheme in js/app.js). The palette below
   re-declares --bg/--bg1/--teal/--teal2 ON BODY, which beats an inherited value
   from <html> — applyTenantTheme therefore writes to html AND body so a tenant's
   own scheme outranks this skin. Do NOT delete these declarations to "fix" that:
   they are the DEFAULT look for the ~100% of tenants who never customized.
   EVERYTHING below is scoped under body.ui-v4: with no flag row the class is
   never applied and every tenant renders byte-identical. Design language from
   Sam's 2026-07-17 reference screenshots: serif display type (Playfair) for
   titles + money figures, small-caps eyebrow labels, soft dark cards,
   underline tab strips, a quieter refined sidebar. Semantic money colors
   (--red/--grn/--yel = loss/profit/warning) are deliberately untouched.
   ═══════════════════════════════════════════════════════════════════════════ */
body.ui-v4 {
  --bg:  #0a0b0c;
  --bg1: #0d0e10;
  --bg2: #101113;
  --bg3: #16181b;
  --bg4: #1e2126;
  --brd: #1d2025;
  --brd2:#2b2f36;
  --teal:#39c9b0;
  --teal2:#2ba894;
  --tealdim: rgba(57,201,176,.10);
  --tealglw: rgba(57,201,176,.16);
  --ontl:#04110e;
  --ontl2:#04110e;
  --fs: 'Playfair Display', Georgia, 'Times New Roman', serif;
}

/* ── sidebar ── */
body.ui-v4 .sb { border-right-color: var(--brd); }
body.ui-v4 .logo-dot { display: none; }
/* v2.21.435: sizes go through --ll-font-scale (the Customize "Text size" knob was
   inert under this skin — every size below was a hard px), and the hard-coded
   grays become sidebar/page ink vars so a light tenant theme stays legible. */
body.ui-v4 .logo-text { font-family: var(--fs); font-size: calc(21px * var(--ll-font-scale, 1)); font-weight: 700; letter-spacing: .2px; }
body.ui-v4 .logo-sub { font-size: calc(8.5px * var(--ll-font-scale, 1)); letter-spacing: 2.4px; }
body.ui-v4 .nav-sec { font-size: calc(9.5px * var(--ll-font-scale, 1)); letter-spacing: 2px; color: var(--sbtxt3); padding: 18px 12px 7px; }
body.ui-v4 .ni { padding: 8px 12px; border-radius: 8px; font-size: calc(12.5px * var(--ll-font-scale, 1)); color: var(--sbtxt2); }
body.ui-v4 .ni:hover { color: var(--sbtxt); }
body.ui-v4 .ni.on { background: var(--tealdim); color: var(--teal); border-color: rgba(57,201,176,.22); }
body.ui-v4 .ni.on::before { display: none; }
body.ui-v4 .ni-badge { border-radius: 9px; padding: 1px 7px; }

/* ── page headers: eyebrow + serif title ── */
.ui4-eyebrow { display: none; }
body.ui-v4 .ui4-eyebrow { display: block; font-family: var(--fd); font-size: calc(10px * var(--ll-font-scale, 1)); letter-spacing: 2.4px; text-transform: uppercase; color: var(--teal); font-weight: 700; margin-bottom: 7px; }
body.ui-v4 .pt { font-family: var(--fs); font-size: calc(30px * var(--ll-font-scale, 1)); font-weight: 600; letter-spacing: .2px; line-height: 1.1; }
body.ui-v4 .ps { font-size: calc(12.5px * var(--ll-font-scale, 1)); color: var(--txt2); margin-top: 6px; }
body.ui-v4 .ph { margin-bottom: 26px; }

/* ── KPI tiles: serif figures, clean cards ── */
body.ui-v4 .kpi { border-radius: 10px; padding: 18px; }
body.ui-v4 .kpi::before { display: none; }
body.ui-v4 .kpi-corner { display: none; }
body.ui-v4 .kl { font-size: calc(9.5px * var(--ll-font-scale, 1)); letter-spacing: 1.8px; color: var(--txt3); font-weight: 600; margin-bottom: 10px; }
body.ui-v4 .kv { font-family: var(--fs); font-size: calc(26px * var(--ll-font-scale, 1)); font-weight: 600; letter-spacing: .2px; }
body.ui-v4 .ks { font-size: calc(11px * var(--ll-font-scale, 1)); margin-top: 7px; }
body.ui-v4 .ll-kpi-v { font-family: var(--fs); }
body.ui-v4 .plh-val { font-family: var(--fs); font-weight: 600; }
body.ui-v4 .plh-stat .v { font-family: var(--fs); }

/* ── cards / tables ── */
body.ui-v4 .tw, body.ui-v4 .ic { border-radius: 12px; }
body.ui-v4 .th { background: transparent; padding: 13px 16px; }
body.ui-v4 .tt-dot { display: none; }
body.ui-v4 .tt { font-size: calc(13px * var(--ll-font-scale, 1)); letter-spacing: .2px; }
body.ui-v4 thead th { background: transparent; font-size: calc(9.5px * var(--ll-font-scale, 1)); letter-spacing: 1.5px; color: var(--txt3); padding: 10px 14px; }
body.ui-v4 td { padding: 11px 14px; }
body.ui-v4 tbody tr { border-bottom-color: var(--brd); }
body.ui-v4 .modal { border-radius: 14px; }
body.ui-v4 .modal h2 { font-family: var(--fs); font-weight: 600; letter-spacing: .4px; }

/* ── buttons / inputs / chips ── */
body.ui-v4 .btn { border-radius: 8px; }
body.ui-v4 .btn-t { color: var(--ontl); }
body.ui-v4 .si { border-radius: 8px; }
body.ui-v4 .bge { border-radius: 6px; letter-spacing: .6px; }
body.ui-v4 .sync-pill { border-radius: 14px; }

/* ── tab strips: the .ctabs rows (Combine, Inventory, …) go underline-style ── */
body.ui-v4 .ctabs { gap: 0; border-bottom: 1px solid var(--brd); margin-bottom: 20px; }
body.ui-v4 .ctab { background: transparent; border: none; border-bottom: 2px solid transparent; border-radius: 0; padding: 10px 4px; margin-right: 26px; color: var(--txt2); font-size: calc(12.5px * var(--ll-font-scale, 1)); }
body.ui-v4 .ctab:hover { color: var(--txt); }
body.ui-v4 .ctab.on { background: transparent; color: var(--txt); border-bottom-color: var(--teal); }
body.ui-v4 .ctab span { color: var(--teal); font-family: var(--fm); font-size: 11px; }

/* v2.21.419 (Sam PM-7): the in-page hub-tile rows (nav_v2) and the money-report quick-nav
   strip are DUPLICATES of the new headings-nav section tab bar → hide them under ui-v4 so
   each page shows a single tab strip (the "double buttons" Sam flagged). */
body.ui-v4 .navv2-hub-tiles,
body.ui-v4 .reports-quicknav { display: none !important; }

/* user-hidden nav entries (sidebar customization) — hide-only, scoped to the flag */
body.ui-v4 .ll-nav-hidden { display: none !important; }

/* ── COMBINE Exact/Similar group rows → the parent/child card layout ──
   (cmb4-* class hooks are inert without body.ui-v4; every data-act handler,
   arg and inline style in the markup is unchanged — this layer only re-skins) */
body.ui-v4 .cmb4-row td { padding: 16px 14px; vertical-align: top; }
body.ui-v4 img.cmb4-img { width: 52px !important; height: 72px !important; border-radius: 6px !important; }
body.ui-v4 div.cmb4-img { width: 52px !important; height: 72px !important; border-radius: 6px !important; }
body.ui-v4 .cmb4-lab { display: block !important; font-size: 9px !important; letter-spacing: 1.7px !important; color: var(--teal) !important; font-weight: 700; margin-bottom: 7px !important; }
body.ui-v4 .cmb4-sku { display: inline-block; background: var(--bg3); border: 1px solid var(--brd2); padding: 1px 7px; border-radius: 5px; font-size: 10px !important; margin-bottom: 5px !important; }
body.ui-v4 .cmb4-title { font-size: 12.5px !important; font-weight: 600; color: var(--txt) !important; line-height: 1.45 !important; white-space: normal !important; overflow: visible !important; text-overflow: clip !important; }
body.ui-v4 .cmb4-meta { font-size: 10.5px !important; margin-top: 5px !important; }
body.ui-v4 .cmb4-qty { font-family: var(--fs); font-size: 26px !important; font-weight: 600 !important; color: var(--teal) !important; text-align: center; min-width: 76px; }
body.ui-v4 .cmb4-qty::before { content: 'New qty'; display: block; font-family: var(--fd); font-size: 9px; letter-spacing: 1.7px; text-transform: uppercase; color: var(--txt3); font-weight: 600; margin-bottom: 5px; }
body.ui-v4 .cmb4-score { text-align: center; }
body.ui-v4 .cmb4-act { min-width: 122px; }
body.ui-v4 .cmb4-act .btn { display: flex; width: 112px; justify-content: center; margin: 0 0 6px !important; }
body.ui-v4 .cmb4-swap { background: transparent !important; border: 1px solid var(--brd2) !important; color: var(--txt3) !important; }
body.ui-v4 .cmb4-swap:hover { color: var(--txt2) !important; border-color: var(--txt3) !important; }

/* serif price figure on the backfill card rows when the reskin is on */
body.ui-v4 .lbc-price { font-family: var(--fs); font-size: 20px; font-weight: 600; }
body.ui-v4 .lbc-title { font-size: 13.5px; }

/* ═══════════════════════════════════════════════════════════════════════════
   v2.21.417 — UI V4 NAV: headings-only sidebar + built-in horizontal section
   tabs (Sam's directive). All scoped under body.ui-v4 → flag off = untouched.
   Under body.ui-v4: the detailed sidebar is hidden (kept in DOM for gating +
   Uncategorized resolution) and #ui4-secnav (JS-rendered heading buttons) shows;
   #ui4-sectabs is the persistent tab strip at the top of the content area.
   ═══════════════════════════════════════════════════════════════════════════ */
body.ui-v4 .sb-nav > :not(#ui4-secnav) { display: none !important; }
body.ui-v4 #ui4-secnav { display: block; }
.ui4-sechead {
  padding: 11px 12px; margin-bottom: 3px; border-radius: 8px; cursor: pointer;
  font-family: var(--fs, Georgia, serif); font-size: 16px; font-weight: 600; letter-spacing: .2px;
  color: var(--sbtxt2); transition: background .12s, color .12s; user-select: none;
}
.ui4-sechead:hover { background: var(--bg3); color: var(--sbtxt); }
.ui4-sechead.active { background: var(--tealdim); color: var(--teal); }
/* v2.21.419: persistent action buttons below the Admin heading (eBay Sync / TCGP Sync). */
.ui4-navactions { margin-top: 14px; padding-top: 12px; border-top: 1px solid var(--brd); display: flex; flex-direction: column; gap: 6px; }
.ui4-navaction { padding: 8px 12px; border-radius: 8px; cursor: pointer; font-size: 12px; font-weight: 600; color: var(--txt2); border: 1px solid var(--brd2); background: transparent; transition: color .12s, border-color .12s; user-select: none; }
.ui4-navaction:hover { color: var(--teal); border-color: var(--teal); }
#ui4-sectabs {
  align-items: center; gap: 0; flex-wrap: wrap;
  border-bottom: 1px solid var(--brd); margin: 0 0 22px; padding-top: 2px;
}
.ui4-stab {
  padding: 11px 4px; margin-right: 26px; cursor: pointer; white-space: nowrap;
  font-size: calc(13px * var(--ll-font-scale, 1)); color: var(--txt2); border-bottom: 2px solid transparent;
  transition: color .12s, border-color .12s; user-select: none;
}
.ui4-stab:hover { color: var(--txt); }
/* v2.21.418 (Sam PM-6): active tab highlighted GREEN so it pops out. */
.ui4-stab.on { color: var(--teal); border-bottom-color: var(--teal); font-weight: 700; background: var(--tealdim); border-radius: 6px 6px 0 0; }
@media (max-width: 900px) {
  #ui4-sectabs { -webkit-overflow-scrolling: touch; overflow-x: auto; flex-wrap: nowrap; }
  .ui4-stab { margin-right: 18px; }
}
/* v2.21.418: Pricing tab = pricing tool ONLY. When body.ui4-pricing-only, hide the
   inventory page's header / hub-tiles / sub-tab strip / listings panel — leaving just
   the pricing panel (invTab('pricing') shows it). go() clears the class on any nav. */
body.ui4-pricing-only #page-inv > .ph,
body.ui4-pricing-only #page-inv > .navv2-hub-tiles,
body.ui4-pricing-only #page-inv > #inv-subtabs,
body.ui4-pricing-only #page-inv > #inv-panel-listings { display: none !important; }
/* v2.21.418: Customize-Sidebar editor "+ Add a feature" row. */
.navcfg-add { display: flex; gap: 6px; align-items: center; padding: 7px 10px 9px 24px; border-top: 1px dashed var(--brd); }

/* ═══════════════════════════════════════════════════════════════════════════
   v2.21.420 — Scan Batch History: custom folders + collapse (Sam PM-8).
   A tenant groups batches into collapsible folders so big non-priority batches
   don't crowd the UI. Base theme (the whole page is already scan_identify-gated).
   ═══════════════════════════════════════════════════════════════════════════ */
.scan-hist-toolbar { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; padding: 4px 6px 10px; }
.scan-fgroup { border: 1px solid var(--brd); border-radius: 8px; margin-bottom: 10px; overflow: hidden; }
.scan-fhead { display: flex; align-items: center; gap: 8px; padding: 9px 12px; background: var(--bg3); cursor: pointer; user-select: none; }
.scan-fhead:hover { background: var(--bg4, var(--bg3)); }
.scan-fchev { font-size: 9px; opacity: .7; transition: transform .12s; display: inline-block; }
.scan-fgroup.collapsed .scan-fchev { transform: rotate(-90deg); }
.scan-fgroup.collapsed .scan-fbody { display: none; }
.scan-fname { font-size: 13px; font-weight: 700; }
.scan-fcount { font-size: 11px; color: var(--txt3, var(--m)); background: var(--bg2); border: 1px solid var(--brd2); border-radius: 10px; padding: 0 8px; }
.scan-facts { margin-left: auto; display: flex; gap: 4px; flex-shrink: 0; }
.scan-fbody { overflow-x: auto; }
.scan-fbody table { width: 100%; }

/* ═══════════════════════════════════════════════════════════════════════════
   v2.21.506 — THE CARD POPUP (cleanup plan phase 3, flag `platform_modal`).
   One card → one modal, a tab per platform. MOBILE-FIRST per Sam's decision #10:
   on a phone this is a FULL-SCREEN SHEET, not a shrunken centred modal.

   v2.21.508: keyed to the shared CLASS `.ll-cm`, not an id. The bulk-actions window
   reuses these same cm-* classes under a different id, and the original id-scoped
   rules left it completely unstyled. A class also lets a third window join later
   without touching this block.

   Every rule below applies only to elements _platformModalOn() actually built. With
   no flag row neither window is ever created, so this whole block is inert — that is
   what keeps flag-off byte-identical.
   Colours come from the same CSS variables applyTenantTheme writes, and are never
   re-declared on a descendant (the v2.21.435 cascade lesson).
   ═══════════════════════════════════════════════════════════════════════════ */
.ll-cm .cm-shell { display: flex; flex-direction: column; max-height: 90vh; }
.ll-cm .cm-head { display: flex; align-items: flex-start; gap: 12px; margin-bottom: 4px; }
.ll-cm .cm-thumb { width: 54px; height: 54px; border-radius: 5px; border: 1px solid var(--brd); object-fit: cover; background: var(--bg3); flex-shrink: 0; }
.ll-cm .cm-title { font-size: 14px; font-weight: 650; line-height: 1.3; color: var(--txt); word-break: break-word; }
.ll-cm .cm-sub { font-size: 11px; color: var(--txt3); margin-top: 3px; }
.ll-cm .cm-x { margin-left: auto; background: transparent; border: 1px solid var(--brd2); color: var(--txt3); border-radius: 6px; cursor: pointer; font-size: 15px; line-height: 1; padding: 6px 10px; flex-shrink: 0; }
.ll-cm .cm-x:hover { color: var(--txt); border-color: var(--brd); }

/* Tab strip — generated from the PLATFORMS list, never hand-enumerated. */
.ll-cm .cm-tabs { display: flex; gap: 4px; border-bottom: 1px solid var(--brd); margin: 12px 0 0; overflow-x: auto; flex-shrink: 0; }
.ll-cm .cm-tab { background: transparent; border: none; border-bottom: 2px solid transparent; color: var(--txt3); font-size: 12px; font-weight: 600; padding: 9px 13px; cursor: pointer; white-space: nowrap; }
.ll-cm .cm-tab:hover { color: var(--txt); }
.ll-cm .cm-tab.on { color: var(--teal); border-bottom-color: var(--teal); }
.ll-cm .cm-body { padding-top: 14px; overflow-y: auto; flex: 1 1 auto; }

.ll-cm .cm-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 9px 14px; }
.ll-cm .cm-f { min-width: 0; }
.ll-cm .cm-k { font-size: 10px; text-transform: uppercase; letter-spacing: .4px; color: var(--txt3); }
.ll-cm .cm-v { font-size: 13px; color: var(--txt); word-break: break-word; }
.ll-cm .cm-acts { display: flex; flex-wrap: wrap; gap: 7px; margin-top: 16px; }
.ll-cm .cm-note { font-size: 11px; color: var(--txt3); line-height: 1.45; margin-top: 12px; }
.ll-cm .cm-warn { font-size: 11px; line-height: 1.45; margin-top: 12px; border: 1px solid #f59e0b; color: #f59e0b; background: #f59e0b14; border-radius: 6px; padding: 9px 11px; }

/* v2.21.511 — Manual Listing on a phone.
   #page-manual lays its form and "Recently Added" side by side via an INLINE
   style="display:grid;grid-template-columns:1fr 1fr", so a stylesheet rule needs !important
   to override it. Measured at 375px before this: the second column sat at right:410 — off
   screen. Pre-existing, but .508 promoted this page to a first-class Inventory tab and
   decision #10 makes mobile first-class, so it collapses to one column here.
   Unflagged: a pure layout fix, and the standalone page benefits too. */
@media (max-width: 640px) {
  #page-manual > div[style*="grid-template-columns"] { grid-template-columns: 1fr !important; }
}

/* MOBILE: full-screen sheet. Not a shrunken modal — the sheet fills the viewport,
   the tab strip and header stay put, and only the body scrolls. Touch targets ≥40px. */
@media (max-width: 640px) {
  .ll-cm.modal-bg { padding: 0; align-items: stretch; justify-content: stretch; }
  .ll-cm .modal { max-width: none; width: 100%; height: 100%; max-height: 100%; border: none; border-radius: 0; padding: 16px 14px calc(14px + env(safe-area-inset-bottom, 0px)); }
  .ll-cm .cm-shell { max-height: none; height: 100%; }
  .ll-cm .cm-grid { grid-template-columns: 1fr; }
  .ll-cm .cm-tab { padding: 12px 14px; font-size: 13px; }
  .ll-cm .cm-x { padding: 10px 14px; font-size: 17px; }
  .ll-cm .cm-acts .btn { min-height: 40px; flex: 1 1 46%; }
}
