/* ==========================================================================
   Head to Tail Lights — ERP  (v2 "parts counter console")
   Black console chrome + white paper bench + sky-blue signal.
   Statuses read as indicator lamps; data is set in the instrument face.
   Type roles: Montserrat 800 (display, from the logo), IBM Plex Sans (UI),
   IBM Plex Mono (instrument: SKUs, labels, table headers, figures).
   ========================================================================== */
:root {
  --black: #0a0a0b;
  --console: #101114;
  --ink: #17191c;
  --muted: #64707b;
  --line: #e2e6ea;
  --line-soft: #edf0f3;
  --bg: #f3f5f7;
  --paper: #ffffff;
  --accent: #29abe2;
  --accent-strong: #189bd3;
  --accent-deep: #1274a6;
  --green: #1e8a57;
  --amber: #b26b0f;
  --red: #c0392b;
  --violet: #71589e;
  --r-card: 6px;
  --r-ctl: 5px;
  --font-ui: 'IBM Plex Sans', 'Segoe UI', system-ui, -apple-system, sans-serif;
  --font-mono: 'IBM Plex Mono', ui-monospace, Consolas, monospace;
  --font-display: 'Montserrat', 'IBM Plex Sans', sans-serif;
  font-size: 15px;
}
* { box-sizing: border-box; }
body { margin: 0; background: var(--bg); color: var(--ink); font-family: var(--font-ui); }
a { color: var(--accent-deep); text-decoration: none; }
a:hover { text-decoration: underline; }

:is(button, .btn, input, select, textarea, a, summary):focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

/* ---------- Brand ---------- */
.brand-logo { display: block; mix-blend-mode: screen; }

/* ---------- Login ---------- */
.login-wrap {
  min-height: 100vh; display: flex; flex-direction: column;
  align-items: center; justify-content: center;
  background: radial-gradient(900px 480px at 50% 18%, #16181c 0%, var(--black) 62%);
  padding: 32px 16px;
}
.login-brand { width: min(380px, 84vw); }
.login-brand .brand-logo { width: 100%; }
.login-beam {
  width: min(480px, 92vw); height: 1px; margin: 26px 0 34px;
  background: linear-gradient(90deg, transparent, #3a4048 18%, var(--accent) 50%, #3a4048 82%, transparent);
  animation: beam-sweep .7s ease-out both;
}
@keyframes beam-sweep { from { transform: scaleX(0); opacity: 0; } to { transform: scaleX(1); opacity: 1; } }
.login-card {
  background: var(--paper); border-radius: var(--r-card); padding: 30px 32px 26px;
  width: min(380px, 92vw); box-shadow: 0 18px 60px rgba(0, 0, 0, .55);
}
.login-foot {
  margin-top: 26px; font-family: var(--font-mono); font-size: .62rem;
  letter-spacing: .42em; text-transform: uppercase; color: #5a636d;
}

/* ---------- App shell (left sidebar) ----------
   Desktop: a fixed rail. Expanded (250px) on hover/focus; after 1s away it
   tucks into a 64px icon rail. Content reserves the rail width, so the
   expanded state floats over the page instead of reflowing it. */
.shell { display: flex; min-height: 100vh; align-items: stretch; padding-left: 64px; }
.side {
  width: 274px; flex: none; background: var(--black); color: #fff;
  display: flex; flex-direction: column;
  position: fixed; left: 0; top: 0; height: 100vh; z-index: 40;
  overflow-y: auto; overflow-x: hidden;
  scrollbar-width: thin; scrollbar-color: #3a4048 transparent;
  /* Smoother expand/collapse: a longer, eased curve (Material standard-easing)
     glides the rail rather than the old snappy .18s linear-ish ease. The
     box-shadow is transitioned too — it fades in with the expand instead of
     popping — so .side.compact keeps the SAME shadow at zero alpha (you can't
     interpolate to `none`). */
  transition: width .22s cubic-bezier(.4, 0, .2, 1),
              box-shadow .22s cubic-bezier(.4, 0, .2, 1);
  box-shadow: 10px 0 34px rgba(0, 0, 0, .22);
}
.side.compact { width: 64px; box-shadow: 10px 0 34px rgba(0, 0, 0, 0); }
/* ---------- Floating content panel (2026-07-16) ----------
   The content is a rounded panel floating on the rail-tone background with an
   even gutter all around — so it shares no straight edge with the rail (no
   divider), and the whole dark area reads as one surface with the content
   floating on it. The content column stays centered INSIDE the panel via
   .view-inner (so cards/readable width are unchanged).
   DESKTOP ONLY: on mobile the rail is a drawer, so the content stays full-width
   with no gutter (avoids congestion). Overlay trade-off: the expanded rail
   covers the panel's left corners on hover; the panel shows at rest.
   DELETE this block to revert (main/.view-inner rules below stay valid). */
@media (min-width: 901px) {
  /* Gutter only goes near-black in dark mode. Light mode already gets a strong
     panel-vs-rail contrast for free (light panel on the dark rail) and never
     had this reviewed against a dark gutter — leave its .shell background as
     the ordinary page bg (body's var(--bg), unset here) so light mode is
     untouched by this block. */
  .dark .shell { background: var(--black); }
  main#view {
    margin: 12px;
    background: var(--bg);
    border-radius: 16px;
  }
  .dark main#view { background: #101319; }
  /* Drop the dark-mode rail right border (a leftover 1px steel line, .dark .side
     below): here the rail and the gutter are the same black, so that line was
     the ONLY visible edge and made the navbar read as a bordered separate piece.
     Gone, the rail melts into the gutter — one dark surround, content floating on
     it. Higher specificity than `.dark .side` so it wins regardless of source. */
  .dark .shell .side { border-right-color: transparent; }
}
/* ---------- end floating content panel ---------- */
/* Branded rail scrollbar (short/small-laptop viewports only need it, but the
   rule is unconditional — nothing to override once the rail fits). Track is
   transparent so the console-black rail shows through; the thumb is a muted
   steel pill, inset from the gutter edge via a black clip-border so it reads
   as a floating capsule rather than an edge-to-edge OS bar, brightening to
   brand cyan on hover — the same muted-steel→cyan language as the search
   field's hover state just above it in the rail. */
.side::-webkit-scrollbar { width: 9px; }
.side::-webkit-scrollbar-track { background: transparent; }
.side::-webkit-scrollbar-thumb {
  background-color: #3a4048; border-radius: 999px;
  border: 2px solid var(--black); background-clip: padding-box;
}
.side::-webkit-scrollbar-thumb:hover { background-color: var(--accent); }
/* Fixed-height logo slot — the mark and the full banner both sit in the
   same box, so swapping between them on expand/collapse never pushes the
   nav rows below. (Without this, expanding grows the logo a lot taller,
   which slides every row down and out from under a hovering cursor.) */
/* Vertical padding gives the logo breathing room top + bottom (it used to touch
   the sidebar's top edge). box-sizing keeps the slot a fixed height so the
   mark/banner swap on expand still doesn't push the nav rows. */
.side-logo { display: flex; flex: none; align-items: center; justify-content: flex-start; height: 54px; padding: 8px 18px; box-sizing: border-box; }
.side-logo .brand-logo { max-width: 100%; max-height: 100%; width: auto; height: auto; }
.side-logo .logo-mark { display: none; margin: 0 auto; max-height: 34px; }
.side.compact .side-logo { padding: 8px; justify-content: center; }
.side.compact .logo-full { display: none; }
.side.compact .logo-mark { display: block; }
.ic { flex: none; width: 16px; height: 16px; display: inline-flex; opacity: .9; }
.ic svg { width: 100%; height: 100%; }

/* Rail text (nav labels, group headers, chevrons, the search field's label +
   kbd) fades with the width instead of popping. Previously each carried a
   `[.side.compact_&]:hidden` Tailwind variant (display:none), which vanished a
   frame before the rail moved — the "snap" you still felt once width animated.
   `display` rides the transition via allow-discrete so the collapsed rail keeps
   the label OUT of layout (preserving the centered-icon compact rail) while the
   opacity fade plays in both directions. @starting-style seeds the fade-IN on
   expand. Fades don't cause vestibular motion, so this runs under reduced
   motion too. Graceful fallback on browsers without allow-discrete: the label
   simply pops as before. */
.side .nav-label {
  transition: opacity .2s ease, display .2s allow-discrete;
}
.side.compact .nav-label { opacity: 0; display: none; }
@starting-style {
  .side:not(.compact) .nav-label { opacity: 0; }
}

/* Sidebar section spacing — the even gap between the three nav groups
   (Inventory / Call Hub / Admin). HEIGHT-RESPONSIVE so a tall monitor fills the
   rail elegantly with generous, even section breaks, while a short laptop stays
   compact and doesn't force an internal scroll. margin-top == padding-top keeps
   the compact-rail divider centred between sections; the value is identical in
   the compact and expanded rail, so a row sits at the same Y in both states and
   nothing drifts under the cursor on hover-expand. Top-anchored flow (no flex
   justify) means collapsing a group keeps its header in place and the groups
   below rise up. Overrides the moved-out Tailwind margins via higher specificity.
   NOTE: when nav rows are ADDED (each ~30px), re-check fill at 956px AND overflow
   at 768px and re-tune these four values / breakpoints — extra rows eat the bottom
   breathing room and worsen the short-laptop internal scroll. Never go back to
   flex `justify-between` to "fill" — it spreads unevenly and breaks the collapse. */
/* Ladder re-tuned 2026-07-16 for 19 rows (the Lost Sales row was added this
   phase). The generous "fill" spacing only turns on at heights where all 19 rows
   still clear the sticky account footer; below that the rail stays tight (no
   internal scroll). Base = tight so 1366×768 fits; each min-height step adds even
   section spacing once there's headroom for it. margin-top == padding-top keeps
   the compact-rail divider centred (see note above). Re-tune if more rows land. */
.side nav { padding-top: 0px; }
.side nav > .group\/grp { margin-top: 0px; padding-top: 0px; }
@media (min-height: 840px)  { .side nav { padding-top: 8px; }  .side nav > .group\/grp { margin-top: 12px; padding-top: 12px; } }
@media (min-height: 910px)  { .side nav > .group\/grp { margin-top: 20px; padding-top: 20px; } }
@media (min-height: 980px)  { .side nav { padding-top: 12px; } .side nav > .group\/grp { margin-top: 28px; padding-top: 28px; } }

/* main is the content region (a floating panel on desktop — see the floating
   content panel block above). The readable column lives in .view-inner, which
   caps its width and centers it INSIDE main; margin-inline:auto sends leftover
   flex space to the side margins rather than pooling on the right. */
main { flex: 1; min-width: 0; }
.view-inner { max-width: 1460px; margin-inline: auto; padding: 22px 26px 60px; }

/* Mobile/tablet (≤900px) header bar — hidden on desktop, hamburger opens the
   drawer built from the same .side markup (see the ≤900px block below). */
.mobile-bar {
  display: none; align-items: center; justify-content: space-between; gap: 10px;
  height: 56px; padding: 0 14px; background: var(--black);
  position: sticky; top: 0; z-index: 41;
}
.mobile-bar .side-logo { height: 56px; padding: 10px 0; }
.hamburger {
  display: flex; flex-direction: column; justify-content: center; align-items: center; gap: 4px;
  width: 38px; height: 38px; padding: 0; border: 0; background: transparent; cursor: pointer;
}
.hamburger span { display: block; width: 22px; height: 2px; background: #fff; border-radius: 2px; transition: transform .18s ease, opacity .18s ease; }
.hamburger[aria-expanded="true"] span:nth-child(1) { transform: translateY(6px) rotate(45deg); }
.hamburger[aria-expanded="true"] span:nth-child(2) { opacity: 0; }
.hamburger[aria-expanded="true"] span:nth-child(3) { transform: translateY(-6px) rotate(-45deg); }
body.nav-lock { overflow: hidden; }

/* ---------- Console strip (page title) ---------- */
.page-title {
  display: flex; align-items: center; gap: 14px; flex-wrap: wrap;
  background: var(--console); color: #fff; border-radius: 8px;
  padding: 14px 18px; margin: 0 0 18px;
  position: relative; overflow: hidden;
}
.page-title h1 {
  font-family: var(--font-display); font-weight: 800; font-size: 1.3rem; margin: 0;
  text-transform: uppercase; letter-spacing: .05em; line-height: 1.15; color: #fff;
}
.page-title .count { color: #8b95a0; font-size: .76rem; font-family: var(--font-mono); }
.page-title .grow { flex: 1; }
.page-title button { border-color: transparent; }
.page-title button:not(.primary):not(.danger) { background: rgba(255, 255, 255, .1); color: #e8ecef; }
.page-title button:not(.primary):not(.danger):hover { background: rgba(255, 255, 255, .18); }

/* dashboard console: heading pairs with a personal greeting; clock reads like
   an instrument-cluster readout, quiet enough not to compete with the title */
.dash-heading { display: flex; flex-direction: column; gap: 2px; }
.dash-welcome { font-family: var(--font-mono); font-size: .74rem; color: #9fb0bd; }
.dash-clock { text-align: right; line-height: 1.2; }
.dash-clock .time {
  font-family: var(--font-mono); font-weight: 600; font-size: 1.1rem; color: #fff;
  font-variant-numeric: tabular-nums; letter-spacing: .02em;
}
.dash-clock .date {
  font-family: var(--font-mono); font-size: .62rem; color: #8b95a0;
  text-transform: uppercase; letter-spacing: .13em; margin-top: 1px;
}
@media (max-width: 560px) { .dash-clock { display: none; } }

/* ---------- Cards / stats ---------- */
/* Three labeled clusters (Inventory / Sales / Operations) instead of one flat
   grid — the cluster border carries the hierarchy, so cards inside can run
   smaller. Mobile collapses each cluster into a tappable summary row. */
.stat-groups { display: grid; grid-template-columns: repeat(3, 1fr); gap: 14px; margin-bottom: 22px; align-items: start; }
.stat-group { border: 1px solid var(--line); border-radius: var(--r-card); background: var(--paper); overflow: hidden; }
.sg-head {
  display: flex; align-items: center; gap: 10px; padding: 10px 14px;
  font-family: var(--font-mono); font-size: .68rem; color: var(--muted);
}
.sg-label { text-transform: uppercase; letter-spacing: .14em; font-weight: 600; color: var(--ink); }
.sg-chips { display: none; }
.sg-chip {
  font: inherit; font-family: var(--font-mono); font-weight: 600; color: var(--ink);
  background: none; border: 0; padding: 0; cursor: pointer;
}
.sg-chip:hover, .sg-chip.selected { color: var(--accent-deep); }
.sg-sep { color: #b9c2ca; margin: 0 5px; }
.sg-head .caret { display: none; flex: none; width: 14px; height: 14px; margin-left: auto; color: var(--muted); transition: transform .18s ease; }
.sg-body { grid-template-rows: 1fr; display: grid; }
.sg-in { overflow: hidden; min-height: 0; display: grid; grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); gap: 10px; padding: 0 12px 12px; }

.stat {
  background: var(--paper); border: 1px solid var(--line); border-top: 3px solid var(--ink);
  border-radius: var(--r-card); padding: 14px 16px 12px;
}
.stat .n { font-family: var(--font-mono); font-weight: 600; font-size: 1.7rem; letter-spacing: -.01em; font-variant-numeric: tabular-nums; }
.stat .l {
  color: var(--muted); font-family: var(--font-mono); font-size: .64rem;
  text-transform: uppercase; letter-spacing: .1em; margin-top: 2px;
}
/* Dashboard stat-group cards ONLY (.sg-in). Scoped here — not on the base .stat
   — so other stat cards (e.g. LostSales' `.lost-stats`, whose labels are long
   sentences and whose "Top reason" tile puts TEXT in .n) keep their natural
   wrapping instead of getting truncated. */
.sg-in .stat { container-type: inline-size; }
/* Figure never bleeds past the card: one line, scaled to the card width
   (13cqi, capped at the 1.7rem design size, floored so it stays legible);
   overflow/ellipsis is a last-resort backstop after scaling. */
.sg-in .stat .n {
  font-size: clamp(0.85rem, 13cqi, 1.7rem);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
/* Every card reserves exactly two lines for its label so all cards are the same
   height (one word or three); a longer label truncates with an ellipsis (full
   text stays available via the title tooltip set in Dashboard.jsx). */
.sg-in .stat .l {
  line-height: 1.25; min-height: 2.5em;
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2;
  line-clamp: 2; overflow: hidden;
}
.stat.accent { border-top-color: var(--accent); }
.stat.accent .n { color: var(--accent-deep); }
/* clickable stat cards drive the dashboard chart */
.stat.as-btn {
  display: block; width: 100%; text-align: left; font: inherit; cursor: pointer;
  padding: 12px 14px 10px; border-radius: var(--r-card);
  background: var(--paper); border: 1px solid var(--line); border-top: 3px solid var(--ink);
}
.stat.as-btn.accent { border-top-color: var(--accent); }
.stat.as-btn:hover { background: #f7fbfd; border-color: #b9cbd6; }
.stat.as-btn.selected { background: var(--accent-wash); border-color: var(--accent); border-top-color: var(--accent); }
.stat.as-btn.selected .l { color: var(--accent-deep); }

/* Stat delta — "+/- % from <prior period>" meta chip, rendered INLINE beside the
   card's number (Dashboard <StatDelta>, in a shared .n-row with <StatNum>) — NOT
   a 3rd line under the label. A below-label delta made Sales/Operations cards
   taller than Inventory cards (which have no delta), breaking .stat-group height
   parity across the three groups; sitting beside the number adds no extra row.
   Green up / red down, but direction is also carried by a glyph + a signed number
   so it never relies on colour alone (ux color-not-decorative-only). Tabular
   figures. Only Sales + Operations cards get one; --green/--red already have
   dark-theme variants, so both modes work. */
/* The number owns the row's width (flex:1, allowed to shrink to 0 so its OWN
   cqi-scaled font — not the delta — decides how much it needs); the delta is a
   fixed, narrow, VERTICALLY-STACKED chip pinned to the top-right (percent over
   period label), so it never competes horizontally with the figure. Aligning the
   chip's baseline to the number's keeps "+16.8%" level with the top of the digits
   and lets "last month" hang into the whitespace above the label. */
.stat .n-row { display: flex; align-items: center; justify-content: space-between; gap: 8px; min-height: 1.7rem; }
.stat .n-row .n { min-width: 0; flex: 1 1 auto; }
/* The 2-line stacked chip is kept SHORTER than the number's line box (tight
   line-heights + tiny gap) and centered within .n-row, so it sits fully beside
   the figure and adds NO height — the flow cards stay the same height as the
   Inventory cards (which have no delta). */
.stat .delta {
  display: flex; flex-direction: column; align-items: flex-end; gap: 0;
  flex: 0 0 auto; white-space: nowrap; text-align: right;
  font-family: var(--font-mono); font-size: .58rem; font-variant-numeric: tabular-nums; line-height: 1.1;
}
.stat .delta .delta-main { display: flex; align-items: center; gap: 4px; }
.stat .delta .delta-ic { flex: none; }
.stat .delta .delta-pct { font-weight: 600; letter-spacing: .01em; }
.stat .delta .delta-since { color: var(--muted); font-size: .54rem; letter-spacing: .02em; }
.stat .delta.up { color: var(--green); }
.stat .delta.down { color: var(--red); }
.stat .delta.flat, .stat .delta.flat .delta-pct { color: var(--muted); }

/* dashboard chart panel header: a 3-column console strip — breakdown toggle on
   the LEFT, chart title centred, range toggle on the RIGHT. The two 1fr side
   columns are equal, so the title stays dead-centred regardless of toggle width,
   and a hidden toggle just leaves its column empty (no title shift). Stacks on
   mobile (see the ≤640px block). */
.chart-head { display: grid; grid-template-columns: 1fr auto 1fr; align-items: center; gap: 6px 12px; }
/* explicit grid-row keeps all three on ONE row regardless of DOM order (the title
   is first in the DOM — so mobile stacks it on top — but sits in the centre column;
   without an explicit row, sparse auto-placement would wrap the left toggle down). */
.chart-head h2 { grid-column: 2; grid-row: 1; text-align: center; margin: 0; }
.chart-head .seg-left { grid-column: 1; grid-row: 1; justify-self: start; }
.chart-head .seg-right { grid-column: 3; grid-row: 1; justify-self: end; }

/* Segmented control — connected segments inside one inset track; the selected
   segment reads as a raised, cyan-lit "key" (instrument-console language). Token
   driven, so light/dark and the shared shape scale come for free. */
.range-sel {
  display: inline-flex; gap: 2px; padding: 3px;
  background: var(--line-soft); border: 1px solid var(--line); border-radius: 999px;
}
.range-sel[hidden] { display: none; }
.range-sel button {
  font-family: var(--font-mono); font-size: .63rem; font-weight: 600;
  text-transform: uppercase; letter-spacing: .05em;
  padding: 4px 12px; border-radius: 999px;
  border: 0; background: transparent; color: var(--muted); cursor: pointer;
  transition: background .16s ease, color .16s ease, box-shadow .16s ease;
}
.range-sel button:hover:not(.active) { color: var(--ink); background: var(--paper); }
.range-sel button.active {
  background: var(--accent); color: var(--black);
  box-shadow: 0 1px 2px rgba(23, 25, 28, .16), var(--panel-highlight);
}

/* dashboard chart (single series — no legend; title carries identity).
   Rendered at true pixel size (see barChart() in views-inventory.js), so this
   just a safety net during the resize debounce. */
#chart-box svg { max-width: 100%; }
/* Breathing room between the title/range toggle and the top of the chart's y-axis. */
#chart-box { margin-top: 22px; }

.sync-foot {
  display: flex; gap: 10px; align-items: center; justify-content: flex-end;
  font-family: var(--font-mono); font-size: .68rem; color: var(--muted); padding: 2px;
}
.sync-foot button { font-size: .68rem; padding: 3px 9px; }

/* Bulk price-edit action bar (Price Review) — sits above the table, below the
   search/filter toolbar, so the batch actions are reachable without scrolling
   past the list. Accent-tinted so it reads as an active working state. */
.bulk-bar {
  display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
  background: rgba(41, 171, 226, .08);
  border: 1px solid rgba(41, 171, 226, .35);
  border-radius: var(--r-card);
  padding: 9px 14px; margin: 0 0 14px;
}
.bulk-bar .bulk-count {
  font-family: var(--font-mono); font-weight: 700; font-size: .78rem; color: var(--ink);
  white-space: nowrap;
}
.bulk-bar .bulk-hint { font-family: var(--font-mono); font-size: .68rem; color: var(--muted); }
.bulk-bar .bulk-actions { margin-left: auto; display: flex; gap: 8px; flex-wrap: wrap; }

/* Rows pinned to the top of Price Review because they're in the current bulk
   selection. Element+class (and matching :hover) so the tint beats the table
   primitive's tr-level hover utility. */
tr.row-pinned { background: rgba(41, 171, 226, .07); }
tr.row-pinned:hover { background: rgba(41, 171, 226, .12); }
tr.row-pinned td:first-child { box-shadow: inset 3px 0 0 var(--accent); }

.settings-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 18px; align-items: start; max-width: 900px; }
@media (max-width: 760px) { .settings-grid { grid-template-columns: 1fr; } }

.panel {
  background: var(--paper); border: 1px solid var(--line); border-radius: var(--r-card);
  padding: 18px; margin-bottom: 18px;
}
.panel h2 {
  margin: 0 0 14px; font-family: var(--font-display); font-weight: 800;
  font-size: .82rem; text-transform: uppercase; letter-spacing: .08em;
  position: relative; padding-bottom: 9px;
}
/* Sky-blue signal underline — reads the section title as a console instrument
   label rather than plain text (ties into the LED-dot / #29ABE2 accent set). */
.panel h2::after {
  content: ''; position: absolute; left: 0; bottom: 0;
  width: 100%; max-width: 220px; height: 2px; border-radius: 2px;
  background: var(--beam); opacity: .9;
}
/* dashboard chart panel: a touch tighter than a standard panel so the graph
   reads as a compact instrument rather than the page's main event */
/* Sized and centered like a gauge, not stretched like the ledger below it —
   the card hugs its content instead of leaving a dead gap the chart used to
   float in in on wide monitors. */
/* Wide + present, but bounded so a sparse range (a half-finished month = ~9
   bars) doesn't leave the bars marooned in a full-1460 plot. ~1150 reads bold
   and fills at 30-day/category ranges; chunky bars (maxBarSize in the chart
   components) carry the presence on sparse ranges. */
.panel.chart-panel { padding: 18px 20px 12px; max-width: 1150px; margin-left: auto; margin-right: auto; }
.panel.chart-panel h2 { margin-bottom: 6px; text-align: center; }
.panel.chart-panel h2::after { left: 50%; transform: translateX(-50%); } /* center the accent under the centered title */

/* ---------- Data-source meta line ---------- */
.src-line {
  display: flex; align-items: center; gap: 7px; flex-wrap: wrap;
  font-family: var(--font-mono); font-size: .66rem; letter-spacing: .04em;
  color: var(--muted); margin: -4px 2px 10px;
}
.src-line .src-k {
  text-transform: uppercase; letter-spacing: .2em; font-size: .58rem; color: #97a3ac;
  border: 1px solid var(--line); border-radius: 3px; padding: 1px 5px; background: var(--paper);
}
.src-info {
  border: 0; background: none; color: var(--accent-deep); cursor: pointer;
  font-size: .85rem; line-height: 1; padding: 0 2px;
}
.src-info:hover { color: var(--accent-strong); }

/* ---------- Tables ---------- */
.table-wrap { background: var(--paper); border: 1px solid var(--line); border-radius: var(--r-card); overflow: auto; }
.table-wrap.tall { max-height: min(68vh, 900px); }
table { width: 100%; border-collapse: collapse; font-size: .86rem; }
th {
  text-align: left; padding: 10px 12px; background: linear-gradient(180deg, #fbfcfd, #f5f8fa); color: var(--muted);
  font-family: var(--font-mono); font-weight: 500; font-size: .66rem;
  text-transform: uppercase; letter-spacing: .1em;
  border-bottom: 1px solid var(--line); white-space: nowrap;
  position: sticky; top: 0; z-index: 2;
}
td { padding: 8px 12px; border-bottom: 1px solid var(--line-soft); vertical-align: middle; }
tr:last-child td { border-bottom: 0; }
tbody tr[data-id], tbody tr[data-oid] { cursor: pointer; }
tbody tr:hover { background: var(--accent-wash); box-shadow: inset 2px 0 0 var(--accent); }
tfoot td { border-top: 1px solid var(--line); border-bottom: 0; background: #fafbfc; }
td.num, th.num { text-align: right; white-space: nowrap; font-variant-numeric: tabular-nums; }
td.t { min-width: 260px; }
td.clip {
  max-width: 220px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  color: var(--muted); font-size: .8rem;
}
td.clip.wide { max-width: 420px; }
.mono { font-family: var(--font-mono); font-size: .8rem; font-variant-numeric: tabular-nums; }
td.mono, td .mono { white-space: nowrap; }
/* shrink-to-content columns — lets Notes/Outcome take the leftover width */
th.fit, td.fit { width: 1%; white-space: nowrap; }

/* thumbnails */
td.thumb { width: 56px; padding: 5px 6px 5px 12px; }
td.thumb img, td.thumb .noimg { display: block; width: 44px; height: 44px; border-radius: 3px; }
td.thumb img { object-fit: cover; background: var(--line-soft); }
td.thumb .noimg {
  background:
    linear-gradient(to top right, transparent calc(50% - 1px), var(--line) calc(50% - 1px), var(--line) calc(50% + 1px), transparent calc(50% + 1px)),
    var(--line-soft);
  border: 1px solid var(--line);
}

/* ---------- Detailed list view (DetailList.jsx) ---------- */
/* Alternate density for the order/sale list pages: tall record rows (item photo +
   labelled field grid + trailing amount/date/action), divided by hairlines. The
   compact table stays the default; ViewToggle swaps between them. Row ≈ 3× a
   compact table row. */
.dlist { border: 1px solid var(--line); border-radius: var(--r-card); overflow: hidden; background: var(--paper); }
.drow {
  display: grid; grid-template-columns: auto 1fr auto; gap: 16px; align-items: center;
  width: 100%; text-align: left; color: var(--ink);
  background: var(--paper); border-bottom: 1px solid var(--line);
  padding: 13px 16px; cursor: pointer; transition: background .14s ease;
}
.drow:last-child { border-bottom: 0; }
.drow[role="button"]:hover { background: #f7fbfd; }
.drow[role="button"]:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.drow-media { flex: none; }
.drow-media img, .drow-media .noimg {
  display: block; width: 88px; height: 88px; border-radius: 4px;
  border: 1px solid var(--line); object-fit: cover; background: var(--line-soft);
}
.drow[role="button"]:hover .drow-media img, .drow[role="button"]:hover .drow-media .noimg { border-color: var(--accent); }
.drow-media .noimg {
  background:
    linear-gradient(to top right, transparent calc(50% - 1px), var(--line) calc(50% - 1px), var(--line) calc(50% + 1px), transparent calc(50% + 1px)),
    var(--line-soft);
}
.drow-main { min-width: 0; display: flex; flex-direction: column; gap: 5px; }
.drow-head { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.drow-ref { font-family: var(--font-mono); font-weight: 600; font-size: .82rem; color: var(--ink); }
.drow-title {
  font-size: .9rem; line-height: 1.3; color: var(--ink);
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; line-clamp: 2; overflow: hidden;
}
.drow-meta { display: flex; flex-wrap: wrap; gap: 3px 18px; margin-top: 1px; }
.drow-meta > span { font-size: .78rem; color: var(--ink); white-space: nowrap; }
.drow-meta i {
  font-family: var(--font-mono); font-style: normal; text-transform: uppercase;
  letter-spacing: .06em; font-size: .58rem; color: var(--muted); margin-right: 6px;
}
.drow-side { display: flex; flex-direction: column; align-items: flex-end; gap: 5px; text-align: right; white-space: nowrap; flex: none; }
.drow-amount { font-family: var(--font-mono); font-weight: 600; font-size: 1rem; font-variant-numeric: tabular-nums; color: var(--ink); }
.drow-date { font-family: var(--font-mono); font-size: .72rem; color: var(--muted); }
.drow-action {
  font-family: var(--font-ui); font-size: .74rem; padding: 3px 10px; border-radius: var(--r-ctl);
  border: 1px solid var(--line); background: var(--paper); color: var(--ink); cursor: pointer;
}
.drow-action:hover { border-color: var(--accent); color: var(--accent-deep); }
.dlist-empty {
  border: 1px solid var(--line); border-radius: var(--r-card); background: var(--paper);
  padding: 34px; text-align: center; color: var(--muted);
}
.dark .drow[role="button"]:hover { background: rgba(255, 255, 255, .03); }
.view-toggle { flex: none; }
/* Mobile: drop to a 2-column row (smaller photo + body) and move the trailing
   column onto its own line below, laid out horizontally so nothing is cramped. */
@media (max-width: 640px) {
  .drow { grid-template-columns: 64px 1fr; gap: 12px; }
  .drow-media img, .drow-media .noimg { width: 64px; height: 64px; }
  .drow-side { grid-column: 1 / -1; flex-direction: row; align-items: center; justify-content: flex-start; gap: 16px; text-align: left; }
}

/* ---------- Status lamps (badges) ---------- */
.badge {
  display: inline-block; padding: 2px 8px; border-radius: 3px;
  font-family: var(--font-mono); font-size: .64rem; font-weight: 600;
  text-transform: uppercase; letter-spacing: .07em; white-space: nowrap;
}
.badge:not([class*="role-"]):not(.site)::before {
  content: ''; display: inline-block; width: 6px; height: 6px; border-radius: 50%;
  background: currentColor; margin-right: 6px; vertical-align: 1px;
}
.badge.Live::before { box-shadow: 0 0 5px currentColor; }
.badge.Live { background: #e0f2ea; color: var(--green); }
.badge.Sold { background: #e3f2fa; color: var(--accent-deep); }
.badge.Warehouse { background: #eceff1; color: #4b5563; }
.badge.Repair { background: #f8ecd9; color: var(--amber); }
.badge.Ready { background: #ebf3e3; color: #48731f; }
.badge.OnHold { background: #fbe9e7; color: var(--red); }
.badge.Returned { background: #fbe9e7; color: var(--red); }
.badge.Trash { background: #ede8f4; color: var(--violet); }
.badge.Quote { background: #eceff1; color: #4b5563; }
.badge.PendingPayment { background: #f8ecd9; color: var(--amber); }
.badge.Paid { background: #e0f2ea; color: var(--green); }
.badge.Shipped { background: #e3f2fa; color: var(--accent-deep); }
.badge.Delivered { background: #ebf3e3; color: #48731f; }
.badge.Cancelled { background: #fbe9e7; color: var(--red); }
.badge.Open { background: #f8ecd9; color: var(--amber); }
.badge.Completed { background: #e0f2ea; color: var(--green); }
.badge.Missed, .badge.Declined, .badge.Failed { background: #fbe9e7; color: var(--red); }
.badge.Voicemail { background: #e3f2fa; color: var(--accent-deep); }
.badge.Goodprice { background: #e0f2ea; color: var(--green); }
.badge.Over-priced { background: #fbe9e7; color: var(--red); }
.badge.Under-priced { background: #f8ecd9; color: var(--amber); }
/* Phase 9 — eBay Orders local workflow states (New/Packed/Label Printed); reuses
   the existing .badge.Shipped (blue) rather than the spec's literal "green" —
   Shipped already means the same thing on MPO orders, and one word shouldn't
   carry two colors depending on which page rendered it. */
.badge.New { background: #eceff1; color: #4b5563; }
.badge.Packed { background: #e3f2fa; color: var(--accent-deep); }
.badge.LabelPrinted { background: #f8ecd9; color: var(--amber); }
.badge.role-admin { background: #fff; color: var(--black); border: 1px solid var(--black); }
.badge.role-va { background: #e3f2fa; color: var(--accent-deep); }
.badge.role-warehouse { background: #f8ecd9; color: var(--amber); }
.side .badge.role-admin { background: transparent; color: #fff; border-color: #4a525a; }
/* physical-warehouse tag: outlined so it reads as a location, not a lifecycle lamp */
.badge.site { background: transparent; border: 1px solid #b9c2ca; color: var(--muted); }
.page-title .badge.site { border-color: #4a525a; color: #cdd4da; }

/* ---------- Item detail media ---------- */
/* React uses .item-media; .part-media is kept for the classic UI at /classic/
   (drop it when classic is retired). Both share identical styling below. */
.detail-grid { display: grid; grid-template-columns: 300px 1fr; gap: 20px; align-items: start; }
.part-media, .item-media {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  background: var(--line-soft); border: 1px solid var(--line); border-radius: var(--r-card);
  min-height: 220px; overflow: hidden;
}
a.part-media:hover, a.item-media:hover { border-color: var(--accent); }
.part-media img, .item-media img { width: 100%; height: 100%; max-height: 340px; object-fit: contain; display: block; }
.empty-media { gap: 10px; padding: 24px; text-align: center; }
.empty-media .rings {
  width: 58px; height: 58px; border-radius: 50%; border: 2px solid #c3ccd4; position: relative;
}
.empty-media .rings::before {
  content: ''; position: absolute; inset: 9px; border-radius: 50%; border: 2px solid #c3ccd4;
}
.empty-media .rings::after {
  content: ''; position: absolute; inset: 23px; border-radius: 50%; background: #c3ccd4;
}
.empty-media .nm {
  font-family: var(--font-mono); font-size: .66rem; letter-spacing: .18em;
  text-transform: uppercase; color: var(--muted);
}
.empty-media a { font-size: .82rem; font-weight: 600; }
.empty-media .hint { font-size: .74rem; color: var(--muted); max-width: 220px; }
/* eBay-sourced image tag + market-price sample size (Phase 1) */
.item-media { position: relative; }
.item-media .img-src {
  position: absolute; left: 8px; bottom: 8px;
  background: rgba(0, 0, 0, .66); color: #fff; font-size: .68rem; font-weight: 600;
  letter-spacing: .04em; padding: 2px 7px; border-radius: 5px;
}
.market-n { color: var(--muted); font-size: .82rem; }
.market-cell { display: inline-flex; align-items: center; gap: 8px; }
.mkt-refresh {
  font-size: .8rem; line-height: 1; padding: 2px 7px; border-radius: 5px;
  border: 1px solid var(--line); background: var(--line-soft); color: var(--muted);
  cursor: pointer; transition: color .12s, border-color .12s;
}
.mkt-refresh:hover:not(:disabled) { border-color: var(--accent); color: var(--accent); }
.mkt-refresh:disabled { opacity: .55; cursor: default; }

/* ---------- Forms ---------- */
.toolbar { display: flex; gap: 10px; flex-wrap: wrap; margin-bottom: 14px; align-items: center; }
input, select, textarea {
  font: inherit; padding: 8px 11px; border: 1px solid #c8d1d9; border-radius: var(--r-ctl);
  background: #fff; color: var(--ink); outline: none;
}
input:focus, select:focus, textarea:focus {
  border-color: var(--accent); box-shadow: 0 0 0 3px rgba(41, 171, 226, .2);
}
textarea { resize: vertical; min-height: 70px; }
label {
  display: block; font-family: var(--font-mono); font-size: .66rem; font-weight: 500;
  text-transform: uppercase; letter-spacing: .09em; color: var(--muted); margin-bottom: 5px;
}
.field { margin-bottom: 12px; }
/* Secondary/guidance microcopy — muted, smaller, italic so it reads as meta
   rather than body text (used app-wide: form notes, panel status lines, etc.). */
.hint { font-size: .8rem; color: var(--muted); font-style: italic; line-height: 1.4; }
/* Informational callout for an action that also changes something external/live
   (e.g. "this price update also revises the live eBay listing"). Not muted — the
   user should clearly see the extra effect; accent rule marks it as noteworthy. */
.revise-note {
  font-size: .8rem; color: var(--ink); line-height: 1.4;
  margin-top: 6px; padding-left: 9px; border-left: 2px solid var(--accent);
}
.grid { display: grid; gap: 12px 14px; }
.grid.c2 { grid-template-columns: 1fr 1fr; }
.grid.c3 { grid-template-columns: 1fr 1fr 1fr; }
.grid.c4 { grid-template-columns: repeat(4, 1fr); }
.grid .full { grid-column: 1 / -1; }

button, .btn {
  font: inherit; font-weight: 600; border: 1px solid var(--line); border-radius: var(--r-ctl);
  padding: 8px 15px; cursor: pointer; background: var(--paper); color: var(--ink); font-size: .85rem;
}
button:hover, .btn:hover { background: var(--bg); }
button.primary, .btn.primary {
  background: var(--accent); border-color: var(--accent); color: var(--black); font-weight: 700;
}
button.primary:hover, .btn.primary:hover { background: var(--accent-strong); border-color: var(--accent-strong); }
button.danger { background: #fbe9e7; border-color: #f2cfcb; color: var(--red); }
button.danger:hover { background: #f7dbd7; }
button.ghost { background: transparent; border-color: transparent; color: var(--muted); }
button.ghost:hover { background: var(--line-soft); }
button:disabled { opacity: .45; cursor: default; }
button:disabled:hover { background: var(--paper); }

/* ---------- Modal ---------- */
.modal {
  background: #fff; border-radius: 8px; width: 100%; max-width: 860px; padding: 24px 26px;
  position: relative;
  box-shadow: var(--shadow-float), var(--panel-highlight);
}
.modal.narrow { max-width: 520px; }
.modal h2 {
  margin: 0 0 18px; font-family: var(--font-display); font-weight: 800;
  font-size: .95rem; text-transform: uppercase; letter-spacing: .05em;
}
.modal .actions { display: flex; justify-content: flex-end; gap: 10px; margin-top: 18px; }
.modal .panel h2 { font-size: .78rem; }

/* ---------- Call Hub ---------- */
.hub-grid { display: grid; grid-template-columns: minmax(420px, 560px) 1fr; gap: 18px; align-items: start; }
.hub-side { display: flex; flex-direction: column; gap: 0; }
.chips { display: flex; flex-wrap: wrap; gap: 6px; }
.chip {
  font-family: var(--font-mono); font-size: .68rem; font-weight: 600; letter-spacing: .07em;
  padding: 6px 10px; border: 1px solid var(--line); border-radius: 999px; background: var(--paper);
}
.chip:hover { border-color: var(--accent); background: var(--paper); }
.chip.active { background: var(--accent); border-color: var(--accent); color: var(--black); }
.hit {
  display: flex; gap: 8px; align-items: center; width: 100%; text-align: left;
  border: 1px solid var(--line); border-radius: var(--r-ctl); padding: 6px 10px;
  margin: 0 0 6px; font-size: .82rem; background: #fafbfc;
}
.hit:hover { border-color: var(--accent); background: var(--accent-wash); }
.hit .num { margin-left: auto; }
.picked {
  display: flex; gap: 8px; align-items: center; flex-wrap: wrap;
  border: 1px solid var(--accent); border-left-width: 3px; border-radius: var(--r-ctl);
  padding: 6px 10px; margin: 0 0 12px; font-size: .82rem; background: var(--accent-wash);
}
.picked button.ghost { padding: 2px 7px; margin-left: auto; }
.hub-feed { display: flex; flex-direction: column; gap: 2px; max-height: 44vh; overflow: auto; }
.feed-row {
  display: flex; gap: 10px; align-items: baseline; flex-wrap: wrap;
  padding: 7px 2px; border-bottom: 1px solid var(--line-soft); font-size: .82rem;
}
.feed-row:last-child { border-bottom: 0; }
.feed-row .oc {
  font-family: var(--font-mono); font-size: .64rem; letter-spacing: .07em;
  padding: 1px 7px; border-radius: 3px; background: var(--line-soft); color: var(--ink);
}
@media (max-width: 1000px) { .hub-grid { grid-template-columns: 1fr; } }

/* ---------- Dictate-to-fill (Call Hub) ---------- */
.dictate { margin: 0 0 14px; }
.dictate-mic { display: inline-flex; align-items: center; gap: 8px; font-weight: 700; font-size: .82rem; }
.dictate-mic .dictate-dot { width: 9px; height: 9px; border-radius: 50%; background: var(--accent); }
.dictate-mic.live { border-color: var(--red); color: var(--red); background: #fbe9e7; }
.dictate-mic.live .dictate-dot { background: var(--red); animation: dictate-pulse 1s ease-in-out infinite; }
@keyframes dictate-pulse { 0%, 100% { opacity: 1; } 50% { opacity: .2; } }
.dictate-hint { display: block; margin-top: 6px; font-size: .78rem; line-height: 1.35; }
.dictate-fallback { display: flex; gap: 8px; align-items: flex-start; }
.dictate-fallback textarea { flex: 1; min-height: 46px; }
.dictate-filled {
  display: flex; gap: 8px; align-items: center; flex-wrap: wrap;
  margin-top: 8px; padding: 6px 10px; font-size: .8rem;
  border: 1px solid var(--green); border-left-width: 3px; border-radius: var(--r-ctl);
  background: rgba(30, 138, 87, .08); color: var(--ink);
}
.dictate-filled button.ghost { padding: 2px 7px; margin-left: auto; }
.dark .dictate-mic.live { background: rgba(225, 116, 107, .14); }

/* ---------- Dashboard: call-desk panels + activity summary ---------- */
.dash-calls { display: grid; grid-template-columns: 1fr 1fr; gap: 18px; margin-bottom: 18px; align-items: start; }
.dash-calls .panel h2 { display: flex; align-items: baseline; gap: 8px; }
.dash-calls .panel h2 .count { font-family: var(--font-mono); font-size: .72rem; color: var(--muted); font-weight: 600; }
/* `.panel.chart-panel`'s max-width+auto-margin (below) shrink-wraps it to content —
   correct for a single full-width dashboard chart, but LostSales' two side-by-side
   `.dash-calls` charts reuse the same class and were collapsing to ~130px (their
   grid TRACK was the correct ~550px; the child just wasn't stretching to fill it),
   which is what made "By reason"/"By platform" look cramped/broken regardless of
   bar orientation. Neutralize the centering here so these panels fill their column. */
.dash-calls .chart-panel { max-width: none; margin-left: 0; margin-right: 0; }
.activity-table td { vertical-align: top; }
@media (max-width: 900px) { .dash-calls { grid-template-columns: 1fr; } }
@media (max-width: 640px) { .activity-table .col-who { display: none; } }

/* ---------- Databases map (admin) ---------- */
.dbmap { overflow-x: auto; }
.dbmap svg { min-width: 980px; width: 100%; height: auto; display: block; }
.dbmap .col-h {
  font-family: var(--font-mono); font-size: 10px; letter-spacing: .22em;
  text-transform: uppercase; fill: #97a3ac;
}
.dbmap .eg { fill: none; stroke: #c3ccd4; stroke-width: 1.5; transition: opacity .12s; }
.dbmap .eg.write { stroke: var(--amber); stroke-dasharray: 5 4; }
.dbmap .eg.writeback { stroke: var(--violet); stroke-dasharray: 3 3; }
.dbmap .nd { transition: opacity .12s; }
.dbmap .nd rect { stroke-width: 1.2; }
.dbmap .nd-sheet rect { fill: #e0f2ea; stroke: #9fd4bb; }
.dbmap .nd-sync rect { fill: #e3f2fa; stroke: #a8d8ef; }
.dbmap .nd-table rect { fill: var(--console); stroke: var(--console); }
.dbmap .nd-page rect { fill: #fff; stroke: #c8d1d9; }
.dbmap .nd text { font-family: var(--font-mono); pointer-events: none; }
.dbmap .tt { font-size: 11px; font-weight: 600; fill: var(--ink); }
.dbmap .ss { font-size: 9.5px; fill: var(--muted); }
.dbmap .nd-table .tt { fill: #fff; }
.dbmap .lk { font-size: 11px; fill: var(--accent-deep); }
.dbmap .nd[data-url], .dbmap .nd[data-route] { cursor: pointer; }
.dbmap .nd[data-url]:hover rect, .dbmap .nd[data-route]:hover rect { stroke: var(--accent); }
.dbmap.dim .eg:not(.hl) { opacity: .14; }
.dbmap.dim .nd:not(.hl) { opacity: .28; }
.dbmap .eg.hl { stroke: var(--accent-deep); stroke-width: 2.2; }
.dbmap .eg.write.hl { stroke: var(--amber); }
.dbmap .eg.writeback.hl { stroke: var(--violet); stroke-width: 2.2; }
.dbmap-legend {
  display: flex; gap: 16px; flex-wrap: wrap; margin: 8px 0 10px;
  font-family: var(--font-mono); font-size: .64rem; color: var(--muted);
}
.dbmap-legend .sw {
  display: inline-block; width: 12px; height: 12px; border-radius: 3px;
  vertical-align: -2px; margin-right: 5px; border: 1px solid transparent;
}
.sw-sheet { background: #e0f2ea; border-color: #9fd4bb; }
.sw-sync { background: #e3f2fa; border-color: #a8d8ef; }
.sw-table { background: var(--console); }
.sw-page { background: #fff; border-color: #c8d1d9; }
.sw-write { background: transparent; border: 0; border-top: 2px dashed var(--amber); height: 0; margin-top: 5px; }
.sw-writeback { background: transparent; border: 0; border-top: 2px dashed var(--violet); height: 0; margin-top: 5px; }

/* ---------- List on eBay modal (Phase 11) ---------- */
.listing-cats { display: flex; flex-direction: column; gap: 6px; margin-bottom: 8px; }
.listing-cat {
  display: flex; align-items: baseline; gap: 8px; padding: 7px 10px;
  border: 1px solid var(--line); border-radius: var(--r-ctl); cursor: pointer; font-size: .85rem;
}
.listing-cat:hover { border-color: var(--accent); }
.listing-cat.active { border-color: var(--accent); background: var(--line-soft); }
.listing-cat input[type="radio"] { margin: 0; }
.listing-cat-name { font-weight: 600; }
.listing-cat-path { font-size: .74rem; }
.listing-photos { display: flex; flex-wrap: wrap; gap: 8px; }
.listing-photos img {
  width: 64px; height: 64px; object-fit: cover; border-radius: var(--r-ctl); border: 1px solid var(--line);
  background: var(--line-soft);
}

/* ---------- Phase 11-B — listing profile / SKU / condition breakdown / photos ---------- */
/* Matched item's profile shown read-only (the tier dropdown is the real control). */
.listing-profile-name {
  padding: 8px 10px; border: 1px solid var(--line); border-radius: var(--r-ctl);
  background: var(--line-soft); color: var(--ink); font-family: var(--font-mono); font-size: .8rem;
}
.listing-sku-chip {
  margin-left: 8px; padding: 1px 7px; border-radius: 999px; font-family: var(--font-mono);
  font-size: .66rem; font-variant-numeric: tabular-nums; background: var(--accent-wash);
  color: var(--accent-deep); border: 1px solid var(--accent);
}

/* eBay market-price lookup row (under Price) — median comps to price the item. */
.market-row {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 8px;
  margin: -2px 0 12px; padding: 7px 10px; border: 1px solid var(--line);
  border-radius: var(--r-ctl); background: var(--line-soft);
}
.market-fig { font-size: .82rem; color: var(--ink); }
.market-fig b { font-family: var(--font-mono); font-variant-numeric: tabular-nums; }
.market-actions { display: flex; gap: 4px; }
.market-actions button { padding: 4px 10px; font-size: .78rem; }

/* Condition breakdown editor — a grouped fieldset so it reads as one unit that
   feeds the description's "Detailed Condition" block. */
.cond-break {
  border: 1px solid var(--line); border-radius: var(--r-card); padding: 12px 14px 6px; margin: 14px 0;
}
.cond-break > legend {
  font-family: var(--font-mono); font-size: .66rem; font-weight: 600; text-transform: uppercase;
  letter-spacing: .12em; color: var(--muted); padding: 0 6px;
}
.cond-break > .hint { margin: 0 0 10px; }
.cond-row { display: grid; grid-template-columns: 150px 118px 1fr; gap: 8px; align-items: center; margin-bottom: 8px; }
.cond-row-includes { grid-template-columns: 150px 1fr; }
.cond-row-label { font-family: var(--font-mono); font-size: .72rem; color: var(--ink); }
.cond-row select, .cond-row input { width: 100%; }
/* Insurance Quality locks the breakdown (no damage allowed). */
.cond-row-locked { opacity: .45; }
.cond-row-locked select:disabled, .cond-row-locked input:disabled { cursor: not-allowed; }
@media (max-width: 640px) {
  .cond-row, .cond-row-includes { grid-template-columns: 1fr; gap: 5px; }
  .cond-row-label { color: var(--muted); font-size: .68rem; }
}

/* Photo manager — the eBay gallery, first tile = primary. */
.photo-mgr-grid { display: flex; flex-wrap: wrap; gap: 10px; }
.photo-tile {
  position: relative; width: 84px; height: 84px; margin: 0; border: 1px solid var(--line);
  border-radius: var(--r-ctl); overflow: hidden; background: var(--line-soft); cursor: grab;
}
.photo-tile img { width: 100%; height: 100%; object-fit: cover; display: block; }
.photo-tile.is-primary { border-color: var(--accent); box-shadow: 0 0 0 1px var(--accent); }
.photo-primary-tag {
  position: absolute; left: 0; bottom: 0; right: 0; padding: 1px 0; text-align: center;
  background: var(--accent); color: var(--black); font-family: var(--font-mono);
  font-size: .56rem; letter-spacing: .08em; text-transform: uppercase;
}
.photo-del {
  position: absolute; top: 2px; right: 2px; width: 20px; height: 20px; padding: 0; line-height: 1;
  border-radius: 999px; border: 1px solid var(--line); background: var(--paper); color: var(--ink);
  font-size: .9rem; opacity: 0; transition: opacity .15s ease;
}
.photo-tile:hover .photo-del, .photo-del:focus-visible { opacity: 1; }
.photo-del:hover { border-color: var(--red); color: var(--red); }
.photo-add {
  width: 84px; height: 84px; display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 3px; border: 1px dashed var(--line); border-radius: var(--r-ctl); background: transparent;
  color: var(--muted); font-size: .66rem; font-family: var(--font-mono);
}
.photo-add:hover:not(:disabled) { border-color: var(--accent); color: var(--accent-deep); background: var(--accent-wash); }
.photo-add-plus { font-size: 1.3rem; line-height: 1; font-weight: 400; }

/* Live description preview — a real eBay template render (its own white canvas,
   sandboxed, no scripts). */
.preview-toggle { padding-left: 0; }
.listing-preview {
  width: 100%; height: 640px; margin-top: 8px; border: 1px solid var(--line);
  border-radius: var(--r-card); background: #fff;
}

/* ---------- Misc ---------- */
.muted { color: var(--muted); }
.error-text { color: var(--red); font-size: .85rem; margin-top: 10px; min-height: 1.2em; }
.empty { padding: 34px; text-align: center; color: var(--muted); }
.pager {
  display: flex; gap: 8px; align-items: center; justify-content: flex-end;
  padding: 10px 0; font-size: .78rem; font-family: var(--font-mono); color: var(--muted);
}
.pager button { font-size: .78rem; padding: 5px 11px; }
.pager input.page-jump {
  width: 3.4em; text-align: center; padding: 3px 4px;
  font-family: var(--font-mono); font-size: .78rem;
}
.kv { display: grid; grid-template-columns: 165px 1fr; gap: 7px 14px; font-size: .88rem; }
.kv dt {
  color: var(--muted); font-family: var(--font-mono); font-size: .72rem;
  text-transform: uppercase; letter-spacing: .07em; padding-top: 2px;
}
.kv dd { margin: 0; }
.item-row { display: grid; grid-template-columns: 1fr 140px 40px; gap: 10px; margin-bottom: 8px; align-items: center; }

/* ---------- Responsive ---------- */
@media (max-width: 1100px) {
  .detail-grid { grid-template-columns: 260px 1fr; }
  .detail-grid .part-media, .detail-grid .item-media { grid-row: span 2; }
}
@media (max-width: 900px) {
  /* header bar + full-width drawer replace the desktop hover-rail (desktop-only
     logic stays behind wireSidebar()'s own min-width: 901px guard) */
  .shell { flex-direction: column; padding-left: 0; }
  .mobile-bar { display: flex; }
  .side {
    width: 100%; height: auto; position: fixed; top: 56px; left: 0; right: 0; bottom: 0;
    z-index: 39; overflow-y: auto; box-shadow: 0 14px 34px rgba(0, 0, 0, .35);
    flex-direction: column; transform: translateY(-10px); opacity: 0; pointer-events: none;
    transition: opacity .16s ease, transform .16s ease;
  }
  .side.open { transform: translateY(0); opacity: 1; pointer-events: auto; }
  .side.compact { width: 100%; } /* compact never applies on mobile, but guard against a stale class on resize */
  .side .side-logo { display: none; }
  .detail-grid { grid-template-columns: 1fr; }
  .grid.c2, .grid.c3, .grid.c4 { grid-template-columns: 1fr 1fr; }
  /* Backstop: give the content column a hard horizontal boundary so a genuinely
     wide child (the Databases schematic, or a wide table on a tablet) scrolls
     INSIDE its own overflow box instead of leaking its min-content width out to
     the flex column and stretching the whole page sideways. `clip` (not hidden)
     avoids turning main into a vertical scroll container. Inner scroll boxes
     (.table-wrap / shadcn overflow-auto / .dbmap) keep their own x-scroll. */
  main { overflow-x: clip; max-width: 100%; }
  .view-inner { padding: 16px 12px 50px; }
  .kv { grid-template-columns: 130px 1fr; }
  .page-title { padding: 12px 14px; }
  .page-title h1 { font-size: 1.05rem; }
  td.t { min-width: 200px; }
  .stat-groups { grid-template-columns: 1fr 1fr; }
}
@media (max-width: 640px) {
  .stat-groups { grid-template-columns: 1fr; gap: 8px; }
  .sg-head { cursor: pointer; padding: 9px 12px; }
  .sg-chips { display: flex; align-items: center; }
  .sg-head .caret { display: block; }
  .stat-group.open .sg-head .caret { transform: rotate(180deg); }
  .sg-body { grid-template-rows: 0fr; }
  .stat-group.open .sg-body { grid-template-rows: 1fr; }
  /* .sg-in's own padding sits inside the border-box, so it puts a floor under
     how far grid-template-rows: 0fr can actually shrink it — a bottom-padded
     box can't render shorter than its padding, no matter what the row track
     computes. Move the padding onto the group itself (collapses fine) and
     only add it back to .sg-in once the group is open. */
  .sg-in { grid-template-columns: repeat(auto-fit, minmax(110px, 1fr)); padding: 0 12px; }
  .stat-group.open .sg-in { padding: 4px 12px 12px; }
  .chart-head { grid-template-columns: 1fr; justify-items: center; gap: 8px; }
  .chart-head h2, .chart-head .seg-left, .chart-head .seg-right { grid-column: 1; grid-row: auto; justify-self: center; }
}
@media (max-width: 480px) {
  .grid.c2, .grid.c3, .grid.c4 { grid-template-columns: 1fr; }
}
@media (prefers-reduced-motion: reduce) {
  .login-beam { animation: none; }
  /* The rail resize is functional feedback, not decoration, so keep a short,
     eased width slide even under reduced motion (product decision) — just drop
     the longer curve + the shadow flourish. Opacity label fades are
     vestibular-safe and stay on (see .nav-label). */
  .side { transition: width .12s ease; }
}

/* ---------- Stage 3: React dashboard stat groups (append-only) ---------- */
/* The classic dashboard keeps groups always-open on desktop via the base
   .sg-body rule and only collapses at ≤640px. The React dashboard opts into
   collapse at every width with .sg-collapsible, so these rules (which mirror the
   ≤640 block) never touch the classic UI, which doesn't emit that class. */
.sg-collapsible .sg-head { cursor: pointer; }
.sg-collapsible .sg-head .caret { display: block; }
.sg-collapsible.open .sg-head .caret { transform: rotate(180deg); }
.sg-collapsible .sg-chips { display: flex; align-items: center; }
.sg-collapsible .sg-body { grid-template-rows: 0fr; }
.sg-collapsible.open .sg-body { grid-template-rows: 1fr; }
/* Fixed 2-column card grid so the 4-card Inventory group reads as 2×2 (Warehouse
   sits on the same row as Ready to list) instead of the width-dependent 3+1 that
   `repeat(auto-fit, minmax(120px,1fr))` produced on wide screens. 2-card groups
   (Sales/Operations) stay a single row of two. */
.sg-collapsible .sg-in { padding: 0 12px; grid-template-columns: repeat(2, minmax(0, 1fr)); }
.sg-collapsible.open .sg-in { padding: 4px 12px 12px; }
.sg-collapsible .sg-body { transition: grid-template-rows .18s ease; }
/* Quiet group-level hover cue (plan §5.2): border + eyebrow tint toward the
   brand cyan only — no shadow, no transform — so the per-card hover stays the
   real click affordance. One class, trivial to drop if it reads as noisy. */
.sg-hovertint:hover { border-color: var(--accent); }
.sg-hovertint:hover .sg-label { color: var(--accent-deep); }

/* ==========================================================================
   Stage 8.4 — Dark mode (append-only, fully gated behind `.dark` on <html>).
   Light mode is untouched: none of these rules match without the class.

   The app is token-based, so re-pointing the base tokens flips ~every surface
   at once — cards/panels/buttons/borders/text (var(--paper)/--bg/--ink/--line)
   AND the shadcn components (their --ui-* tokens reference these). The console
   chrome (sidebar, page-title, toasts) is ALREADY dark and stays as-is. The
   rules after the token block patch only the literal (non-token) colors that
   can't follow a token: inputs, the modal, table header/hover, hub tints, and
   the badge tints. Brand stays: cyan #29ABE2 primary, status lamps (brightened
   for dark contrast), IBM Plex, console-black character.
   ========================================================================== */
.dark {
  color-scheme: dark;
  --ink: #e7e9ec;
  --muted: #939ca6;
  --line: #313741;
  --line-soft: #23272d;
  --bg: #0c0d10;
  --paper: #16181d;
  /* brand cyan primary stays; deep/strong brighten so links + hovers read on dark */
  --accent-strong: #4bb6e8;
  --accent-deep: #55b7e6;
  /* status lamps brightened for contrast on dark surfaces */
  --green: #46b985;
  --amber: #d7a23f;
  --red: #e1746b;
  --violet: #ad95d7;
}
/* sidebar/mobile-bar are near-black like the page now — add a hairline so they
   don't melt into the content. */
.dark .side { border-right: 1px solid var(--line); }
.dark .mobile-bar { border-bottom: 1px solid var(--line); }
/* paper-surface literals that don't use tokens */
.dark input, .dark select, .dark textarea { background: #1b1e24; border-color: var(--line); }
.dark .modal { background: var(--glass-bg); }
.dark th { background: linear-gradient(180deg, #1b1e24, #171a1f); }
.dark tfoot td { background: #1a1d23; }
.dark tbody tr:hover { background: rgba(41, 171, 226, .08); box-shadow: inset 2px 0 0 var(--accent); }
.dark button.danger { background: rgba(225, 116, 107, .14); border-color: rgba(225, 116, 107, .32); color: var(--red); }
.dark .stat.as-btn:hover { background: rgba(255, 255, 255, .03); border-color: var(--line); }
.dark .stat.as-btn.selected { background: rgba(41, 171, 226, .1); }
.dark .hit { background: #1a1d23; }
.dark .hit:hover, .dark .picked { background: rgba(41, 171, 226, .08); }
/* status badges: one subtle translucent chip (the tinted light bgs would glare);
   colour still reads via the brightened text + lamp dot */
.dark .badge:not([class*="role-"]):not(.site) { background: rgba(255, 255, 255, .06); }
.dark .badge.Warehouse, .dark .badge.Quote { color: #9aa3ad; }
.dark .badge.Ready, .dark .badge.Delivered { color: #82ba60; }
.dark .badge.role-admin { background: transparent; color: var(--ink); border-color: var(--line); }
.dark .badge.role-va { background: rgba(85, 183, 230, .14); color: var(--accent-deep); }
.dark .badge.role-warehouse { background: rgba(215, 162, 63, .14); color: var(--amber); }
.dark .badge.site { border-color: var(--line); }
/* Databases map (admin): the sheet/sync/page node fills are pale pastels tuned
   for white paper — on dark they'd glare like light switches left on. Darken
   the fills, keep the same hues + brightened strokes so the sheet/sync/page/
   table legend is still readable at a glance. */
.dark .dbmap .col-h { fill: #6d7680; }
.dark .dbmap .eg { stroke: #3a4048; }
.dark .dbmap .nd-sheet rect { fill: #16261f; stroke: #3d7a58; }
.dark .dbmap .nd-sync rect { fill: #142530; stroke: #3f7fa3; }
.dark .dbmap .nd-page rect { fill: var(--paper); stroke: var(--line); }
.dark .dbmap .nd-page .tt { fill: var(--ink); }
.dark .dbmap-legend .sw-sheet { background: #16261f; border-color: #3d7a58; }
.dark .dbmap-legend .sw-sync { background: #142530; border-color: #3f7fa3; }
.dark .dbmap-legend .sw-page { background: var(--paper); border-color: var(--line); }

/* ==========================================================================
   Stage 8 addendum B — compact stat "instrument cluster" (append-only).

   The collapsed-group chips become labeled mono micro-readouts (a small
   uppercase label over the value) instead of bare dot-separated numbers, so
   the group head reads as one instrument cluster in the brand's own vernacular.
   React-only: the dashboard emits .sg-chip-l/.sg-chip-n; classic never does, so
   these rules can't touch it. Dark mode is free — every colour is a token that
   re-points under `.dark`. Overrides the earlier .sg-chip/.sg-sep rules by
   source order (unlayered, equal specificity, declared later).
   ========================================================================== */
.sg-chips { flex-wrap: wrap; gap: 3px 14px; }
/* The cluster is a COLLAPSED-only readout: hide it in the expanded head (it
   duplicated the cards below). Expanded head is then just label + caret. The
   two-class .open selector beats `.sg-collapsible .sg-chips { display:flex }`. */
.sg-collapsible.open .sg-chips { display: none; }
/* Collapsed statbox: lay the readouts out on the SAME 2-column grid as the
   expanded cards (.sg-in, below), so the arrangement is identical in both views
   and never depends on how the chips happen to wrap. A 4-stat group (Inventory /
   Operations) reads as 2×2 and a 2-stat group (Sales) as 2×1 (one row of two) —
   never a lone vertical 1×4 or a ragged 3+1 — at every screen size. The chip
   block still claims the space between the label and the caret (flex:1), and the
   two columns (minmax(0,1fr)) stretch to fill it. */
.sg-collapsible .sg-chips { flex: 1 1 auto; min-width: 0; display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px 10px; }
.sg-collapsible .sg-chip { align-items: center; text-align: center; }
.sg-chip {
  display: flex; flex-direction: column; align-items: flex-start; gap: 1px;
  padding: 1px 2px; line-height: 1.08;
}
.sg-chip-l {
  font-family: var(--font-mono); font-size: .58rem; font-weight: 600;
  text-transform: uppercase; letter-spacing: .09em; color: var(--muted);
}
.sg-chip-n {
  font-family: var(--font-mono); font-weight: 600; font-size: .95rem; color: var(--ink);
  font-variant-numeric: tabular-nums; border-bottom: 2px solid transparent;
}
.sg-chip:hover .sg-chip-l { color: var(--accent-deep); }
.sg-chip:hover .sg-chip-n { color: var(--accent-deep); }
/* selected "gauge": accent-deep value + a 2px accent underline under the number */
.sg-chip.selected .sg-chip-n { color: var(--accent-deep); border-bottom-color: var(--accent); }
.sg-chip:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 3px; }

/* ==========================================================================
   Stage 8.5 — Recharts chart theming (append-only).

   The dashboard's charts render via components/ui/bar-chart.jsx (Recharts).
   Bars carry HTTL's brand cyan from JS props (theme-invariant); everything
   themeable (axes, grid, tick text, the hover cursor) is a token here, so it
   follows light/dark for free. The instant-tooltip guarantee needs the wrapper
   transition killed. React-only classes — classic never emits them.
   ========================================================================== */
.chart-rc { width: 100%; }
.chart-rc svg { max-width: 100%; overflow: visible; }
.chart-rc .recharts-cartesian-grid line { stroke: var(--line-soft); }
.chart-rc .recharts-cartesian-axis-tick-value { fill: var(--muted); font-family: var(--font-mono); font-size: 10px; }
.chart-rc .recharts-cartesian-axis-line, .chart-rc .recharts-cartesian-axis-tick-line { stroke: var(--line); }
.chart-rc .chart-rc-vlabel { fill: var(--ink); font-family: var(--font-mono); font-size: 10px; font-weight: 600; }
.chart-rc .recharts-tooltip-cursor { fill: rgba(41, 171, 226, .10); }
/* keyboard/accessibility focus on the chart surface */
.chart-rc .recharts-surface:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
/* INSTANT tooltip: no position/opacity transition on the Recharts wrapper (plan §5.1) */
.recharts-tooltip-wrapper { transition: none !important; pointer-events: none; z-index: 5; }
.chart-rc-tip {
  background: var(--black); color: #fff; padding: 6px 10px; border-radius: 4px;
  font-family: var(--font-mono); font-size: .72rem; white-space: nowrap;
  box-shadow: 0 6px 18px rgba(0, 0, 0, .3); transform: translateY(-8px);
}
@media (max-width: 640px) { .chart-rc .chart-rc-vlabel { display: none; } }

/* ==========================================================================
   Stacked Sales-by-platform palette (append-only). Six CVD-validated
   categorical hues (dataviz skill; validated against the app's own light
   #ffffff / dark #16181d surfaces), one per platform an item can sell on.
   Both modes are SELECTED steps, not an auto-flip — the dark column is the same
   hues re-stepped for the dark surface. Bars reference these via CSS class
   (components/ui/stacked-bar-chart.jsx) so the fill follows the theme token with
   no re-render; the matching legend/tooltip swatches use the same vars.
   Stack + legend order (bottom→top): eBay, PT, CCC, Orlando, Local pickup, Other.
   ========================================================================== */
:root {
  --plat-ebay: #2a78d6;    /* blue   */
  --plat-pt: #1baf7a;      /* aqua   */
  --plat-ccc: #eda100;     /* yellow */
  --plat-orlando: #4a3aa7; /* violet */
  --plat-local: #008300;   /* green  */
  --plat-other: #e34948;   /* red    */
}
.dark {
  --plat-ebay: #3987e5;
  --plat-pt: #199e70;
  --plat-ccc: #c98500;
  --plat-orlando: #9085e9;
  --plat-local: #008300;
  --plat-other: #e66767;
}
/* Category palette (item-category + lost-reason charts) — TEN slots so a series
   with up to 9 distinct values (LOST_REASONS has 8 + a derived "Unspecified")
   never wraps and collides. cat-0..5 alias the six platform hues above for
   continuity; cat-6..9 are four NEW hues chosen (ui-ux-pro-max skill, 2026-07-16)
   to fill the remaining hue-wheel gaps and stay distinguishable under CVD:
   magenta (fills the violet→red gap, differs from both mainly in blue channel),
   brown (low-saturation warm neutral — CVD-safe by design, distinct from yellow
   by darkness not hue), cyan (fills the teal→blue gap, pushed lighter than both
   neighbours so the ACHROMATIC channel carries the distinction, which CVD
   doesn't affect), and a true neutral slate gray (carries no chromatic info at
   all, so it's distinguishable from every hue under every CVD type — the
   catch-all 10th slot). Distinguishability also rides on non-color redundancy
   already in place: the 2px stroke separator between stacked segments, the
   always-visible legend text, and the tooltip's exact value + label. */
:root {
  --cat-0: var(--plat-ebay); --cat-1: var(--plat-pt); --cat-2: var(--plat-ccc);
  --cat-3: var(--plat-orlando); --cat-4: var(--plat-local); --cat-5: var(--plat-other);
  --cat-6: #b83e93; /* magenta */
  --cat-7: #8a5a2f; /* brown   */
  --cat-8: #1797ad; /* cyan    */
  --cat-9: #6b7280; /* gray    */
}
.dark {
  --cat-0: var(--plat-ebay); --cat-1: var(--plat-pt); --cat-2: var(--plat-ccc);
  --cat-3: var(--plat-orlando); --cat-4: var(--plat-local); --cat-5: var(--plat-other);
  --cat-6: #d456ac;
  --cat-7: #b8834f;
  --cat-8: #28b8d1;
  --cat-9: #9aa0ab;
}
/* Bar fills — Recharts applies the Bar's className to the bar <g> layer, so the
   rectangles are its descendants; the first selector also covers the case where
   the class lands on the path itself. A CSS `fill` beats Recharts' own fill
   presentation attribute regardless of specificity. */
.chart-rc.stacked .plat-ebay,    .chart-rc.stacked .plat-ebay .recharts-rectangle    { fill: var(--plat-ebay); }
.chart-rc.stacked .plat-pt,      .chart-rc.stacked .plat-pt .recharts-rectangle      { fill: var(--plat-pt); }
.chart-rc.stacked .plat-ccc,     .chart-rc.stacked .plat-ccc .recharts-rectangle     { fill: var(--plat-ccc); }
.chart-rc.stacked .plat-orlando, .chart-rc.stacked .plat-orlando .recharts-rectangle { fill: var(--plat-orlando); }
.chart-rc.stacked .plat-local,   .chart-rc.stacked .plat-local .recharts-rectangle   { fill: var(--plat-local); }
.chart-rc.stacked .plat-other,   .chart-rc.stacked .plat-other .recharts-rectangle   { fill: var(--plat-other); }
/* 2px surface-colour gap between touching stacked segments (the dataviz stacked
   spacer + the palette's required secondary encoding). A paper-coloured stroke
   straddles each segment edge → ~2px of surface between neighbours. */
.chart-rc.stacked .recharts-rectangle { stroke: var(--paper); stroke-width: 2px; }

/* Legend under the stacked chart — identity never rides colour alone. */
.chart-legend {
  display: flex; flex-wrap: wrap; justify-content: center; gap: 6px 16px; margin-top: 12px;
  font-family: var(--font-mono); font-size: .64rem; color: var(--muted);
  text-transform: uppercase; letter-spacing: .07em;
}
.chart-legend-item { display: inline-flex; align-items: center; gap: 6px; }
.chart-rc-sw { width: 10px; height: 10px; border-radius: 2px; flex: none; display: inline-block; }
.chart-rc-sw.plat-ebay { background: var(--plat-ebay); }
.chart-rc-sw.plat-pt { background: var(--plat-pt); }
.chart-rc-sw.plat-ccc { background: var(--plat-ccc); }
.chart-rc-sw.plat-orlando { background: var(--plat-orlando); }
.chart-rc-sw.plat-local { background: var(--plat-local); }
.chart-rc-sw.plat-other { background: var(--plat-other); }

/* Category-keyed hues — shared by the grouped "by category" chart
   (components/ui/grouped-bar-chart.jsx: x = platform, one bar per item CATEGORY),
   the day-stacked "by category" chart (components/ui/stacked-category-bar-chart.jsx:
   x = day, one stacked segment per CATEGORY, on the eBay / Other charts AND the
   Lost Sales per-day-by-reason chart), keyed cat-0..9 by series order (both
   components cycle at 10 — see the --cat-N token block above for the palette
   design). Bar fills are scoped under .chart-rc.grouped / .chart-rc.cat-stacked;
   swatch classes are global because the tooltip is portaled outside the chart. */
.chart-rc.grouped .cat-0, .chart-rc.grouped .cat-0 .recharts-rectangle,
.chart-rc.cat-stacked .cat-0, .chart-rc.cat-stacked .cat-0 .recharts-rectangle { fill: var(--cat-0); }
.chart-rc.grouped .cat-1, .chart-rc.grouped .cat-1 .recharts-rectangle,
.chart-rc.cat-stacked .cat-1, .chart-rc.cat-stacked .cat-1 .recharts-rectangle { fill: var(--cat-1); }
.chart-rc.grouped .cat-2, .chart-rc.grouped .cat-2 .recharts-rectangle,
.chart-rc.cat-stacked .cat-2, .chart-rc.cat-stacked .cat-2 .recharts-rectangle { fill: var(--cat-2); }
.chart-rc.grouped .cat-3, .chart-rc.grouped .cat-3 .recharts-rectangle,
.chart-rc.cat-stacked .cat-3, .chart-rc.cat-stacked .cat-3 .recharts-rectangle { fill: var(--cat-3); }
.chart-rc.grouped .cat-4, .chart-rc.grouped .cat-4 .recharts-rectangle,
.chart-rc.cat-stacked .cat-4, .chart-rc.cat-stacked .cat-4 .recharts-rectangle { fill: var(--cat-4); }
.chart-rc.grouped .cat-5, .chart-rc.grouped .cat-5 .recharts-rectangle,
.chart-rc.cat-stacked .cat-5, .chart-rc.cat-stacked .cat-5 .recharts-rectangle { fill: var(--cat-5); }
.chart-rc.grouped .cat-6, .chart-rc.grouped .cat-6 .recharts-rectangle,
.chart-rc.cat-stacked .cat-6, .chart-rc.cat-stacked .cat-6 .recharts-rectangle { fill: var(--cat-6); }
.chart-rc.grouped .cat-7, .chart-rc.grouped .cat-7 .recharts-rectangle,
.chart-rc.cat-stacked .cat-7, .chart-rc.cat-stacked .cat-7 .recharts-rectangle { fill: var(--cat-7); }
.chart-rc.grouped .cat-8, .chart-rc.grouped .cat-8 .recharts-rectangle,
.chart-rc.cat-stacked .cat-8, .chart-rc.cat-stacked .cat-8 .recharts-rectangle { fill: var(--cat-8); }
.chart-rc.grouped .cat-9, .chart-rc.grouped .cat-9 .recharts-rectangle,
.chart-rc.cat-stacked .cat-9, .chart-rc.cat-stacked .cat-9 .recharts-rectangle { fill: var(--cat-9); }
.chart-rc-sw.cat-0 { background: var(--cat-0); }
.chart-rc-sw.cat-1 { background: var(--cat-1); }
.chart-rc-sw.cat-2 { background: var(--cat-2); }
.chart-rc-sw.cat-3 { background: var(--cat-3); }
.chart-rc-sw.cat-4 { background: var(--cat-4); }
.chart-rc-sw.cat-5 { background: var(--cat-5); }
.chart-rc-sw.cat-6 { background: var(--cat-6); }
.chart-rc-sw.cat-7 { background: var(--cat-7); }
.chart-rc-sw.cat-8 { background: var(--cat-8); }
.chart-rc-sw.cat-9 { background: var(--cat-9); }

/* Stacked tooltip: the day, each platform (largest first) + swatch, then total.
   Reuses the console-black .chart-rc-tip pill (dark in both themes). */
.chart-rc-tip.stacked { display: flex; flex-direction: column; gap: 3px; min-width: 150px; }
.chart-rc-tip-day { font-weight: 700; margin-bottom: 3px; }
.chart-rc-tip-row { display: flex; align-items: center; gap: 8px; }
.chart-rc-tip-row .chart-rc-tip-l { flex: 1; }
.chart-rc-tip-v { font-variant-numeric: tabular-nums; }
.chart-rc-tip-row.total { margin-top: 3px; padding-top: 4px; border-top: 1px solid rgba(255, 255, 255, .18); font-weight: 700; }

/* ==========================================================================
   Profile photo — Settings uploader (append-only). The nav-bar avatar itself
   is styled with Tailwind utilities in UserMenu.jsx; these rules are only the
   Settings edit control. Tokens → dark mode is free.
   ========================================================================== */
.avatar-edit { display: flex; align-items: center; gap: 16px; margin-bottom: 16px; }
.avatar-preview {
  width: 72px; height: 72px; flex: none; border-radius: var(--r-ctl); overflow: hidden;
  display: grid; place-items: center; background: rgba(41, 171, 226, .14);
  border: 1px solid var(--line);
  font-family: var(--font-mono); font-weight: 600; font-size: 1.3rem; color: var(--accent-deep);
}
.avatar-preview img { width: 100%; height: 100%; object-fit: cover; }
.avatar-controls { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
.avatar-controls .muted { flex-basis: 100%; margin: 2px 0 0; font-size: .74rem; }
.btn-file {
  display: inline-flex; align-items: center; cursor: pointer;
  padding: 7px 14px; border-radius: var(--r-ctl); border: 1px solid var(--line);
  background: var(--paper); color: var(--ink); font-size: .82rem; font-weight: 600;
}
.btn-file:hover { border-color: var(--accent); color: var(--accent-deep); }
.btn-file.is-busy { opacity: .6; cursor: default; }

/* ==========================================================================
   Dashboard console weather (append-only). Replaces the old .grow spacer in the
   dash-title, so it keeps flex:1 (clock stays right) and centers the reading in
   the middle space. Console bar is always dark, so colours are literal like the
   clock's, not tokens.
   ========================================================================== */
.dash-weather {
  flex: 1; min-width: 0; align-self: center;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 1px; line-height: 1.2; text-align: center;
}
.dash-weather .wx-loc {
  font-family: var(--font-mono); font-size: .66rem; color: #cbd4db;
  text-transform: uppercase; letter-spacing: .1em;
  max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.dash-weather .wx-temp {
  display: inline-flex; align-items: center; gap: 6px;
  font-family: var(--font-mono); font-weight: 600; font-size: 1.05rem; color: #fff;
  font-variant-numeric: tabular-nums;
}
.dash-weather .wx-icon { width: 18px; height: 18px; flex: none; color: var(--accent); }
.dash-weather .wx-desc {
  font-family: var(--font-mono); font-size: .62rem; color: #8b95a0;
  text-transform: uppercase; letter-spacing: .1em;
}
/* No room to center a 3-line readout on small screens — hide the text but keep
   the element as the flex spacer. */
@media (max-width: 720px) { .dash-weather > * { display: none; } }

/* ==========================================================================
   Call Hub — mock calling UI (append-only, Phase 2 of CALLS_RETURNS_
   IMPLEMENTATION_PLAN.md). Everything here is token-based so dark mode is
   free. CallDock renders null when the `calls` flag is off (see
   calls/CallDock.jsx) — these rules are simply unused in that mode, never a
   visual regression on the rest of the app.
   ========================================================================== */
/* Bottom-right corner dock (moved off right-center in the Console v3 revamp,
   W10 — the old position covered table cells on every list page). The FAB
   sits in the corner, the dialer panel opens to its LEFT over dead space
   (flex-direction row-reverse, FAB last) — the Call Hub "Log a call" form
   (left column) is still never covered. */
.call-dock {
  position: fixed; right: 16px; bottom: 16px; top: auto; transform: none; z-index: 50;
  display: flex; flex-direction: row-reverse; align-items: flex-end; gap: 12px;
}
/* Round phone FAB — the persistent RingCentral access point. Brand cyan face,
   dark glyph, status pip in the corner. Literal brand hex (not var(--accent))
   and .call-dock-scoped so it wins decisively over the base `button` rule in
   both themes — the same theme-invariant brand approach the charts use. */
.call-dock .call-dock-fab {
  position: relative; flex: none; width: 52px; height: 52px; padding: 0; border-radius: 50%;
  display: grid; place-items: center; cursor: pointer;
  background: #29abe2; border: 1px solid #189bd3; color: #0a0a0b;
  box-shadow: 0 12px 30px rgba(0, 0, 0, .28);
  transition: background .15s ease, transform .15s ease, box-shadow .15s ease;
}
/* the spec's glow-on-hover: kept scoped to :hover (not the base rule) per the
   §0.3(b)/§7 hard rule — glows only on active/live/selected/focus/hover, never
   ambient on idle chrome; the FAB is always visible, so an unconditional glow
   here would read as ambient. */
.call-dock .call-dock-fab:hover { background: #189bd3; transform: scale(1.04); box-shadow: 0 12px 30px rgba(0, 0, 0, .28), var(--glow-accent-soft); }
.call-dock .call-dock-fab:active { transform: scale(.98); }
.call-dock-fab-ic { width: 25px; height: 25px; display: inline-flex; }
.call-dock-fab-ic svg { width: 100%; height: 100%; }
/* live-call pulse so an active/ringing call reads even when the panel is closed */
.call-dock-fab.busy { animation: fab-pulse 1.4s ease-in-out infinite; }
@keyframes fab-pulse {
  0%, 100% { box-shadow: 0 12px 30px rgba(0, 0, 0, .28), 0 0 0 0 rgba(41, 171, 226, .5); }
  50% { box-shadow: 0 12px 30px rgba(0, 0, 0, .28), 0 0 0 10px rgba(41, 171, 226, 0); }
}
.call-dock-dot { width: 8px; height: 8px; border-radius: 50%; flex: none; }
/* status pip in the FAB's top-right corner, ringed in the FAB's own colour */
.call-dock-fab .call-dock-dot { position: absolute; top: 5px; right: 5px; width: 11px; height: 11px; box-shadow: 0 0 0 2px #29abe2; }
.call-dock-fab:hover .call-dock-dot { box-shadow: 0 0 0 2px #189bd3; }
.dot-gray { background: var(--muted); }
.dot-amber { background: var(--amber); box-shadow: 0 0 6px var(--amber); }
.dot-green { background: var(--green); box-shadow: 0 0 6px var(--green); }
.dot-red { background: var(--red); }

.call-dock-panel {
  width: min(340px, calc(100vw - 96px)); max-height: calc(100vh - 32px); overflow-y: auto;
  background: var(--paper); border: 1px solid var(--line);
  border-radius: var(--r-card); border-top: 3px solid var(--black); box-shadow: 0 24px 60px rgba(0, 0, 0, .3);
  padding: 14px 16px; display: flex; flex-direction: column; gap: 12px;
}
.dark .call-dock-panel { border-top-color: var(--accent); }
.call-dock-panel-head { display: flex; align-items: center; gap: 8px; font-size: .78rem; color: var(--muted); }
.call-dock-panel-head .ghost { padding: 2px 7px; }
.call-dock-recover { display: flex; flex-direction: column; gap: 8px; }

/* Busy-agent alert: a new inbound arrived while you're already on a call. The FAB
   shakes (kept alongside the pulse) and a toast floats above the dock — visible
   even when collapsed — instead of ringing over the active call. */
.call-dock .call-dock-fab.shake { animation: fab-shake .5s ease-in-out infinite, fab-pulse 1.4s ease-in-out infinite; }
@keyframes fab-shake {
  0%, 100% { transform: rotate(0); }
  20% { transform: rotate(-9deg); }
  60% { transform: rotate(9deg); }
}
.call-dock-alerts {
  position: absolute; right: 0; bottom: calc(100% + 10px);
  display: flex; flex-direction: column; gap: 8px; align-items: flex-end;
  width: max-content; max-width: min(300px, calc(100vw - 32px));
}
.call-dock-toast {
  display: flex; align-items: center; gap: 10px;
  background: var(--paper); border: 1px solid var(--line); border-radius: var(--r-card);
  box-shadow: 0 16px 40px rgba(0, 0, 0, .28); padding: 9px 12px;
}
.call-dock-toast.waiting { border-left: 3px solid var(--amber); }
.call-dock-toast.busy-note { border-left: 3px solid var(--red); }
.call-dock-toast-ic { width: 18px; height: 18px; color: var(--amber); flex: none; display: inline-flex; }
.call-dock-toast-ic svg { width: 100%; height: 100%; }
.call-dock-toast-txt { display: flex; flex-direction: column; line-height: 1.25; }
.call-dock-toast-txt b { font-size: .82rem; }
.call-dock-toast-txt .mono, .call-dock-toast-txt span { font-size: .72rem; color: var(--muted); }
.call-dock-toast .ghost { padding: 2px 6px; }
.call-dock-lines { font-size: .66rem; color: var(--muted); padding: 1px 7px; border: 1px solid var(--line); border-radius: 20px; }
.call-dock-lines.busy { color: var(--red); border-color: var(--red); }

/* Team line-presence strip (Call Hub): who's on a call vs. free, + capacity. */
.presence-strip { margin: 0 0 14px; padding: 10px 14px; background: var(--paper); border: 1px solid var(--line); border-radius: var(--r-card); }
.presence-strip.all-busy { border-color: var(--red); }
.presence-head { display: flex; align-items: center; gap: 10px; margin-bottom: 8px; flex-wrap: wrap; }
.presence-title { font-family: var(--font-mono); font-size: .66rem; letter-spacing: .08em; text-transform: uppercase; color: var(--muted); }
.presence-count { font-size: .78rem; font-weight: 600; padding: 1px 8px; border-radius: 20px; border: 1px solid var(--line); color: var(--muted); }
.presence-count.busy { color: var(--red); border-color: var(--red); }
.presence-warn { font-size: .74rem; color: var(--red); }
.presence-agents { display: flex; flex-wrap: wrap; gap: 8px; }
.presence-agent { display: flex; align-items: center; gap: 8px; padding: 5px 11px 5px 5px; border: 1px solid var(--line); border-radius: 999px; }
.presence-agent.on { border-color: var(--amber); }
.presence-av { width: 24px; height: 24px; border-radius: 50%; background: var(--black); color: #fff; font-size: .62rem; font-weight: 700; display: grid; place-items: center; flex: none; }
.presence-name { font-size: .8rem; }
.presence-state { display: inline-flex; align-items: center; gap: 5px; font-size: .72rem; color: var(--muted); }
.presence-dot { width: 7px; height: 7px; border-radius: 50%; flex: none; }
.presence-state.free .presence-dot { background: var(--green); box-shadow: 0 0 5px var(--green); }
.presence-state.on .presence-dot { background: var(--amber); box-shadow: 0 0 5px var(--amber); }
/* Away agents (stepped out): muted, and excluded from the "free" count server-side. */
.presence-agent.away { border-color: var(--line); opacity: .72; }
.presence-state.away { color: var(--muted); }
.presence-state.away .presence-dot { background: var(--muted); box-shadow: none; }

/* Per-agent call controls in the strip head (Available/Away, ring mute, alerts). */
.presence-spacer { flex: 1 1 auto; }
.presence-controls { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.presence-avail { display: inline-flex; border: 1px solid var(--line); border-radius: 20px; overflow: hidden; }
.presence-seg {
  font-size: .72rem; padding: 3px 11px; background: transparent; border: 0; color: var(--muted);
  cursor: pointer; transition: background-color .15s, color .15s;
}
.presence-seg:hover { color: var(--ink); }
.presence-seg.active { background: var(--green); color: #fff; }
.presence-seg.active.away { background: var(--muted); color: #fff; }
.presence-icon-btn {
  font-size: .82rem; line-height: 1; padding: 4px 7px; background: transparent;
  border: 1px solid var(--line); border-radius: 20px; cursor: pointer;
}
.presence-icon-btn.muted { opacity: .6; }
.presence-notify-btn {
  font-size: .72rem; padding: 3px 10px; background: transparent; color: var(--accent-deep);
  border: 1px solid var(--accent-deep); border-radius: 20px; cursor: pointer;
}
.presence-notify-btn:hover { background: var(--accent-deep); color: #fff; }
.presence-notify-hint { font-size: .68rem; color: var(--muted); }
.presence-notify-hint.on { color: var(--green); }

/* ---------- Dialer ---------- */
.dialer { display: flex; flex-direction: column; gap: 10px; }
.dialer-number { width: 100%; font-size: 1rem; text-align: center; }
.dialer-pad { display: grid; grid-template-columns: repeat(3, 1fr); gap: 6px; }
.dialer-pad button { font-family: var(--font-mono); font-size: .95rem; padding: 10px 0; }

/* ---------- Incoming call ---------- */
.incoming-call { display: flex; flex-direction: column; gap: 10px; }
.incoming-call-badge {
  align-self: flex-start; font-family: var(--font-mono); font-size: .62rem; font-weight: 700;
  letter-spacing: .1em; text-transform: uppercase; color: var(--green);
  padding: 3px 8px; border-radius: 3px; background: rgba(30, 138, 87, .12);
}
.incoming-call-who { display: flex; flex-direction: column; gap: 2px; font-size: .95rem; }
.incoming-call-actions { display: flex; gap: 10px; }
.incoming-call-actions button { flex: 1; }

/* ---------- Active call ---------- */
.active-call { display: flex; flex-direction: column; gap: 10px; }
.active-call-status { font-family: var(--font-mono); font-size: .68rem; letter-spacing: .08em; text-transform: uppercase; color: var(--muted); }
.active-call-who { display: flex; flex-direction: column; gap: 2px; font-size: .95rem; }
.active-call-timer { font-family: var(--font-mono); font-size: 1.4rem; font-weight: 600; text-align: center; padding: 6px 0; }
.active-call-controls { display: flex; gap: 8px; flex-wrap: wrap; }
.active-call-controls .danger { flex: 1; }
.active-call-dtmf { font-size: .72rem; }

/* ---------- Wrap-up ---------- */
.wrapup { display: flex; flex-direction: column; gap: 10px; }
.wrapup-summary { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; font-size: .85rem; }

/* ---------- Phone links (click-to-call, wherever a phone number renders) ---------- */
.phone-link {
  font: inherit; font-family: var(--font-mono); background: none; border: none; padding: 0;
  color: var(--accent-deep); cursor: pointer;
}
.phone-link:hover:not(:disabled) { text-decoration: underline; }
.phone-link:disabled { color: inherit; cursor: default; opacity: 1; }

@media (max-width: 480px) {
  .call-dock { right: 12px; bottom: 12px; gap: 10px; }
  .call-dock-panel { width: min(320px, calc(100vw - 82px)); }
  .call-dock-fab { width: 52px; height: 52px; }
}

/* ==========================================================================
   Responsive data tables (append-only). On phones a wide multi-column table
   forces the whole page to scroll sideways (a 12-col inventory table is
   ~1130px). Rather than an internal x-scroll, tables tagged `.rtable` collapse
   into stacked CARDS at ≤640px: the header row is visually hidden and each row
   becomes a bordered card whose cells are label→value lines (the label comes
   from each cell's `data-label`, set to its column header). No x-scroll, every
   field still visible. The `:has()` rule neutralises the scroll wrapper
   (shadcn's overflow-auto div OR .table-wrap) so it stops clipping/scrolling
   and just lets the cards flow. The Databases SVG map is exempt — a schematic
   that genuinely needs to scroll ("vitally necessary").
   ========================================================================== */
@media (max-width: 640px) {
  /* let the wrapper stop being a scroll box so cards flow full-width */
  .table-wrap:has(> .rtable),
  div:has(> .rtable) {
    overflow: visible; max-height: none; border: 0; background: transparent; border-radius: 0;
  }
  .rtable { display: block; width: 100%; font-size: .9rem; }
  /* Drop the header row in card mode — the column name rides each cell as its
     data-label instead (::before). display:none also sidesteps the base
     `th { position: sticky }`, which escaped a clip-based hide and forced the
     card to the header's full tabular width. */
  .rtable thead { display: none; }
  .rtable tbody { display: block; }
  .rtable tbody tr {
    display: block; background: var(--paper);
    border: 1px solid var(--line); border-radius: var(--r-card);
    margin-bottom: 10px; padding: 4px 14px;
  }
  .rtable tbody tr:hover { background: var(--paper); }
  .rtable td {
    display: flex; justify-content: space-between; align-items: baseline; gap: 14px;
    padding: 7px 0; border-bottom: 1px solid var(--line-soft);
    white-space: normal; text-align: right; max-width: none; width: auto; min-width: 0;
  }
  .rtable td:last-child { border-bottom: 0; }
  /* the column label, from data-label */
  .rtable td::before {
    content: attr(data-label); flex: 0 0 42%; text-align: left;
    font-family: var(--font-mono); font-size: .6rem; font-weight: 600;
    text-transform: uppercase; letter-spacing: .07em; color: var(--muted);
  }
  /* label-less cells (thumbnail, empty header): no label, left-aligned, full row */
  .rtable td[data-label=""]::before,
  .rtable td:not([data-label])::before { content: none; }
  .rtable td[data-label=""],
  .rtable td:not([data-label]) { justify-content: flex-start; text-align: left; }
  /* clipped/nowrap cells shouldn't force width inside a card */
  .rtable td.clip, .rtable td.clip.wide, .rtable td.mono, .rtable td .mono, .rtable td.t { white-space: normal; max-width: none; overflow: visible; }
  .rtable td.num, .rtable td.fit { width: auto; }
  /* an empty cell collapses instead of leaving a lone label line */
  .rtable td:empty { display: none; }
  /* thumbnails read a touch larger in card form */
  .rtable td.thumb { padding: 8px 0; }
  .rtable td.thumb img, .rtable td.thumb .noimg { width: 52px; height: 52px; }
  /* totals footer (OrderDetail): flatten each summary row to a label→value line */
  .rtable tfoot { display: block; margin-top: 8px; }
  .rtable tfoot tr { display: flex; justify-content: space-between; gap: 14px; padding: 6px 2px; border-top: 1px solid var(--line); }
  .rtable tfoot td { padding: 0; border: 0; background: none; text-align: right; }
  .rtable tfoot td:empty { display: none; }
  /* long unbroken values (emails, SKUs, URLs) must wrap, never force width */
  .rtable td, .kv dd { overflow-wrap: anywhere; }
  .kv { grid-template-columns: 108px 1fr; }
}

/* ===== Console v3 revamp (append-only) ===== */
:root {
  /* Signal Beam — the login-page hairline, promoted to app-wide signature */
  --beam: linear-gradient(90deg, transparent, #3a4048 18%, var(--accent) 50%, #3a4048 82%, transparent);
  --beam-strong: linear-gradient(90deg, transparent, #3a4048 12%, var(--accent) 50%, #3a4048 88%, transparent);

  /* Accent ramp — formalizes the hexes already scattered ad-hoc, so both themes stay consistent */
  --accent-wash: rgba(41, 171, 226, .08);   /* was #f0f8fd / rgba(41,171,226,.08) in ~6 places */
  --accent-tint: #e3f2fa;                    /* was #e3f2fa literal */

  /* Depth — ink-tinted (never pure black) per the design rule */
  --shadow-panel: 0 1px 2px rgba(23, 25, 28, .05);
  --shadow-panel-hover: 0 4px 14px rgba(23, 25, 28, .10);
  --shadow-float: 0 24px 60px rgba(10, 10, 11, .28);
  --panel-highlight: inset 0 1px 0 rgba(255, 255, 255, .7);

  /* Glows — used ONLY on active/live/selected/focus (never ambient) */
  --glow-accent: 0 0 0 1px rgba(41, 171, 226, .35), 0 0 14px rgba(41, 171, 226, .25);
  --glow-accent-soft: 0 0 10px rgba(41, 171, 226, .22);

  /* Glass — floating surfaces only */
  --glass-bg: rgba(255, 255, 255, .72);
  --glass-brd: rgba(255, 255, 255, .6);
  --glass-blur: blur(14px) saturate(140%);
}
.dark {
  --beam: linear-gradient(90deg, transparent, #4a525a 18%, var(--accent) 50%, #4a525a 82%, transparent);
  --beam-strong: linear-gradient(90deg, transparent, #4a525a 12%, var(--accent) 50%, #4a525a 88%, transparent);
  --accent-wash: rgba(41, 171, 226, .12);
  --accent-tint: rgba(85, 183, 230, .14);
  --shadow-panel: 0 1px 2px rgba(0, 0, 0, .40);
  --shadow-panel-hover: 0 4px 16px rgba(0, 0, 0, .55);
  --shadow-float: 0 24px 60px rgba(0, 0, 0, .55);
  --panel-highlight: inset 0 1px 0 rgba(255, 255, 255, .04);
  --glow-accent: 0 0 0 1px rgba(41, 171, 226, .45), 0 0 16px rgba(41, 171, 226, .30);
  --glass-bg: rgba(22, 24, 29, .72);
  --glass-brd: rgba(255, 255, 255, .08);
}
/* Reduced-transparency users get solid surfaces everywhere glass is used */
@media (prefers-reduced-transparency: reduce) {
  :root { --glass-bg: #ffffff; --glass-blur: none; }
  .dark { --glass-bg: #16181d; }
}

/* ---- W1 — Signal Beam + console chrome ---- */
.page-title::before {
  content: ''; position: absolute; inset: 0; pointer-events: none;
  background: linear-gradient(180deg, rgba(255,255,255,.06), transparent 42%);
}
.page-title::after {
  content: ''; position: absolute; left: 0; right: 0; bottom: 0; height: 2px;
  background: var(--beam-strong);
}
/* active nav row reads as a lit signal (NavLink sets aria-current="page" by default) */
.side a[aria-current="page"] { box-shadow: inset 3px 0 0 var(--accent), var(--glow-accent-soft); }
.login-card { box-shadow: 0 18px 60px rgba(0,0,0,.55), var(--panel-highlight); position: relative; }
.login-card::before { content:''; position:absolute; left:0; right:0; top:0; height:2px; background: var(--beam-strong); border-radius: var(--r-card) var(--r-card) 0 0; }

/* ---- W2 — Machined surfaces & depth ---- */
.panel, .table-wrap, .stat-group, .modal, .presence-strip, .call-dock-panel {
  box-shadow: var(--shadow-panel), var(--panel-highlight);
}
.stat.as-btn { transition: background .14s, border-color .14s, box-shadow .14s, transform .05s; }
.stat.as-btn:hover { box-shadow: var(--shadow-panel-hover), var(--panel-highlight); }
.stat.as-btn:active { transform: translateY(1px); }
.hit { transition: border-color .14s, background .14s, box-shadow .14s; }
.hit:hover { box-shadow: var(--shadow-panel); }
/* the bench gets a soft top light source (fixed, GPU-cheap, non-interactive) */
body::before {
  content: ''; position: fixed; inset: 0 0 auto 0; height: 480px; z-index: 0; pointer-events: none;
  background: radial-gradient(1200px 380px at 50% -140px, rgba(41,171,226,.06), transparent 70%);
}
.dark body::before { background: radial-gradient(1200px 380px at 50% -140px, rgba(41,171,226,.05), transparent 70%); }

/* ---- W4 — Kill browser-default controls ---- */
/* custom select chevron (was the native OS arrow) */
select {
  appearance: none; -webkit-appearance: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%2364707b' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right 10px center;
  padding-right: 30px;
}
/* .dark input/select/textarea (Stage 8.4, above) sets the `background` SHORTHAND,
   which resets background-repeat/position to their defaults at equal (0,2,0)
   specificity — restate them here (not just background-image) so the chevron
   doesn't tile across the control. */
.dark select {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%23939ca6' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right 10px center;
}

/* search inputs get an embedded magnifier — target the toolbar's first input and known search boxes */
.toolbar > input[type="text"], .toolbar > input:not([type]),
input[placeholder^="Search"] {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='15' height='15' viewBox='0 0 24 24' fill='none' stroke='%2397a3ac' stroke-width='2' stroke-linecap='round'%3E%3Ccircle cx='11' cy='11' r='7'/%3E%3Cpath d='M21 21l-4.3-4.3'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: 11px center; padding-left: 34px;
}

/* buttons: tactile press + smooth hover (was a flat bg swap) */
button, .btn { transition: background .12s, border-color .12s, box-shadow .12s, transform .04s; }
button:active:not(:disabled), .btn:active { transform: translateY(1px); }
button:not(.primary):not(.danger):not(.ghost):hover { border-color: #b9cbd6; }
button.primary:hover, .btn.primary:hover { box-shadow: var(--glow-accent-soft); }

/* ---- W13 — The "classy" glass + glow layer ---- */
/* 1. Floating dialer panel — frosted glass over content */
.call-dock-panel {
  background: var(--glass-bg); backdrop-filter: var(--glass-blur); -webkit-backdrop-filter: var(--glass-blur);
  border: 1px solid var(--glass-brd); box-shadow: var(--shadow-float), var(--panel-highlight);
}
/* 2. Command palette (Ctrl-K) — same frost. The visible box is actually the
   Radix DialogContent wrapping [cmdk-root] (also .bg-popover), so both need
   the override; !important needed because shadcn's .bg-popover utility ties
   at specificity (0,1,0) and loads after app.css in the built bundle. */
[cmdk-root], .command-dialog, [role="dialog"]:has([cmdk-root]) {
  background: var(--glass-bg) !important; backdrop-filter: var(--glass-blur);
}

/* 3. Sticky table header — subtle frost so rows scrolling under it read cleanly */
.table-wrap.tall th { backdrop-filter: blur(6px); }

/* 4. FAB-live state gets the same restrained glow as other live signals
   (.presence-state.free .presence-dot glow already exists above — kept as-is). */
.call-dock-fab.busy { box-shadow: 0 12px 30px rgba(0,0,0,.28), var(--glow-accent); }

/* 5. Focus rings gain a soft outer glow (accessibility PLUS classy) */
:is(button, .btn, input, select, textarea, a, summary):focus-visible {
  box-shadow: var(--glow-accent-soft);
}
/* (keep the existing 2px solid outline from app.css — the glow is additive, not a replacement) */

/* 6. Command palette search input sat flush against the dialog's rounded top
   edge; its focus outline (2px + 2px offset) plus the new glow above render
   OUTSIDE the input's border box, and the dialog/Command root are both
   overflow:hidden (to clip the scrollable results list to the rounded
   corners) — so the ring's top portion was being clipped into a hard flat
   line instead of a rounded glow. Give the input row a little headroom. */
[cmdk-input-wrapper] { padding-top: 8px; }

/* ===== Console v3 revamp — sub-phase V-B (append-only) ===== */
/* ---- W5 — Tables as instrument ledgers (additions; th/tbody hover edited above) ---- */
/* thumbnails: bigger, framed, lit on row hover */
td.thumb img, td.thumb .noimg { width: 52px; height: 52px; border: 1px solid var(--line); border-radius: 4px; }
tbody tr:hover td.thumb img, tbody tr:hover td.thumb .noimg { border-color: var(--accent); }
/* price/number cells carry a touch more weight at this density */
td.num { font-weight: 600; }

/* ---- W6 — Instrument-gauge stats (numeral size edited above) ---- */
.stat.as-btn.selected { box-shadow: var(--glow-accent), var(--panel-highlight); }

/* ===== Console v3 revamp — sub-phase V-C (append-only) ===== */
/* ---- W7 — Empty states with the lens-ring motif (used by ui/EmptyState.jsx) ---- */
.empty-state { display: flex; flex-direction: column; align-items: center; gap: 12px; padding: 56px 24px; text-align: center; }
.empty-state .rings { width: 58px; height: 58px; border-radius: 50%; border: 2px solid #c3ccd4; position: relative; opacity: .8; }
.empty-state .rings::before { content: ''; position: absolute; inset: 9px; border-radius: 50%; border: 2px solid #c3ccd4; }
.empty-state .rings::after { content: ''; position: absolute; inset: 23px; border-radius: 50%; background: #c3ccd4; }
.empty-state-title { font-family: var(--font-mono); font-size: .82rem; letter-spacing: .1em; text-transform: uppercase; color: var(--muted); }
.empty-state-hint { font-size: .82rem; color: var(--muted); max-width: 340px; line-height: 1.5; }
.dark .empty-state .rings, .dark .empty-state .rings::before { border-color: #3a4048; }
.dark .empty-state .rings::after { background: #3a4048; }

/* ---- W8 — Modals join the console (base .modal border-top/box-shadow edited above;
   .dark .modal repointed to the glass token for dark parity) ---- */
/* box-shadow restated here (not just in the base rule) because W2's machined-surface
   group `.panel, …, .modal, … { box-shadow: var(--shadow-panel)… }` is appended after
   the base .modal rule at equal specificity and would otherwise win — the modal must
   float with --shadow-float, not sit flat like a panel. */
.modal { background: var(--glass-bg); backdrop-filter: var(--glass-blur); -webkit-backdrop-filter: var(--glass-blur); border: 1px solid var(--glass-brd); box-shadow: var(--shadow-float), var(--panel-highlight); }
.modal::before { content: ''; position: absolute; left: 0; right: 0; top: 0; height: 3px; background: var(--beam-strong); border-radius: 8px 8px 0 0; }
/* modal headings get the beam like panels (applies to .modal .panel h2::after, which
   already ships content:''; bare .modal h2 has no ::after box so this is a no-op there) */
.modal h2::after { background: var(--beam); }

/* ===== Console v3 revamp — sub-phase V-D (append-only) ===== */
/* ---- W9 — Motion, restrained ---- */
@media (prefers-reduced-motion: no-preference) {
  .view-inner > * { animation: consoleIn .18s ease-out both; }
  @keyframes consoleIn { from { opacity: 0; transform: translateY(4px); } to { opacity: 1; transform: none; } }
  .panel { transition: box-shadow .14s ease; }
}

/* Lost Sales page */
.lost-stats { display: grid; grid-template-columns: repeat(4, 1fr); gap: 12px; }
@media (max-width: 640px) {
  .lost-stats { grid-template-columns: 1fr 1fr; }
}


/* ---------- Add Template (Manual) modal (Phase 21) ---------- */
/* Per-SKU resolve preview / push results. Reuses .table-wrap + the global table
   rules, so the dark theme and the sticky header come along for free; the cap
   keeps a 200-SKU paste from pushing the modal's actions off-screen. */
.tp-results { max-height: 34vh; margin-bottom: 12px; }
.tp-results td.clip { color: var(--ink); }
.tp-bad { color: var(--red); }
/* Opt-in re-push. The global `label` rule is block/uppercase/mono (built for
   field labels) — this one sits inline beside its checkbox as normal text. */
.tp-force {
  display: flex; align-items: center; gap: 7px; margin-bottom: 12px;
  font-family: inherit; font-size: .82rem; font-weight: 400; letter-spacing: 0;
  text-transform: none; color: var(--ink); cursor: pointer;
}
.tp-force input { margin: 0; }
