/* ====================================
   VOICEBASKET — DASHBOARD STYLES
   ==================================== */
*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}
:root{
  --green:    #16a34a;
  /* Brand green is #16a34a and stays #16a34a. But as TEXT it failed WCAG AA on
     47 of the 49 places it was used - 2.70-3.30:1 against a 4.5 requirement -
     because it is a mid-tone sitting on white and on its own pale tints.
     #166534 is the same hue (142.8 vs 142.1 degrees) taken darker, and clears
     4.5:1 on every measured background with margin (worst case 5.83:1 on the
     .10 tint over --bg). --green-d/#15803d was tried first and is NOT enough:
     it lands at 4.10:1 on that same background. Use this for text only;
     use --green for fills, borders and icons. Audit #11 follow-up, ADR-015. */
  --green-text: #166534;
  --green-l:  #22c55e;
  /* Declared here as well as in styles.css. var() on an undeclared custom
     property makes the whole declaration invalid and it is dropped silently -
     which is exactly what happened when the contrast fixes first referenced
     --green-d here: the buttons computed to a transparent background and the
     icon fell back to inherited grey, with no error anywhere. */
  --green-d:  #15803d;
  --bg:       #f3f4f6;
  --surface:  #ffffff;
  --border:   #e5e7eb;
  --text1:    #111827;
  --text2:    #374151;
  --text3:    #6b7280;
  --text4:    #374151;
  /* audit #11: was #9ca3af, which failed WCAG AA on every one of its 43 uses
     (measured 1.3-2.54:1 against real composited pixels). Owner chose #374151. */
  --sb-bg:    #111827;
  --sb-text:  #d1d5db;
  --sb-hover: rgba(255,255,255,.06);
  --sb-active:rgba(22,163,74,.15);
  --radius:   10px;
  --shadow:   0 2px 8px rgba(0,0,0,.08);
}
html,body{height:100%;font-family:'Inter',system-ui,sans-serif;font-size:14px;background:var(--bg);color:var(--text2);-webkit-font-smoothing:antialiased}
h1,h2,h3,h4,strong{color:var(--text1)}
a{text-decoration:none;color:inherit}
button{font-family:'Inter',sans-serif;cursor:pointer}

/* ===== LAYOUT ===== */
/* grid-template-rows uses dvh, not vh: vh resolves to the LARGE viewport (URL
   bar hidden), so a 100vh shell is taller than the visible area whenever the
   bar is showing - and body{overflow:hidden} then makes that surplus
   unreachable. dvh tracks the viewport as the chrome shows and hides. The vh
   line stays first as the fallback for browsers without dvh. */
body{display:grid;grid-template-columns:220px 1fr;grid-template-rows:100vh;overflow:hidden}
body{grid-template-rows:100dvh}
@media(max-width:900px){body{grid-template-columns:1fr}}

/* ===== SIDEBAR ===== */
.sidebar{grid-column:1;background:var(--sb-bg);color:var(--sb-text);display:flex;flex-direction:column;padding:0;border-right:1px solid rgba(255,255,255,.06);overflow-y:auto}
.sb-logo{display:flex;align-items:center;gap:10px;padding:20px 18px 16px;border-bottom:1px solid rgba(255,255,255,.06);font-size:.95rem;color:#fff}
.sb-logo strong{color:var(--green-l)}
.sb-nav{display:flex;flex-direction:column;gap:4px;padding:12px 10px;flex:1}
.sb-link{display:flex;align-items:center;gap:10px;padding:10px 12px;border-radius:8px;font-size:.85rem;font-weight:500;color:var(--sb-text);transition:all .2s ease;position:relative}
.sb-link:hover{background:var(--sb-hover);color:#fff}
.sb-link.active{background:var(--sb-active);color:var(--green-l)}
.sb-badge{margin-left:auto;background:var(--green-d);color:#fff;font-size:.68rem;font-weight:700;padding:2px 7px;border-radius:100px}
.sb-dot{width:8px;height:8px;background:#22c55e;border-radius:50%;margin-left:auto;box-shadow:0 0 6px #22c55e;animation:pulse-dot 2s infinite}
@keyframes pulse-dot{0%,100%{opacity:1}50%{opacity:.4}}
.sb-shop{display:flex;align-items:center;gap:10px;padding:14px 16px;border-top:1px solid rgba(255,255,255,.06);margin-top:auto}
.sb-av{width:32px;height:32px;background:var(--green-d);color:#fff;border-radius:8px;display:flex;align-items:center;justify-content:center;font-size:.75rem;font-weight:700;flex-shrink:0}
.sb-sname{font-size:.82rem;font-weight:600;color:#fff}
/* Lighter, not darker: this one sits on the DARK sidebar (#111827), so the
   fix runs the opposite way to the badges below. green-500 gives 7.79:1. */
.sb-splan{font-size:.7rem;color:#22c55e}
/* top:60px, not 0: the drawer used to start at the top of the screen, so when open
   it covered the whole topbar strip below it - including the hamburger that opens
   it (drawer z-index 100 vs topbar 50). Every tap on the toggle after the first
   landed on the drawer instead, so the button appeared dead and the only way to
   close was tapping the narrow strip to the right. Sliding out *beneath* the 60px
   topbar keeps the toggle permanently reachable, which is the usual drawer pattern. */
@media(max-width:900px){.sidebar{position:fixed;left:-220px;top:60px;height:calc(100vh - 60px);z-index:100;transition:left .3s ease;width:220px}.sidebar.open{left:0}}
@media(max-width:900px){.sidebar{height:calc(100dvh - 60px)}}

/* ===== MAIN ===== */
.dash-main{grid-column:2;display:flex;flex-direction:column;height:100vh;overflow-y:auto}
.dash-main{height:100dvh}
/* The scroll thumb was left entirely to the platform default. On Windows 11 Chrome
   that is a Fluent scrollbar: the track shows as an empty groove but the thumb is a
   faint sliver that only firms up on hover, so the bar looks like it has no handle.
   Colour it explicitly. --text4 thumb on a --bg track, darkening to --text3 on
   hover; both already in the palette above, so no new colours are introduced.
   Only ::-webkit-scrollbar is used, deliberately: setting `scrollbar-color` as well
   would make Chrome ignore these pseudo-elements and drop the rounding. Firefox
   ignores this block and keeps its own default, which is already visible.

   Guarded twice, on purpose. Touch devices render a transient overlay scrollbar
   that occupies 0px of layout; styling ::-webkit-scrollbar with an explicit width
   forces a permanent classic bar there instead, stealing 15px of content width and
   reflowing the whole mobile dashboard - measured at 8-13% of pixels moved at every
   width <=900px before this guard.
   `pointer: fine` is the semantically right half (mouse, not touch), but it cannot
   be verified here: headless Chrome reports pointer:fine even under device
   emulation and ignores setEmulatedMedia for it. So the width half carries the
   guarantee - 901px is the same boundary the sidebar and topbar toggle already use,
   and it is deterministically testable. Both together mean a desktop-width tablet
   keeps its overlay bar too. */
@media (min-width: 901px) and (pointer: fine) {
  /* 15px matches the platform default this replaces: any other value changes the
     scroller's content width and reflows the dashboard by that many pixels. */
  .dash-main::-webkit-scrollbar{width:15px}
  .dash-main::-webkit-scrollbar-track{background:var(--bg)}
  .dash-main::-webkit-scrollbar-thumb{background:var(--text4);border-radius:8px;border:3px solid var(--bg)}
  .dash-main::-webkit-scrollbar-thumb:hover{background:var(--text3)}
}
@media(max-width:900px){.dash-main{grid-column:1}}
/* Below 900px the shell becomes a normal scrolling document. An app-shell that
   scrolls an inner element never lets the mobile URL bar collapse, because the
   document itself never moves - and it nests a scroller inside a scroller on
   touch. .topbar is already position:sticky, so it keeps its behaviour once the
   viewport becomes the scrolling ancestor. The sidebar is position:fixed and is
   unaffected. */
@media(max-width:900px){
  html,body{height:auto;overflow:visible}
  body{grid-template-rows:auto}
  .dash-main{height:auto;min-height:100dvh;overflow:visible}
}

/* ===== TOPBAR ===== */
.topbar{position:sticky;top:0;z-index:50;background:var(--surface);border-bottom:1px solid var(--border);padding:0 24px;height:60px;display:flex;align-items:center;justify-content:space-between;flex-shrink:0;gap:16px}
.topbar-left{display:flex;align-items:center;gap:12px}
.sidebar-toggle{display:none;background:none;border:none;color:var(--text2);padding:6px;border-radius:6px}
.sidebar-toggle:hover{background:var(--bg)}
@media(max-width:900px){.sidebar-toggle{display:flex}}
/* display:inline because #24 promoted this from <span> to <h1> for the heading
   hierarchy. An h1 is block-level, which pushed the date onto its own line - the
   title and date used to sit side by side. The element's role changed; its
   appearance should not. */
.page-title{display:inline;font-family:'Plus Jakarta Sans',sans-serif;font-size:.98rem;font-weight:700;color:var(--text1)}
.page-sub{font-size:.75rem;color:var(--text4);margin-left:8px}
.topbar-right{display:flex;align-items:center;gap:12px}
.live-indicator{display:flex;align-items:center;gap:6px;font-size:.75rem;font-weight:600;color:var(--green-text);background:rgba(22,163,74,.08);border:1px solid rgba(22,163,74,.2);padding:5px 12px;border-radius:100px}
.live-dot{width:7px;height:7px;background:var(--green-l);border-radius:50%;animation:pulse-dot 1.5s infinite}
.lang-toggle{display:flex;gap:4px;background:var(--bg);border-radius:8px;padding:3px;border:1px solid var(--border)}
.lang-toggle span{font-size:.72rem;font-weight:600;padding:4px 8px;border-radius:6px;cursor:pointer;color:var(--text3)}
.lang-toggle .lt-active{background:var(--surface);color:var(--green-text);box-shadow:var(--shadow)}
.btn-back{font-size:.78rem;font-weight:500;color:var(--text3);background:var(--bg);border:1px solid var(--border);padding:6px 12px;border-radius:8px;transition:all .2s ease}
.btn-back:hover{background:var(--surface);color:var(--text1)}
.topbar-av{width:32px;height:32px;background:var(--green-d);color:#fff;border-radius:8px;display:flex;align-items:center;justify-content:center;font-size:.72rem;font-weight:700;flex-shrink:0}
/* The topbar's six controls need ~560px; at 320px there is 272px. Everything else
   in the bar is nowrap, but .btn-back was not, so flex squeezed it to 52px and its
   label broke onto FOUR lines - 71px tall inside a 60px bar - while the bar still
   overflowed by 93px. body{overflow:hidden} clips that with no scrollbar, so
   "Back to Site" and the avatar were simply unreachable below ~412px.
   Keep the label intact and drop the two decorative controls first: the AR/EN/HI
   toggle has no click handler at all (it is a static demo affordance) and the Live
   pill is ornament. Navigation survives at every width. */
@media(max-width:600px){
  .btn-back{white-space:nowrap}
  .live-indicator,.lang-toggle{display:none}
}
@media(max-width:420px){
  .topbar-av,.page-sub{display:none}
}

/* ===== STATS ROW ===== */
.stats-row{display:grid;grid-template-columns:repeat(4,1fr);gap:16px;padding:20px 24px}
@media(max-width:900px){.stats-row{grid-template-columns:repeat(2,1fr)}}
@media(max-width:500px){.stats-row{grid-template-columns:1fr}}
.stat-card{background:var(--surface);border:1px solid var(--border);border-radius:var(--radius);padding:16px;display:flex;align-items:center;gap:12px;box-shadow:var(--shadow)}
.sc-icon{width:40px;height:40px;border-radius:10px;display:flex;align-items:center;justify-content:center;flex-shrink:0}
/* Icon glyph against its own tint, WCAG 1.4.11 wants 3:1. Measured: green 2.96
   and orange 2.86 both failed; blue 3.62 and purple 4.91 already passed and are
   left alone. Only the glyph colour moves - every tint background is unchanged,
   so the four cards keep their colour coding. */
.sc-icon.green{background:rgba(22,163,74,.1);color:var(--green-d)}
.sc-icon.blue{background:rgba(2,132,199,.1);color:#0284c7}
.sc-icon.orange{background:rgba(245,158,11,.1);color:#b45309}
.sc-icon.purple{background:rgba(124,58,237,.1);color:#7c3aed}
.sc-num{font-family:'Plus Jakarta Sans',sans-serif;font-size:1.3rem;font-weight:700;color:var(--text1);line-height:1.1}
.sc-lbl{font-size:.72rem;color:var(--text4);margin-top:2px}
.sc-trend{margin-left:auto;font-size:.72rem;font-weight:600;padding:3px 8px;border-radius:100px;white-space:nowrap}
.sc-trend.up{color:var(--green-text);background:rgba(22,163,74,.08)}
.sc-trend.warn{color:#b45309;background:rgba(245,158,11,.08)}

/* ===== DASH GRID ===== */
/* No grid-template-rows: each column is its own wrapper now and stacks its own
   panels, so neither column is forced to match the other's height.
   align-items:start keeps a short column short instead of stretching it. */
.dash-grid{display:grid;grid-template-columns:1fr 320px;gap:16px;padding:0 24px 24px;align-items:start}
.dash-col{display:flex;flex-direction:column;gap:16px;min-width:0}
@media(max-width:1100px){.dash-grid{grid-template-columns:1fr}}

/* ===== PANELS ===== */
.dash-panel{background:var(--surface);border:1px solid var(--border);border-radius:var(--radius);overflow:hidden;box-shadow:var(--shadow)}
.panel-header{display:flex;align-items:center;gap:12px;padding:16px 20px;border-bottom:1px solid var(--border)}
/* h2, not h3: the panel headings were h3 on a page with no h1 or h2 at all.
   #24 made the page title an h1 and these h2, so the selector had to follow -
   left as h3 it would simply stop matching and the headings would render at
   the browser's default h2 size. */
.panel-header h2{font-size:.9rem;font-weight:600;color:var(--text1);flex:1}
.panel-count{font-size:.75rem;color:var(--text4);background:var(--bg);border:1px solid var(--border);padding:2px 8px;border-radius:100px}
.ph-left{display:flex;align-items:center;gap:10px;flex:1}
.ph-right{display:flex;align-items:center;gap:8px}
.ph-sub{font-size:.75rem;color:var(--text4)}
.live-pill{font-size:.7rem;font-weight:700;color:var(--green-text);background:rgba(22,163,74,.1);border:1px solid rgba(22,163,74,.2);padding:3px 10px;border-radius:100px;animation:pulse-live 2s infinite}
@keyframes pulse-live{0%,100%{opacity:1}50%{opacity:.5}}
.filter-sel{font-size:.78rem;background:var(--bg);border:1px solid var(--border);border-radius:6px;padding:5px 10px;color:var(--text2);cursor:pointer;outline:none}
.ph-btn{font-size:.78rem;background:var(--bg);border:1px solid var(--border);border-radius:6px;padding:6px 12px;color:var(--text2);transition:all .2s ease}
.ph-btn:hover{background:var(--surface);border-color:var(--green);color:var(--green-text)}

/* ===== ORDERS TABLE ===== */

/* The max-height here used to be calc(100vh - 280px). That 280 stood for the
   topbar + stats row + panel header, and it was wrong at every width because
   .stats-row reflows: measured 236px of real stack at 1440 (so the table was
   44px shorter than it could be) but 510px at 375, where the constant pushed
   the last 230px of table below the fold. It also left 381-444px of empty
   panel on the two-column layout, where the grid stretches .orders-panel to
   match the right-hand column but the constant capped the table well short.

   No constant now. Two columns: the panel is already sized by the grid, so the
   table simply fills whatever is left under the header - which is the real
   header stack, tracked rather than guessed. One column: nothing to align to,
   so the table runs to its natural height and .dash-main scrolls. */
.orders-table-wrap{overflow-x:auto}
/* Every panel now sizes to its own content inside its column, so nothing needs
   stretching, capping or aligning. Two earlier attempts only relocated the empty
   space: stretching the table moved it inside the wrap, and align-self:start
   moved it below the panel into the gap above Order Detail. */
.orders-table{width:100%;border-collapse:collapse}
.orders-table thead tr{background:var(--bg);position:sticky;top:0}
.orders-table th{padding:10px 16px;text-align:left;font-size:.72rem;font-weight:600;letter-spacing:.06em;text-transform:uppercase;color:var(--text4);border-bottom:1px solid var(--border);white-space:nowrap}
.orders-table td{padding:12px 16px;border-bottom:1px solid var(--border);font-size:.82rem;vertical-align:middle}
.orders-table tr{cursor:pointer;transition:background .15s ease}
.orders-table tr:hover td{background:rgba(22,163,74,.03)}
.orders-table tr.selected td{background:rgba(22,163,74,.06)}
.order-id{font-family:'Courier New',monospace;font-size:.78rem;color:var(--text3)}
.cust-name{font-weight:500;color:var(--text1)}
.lang-badge{font-size:.65rem;font-weight:700;padding:2px 7px;border-radius:100px;letter-spacing:.05em}
.lang-badge.ar{background:rgba(37,99,235,.1);color:#1d4ed8}
.lang-badge.en{background:rgba(22,163,74,.1);color:var(--green-text)}
.lang-badge.hi{background:rgba(234,88,12,.1);color:#c2410c}
.status-badge{font-size:.72rem;font-weight:600;padding:3px 10px;border-radius:100px;white-space:nowrap}
/* Each badge is its hue at 10% over white, with the same hue as text. At the
   600-weight shades the text sat at 2.95-4.50 on its own tint; one step darker
   (the 700s) clears 4.5 without changing which colour each status reads as.
   Same move ADR-025 made for .sc-icon.orange. delivery (4.91) and out (5.46)
   already passed and are untouched. */
.status-badge.new{background:rgba(37,99,235,.1);color:#1d4ed8}
.status-badge.processing{background:rgba(245,158,11,.1);color:#b45309}
.status-badge.delivery{background:rgba(124,58,237,.1);color:#7c3aed}
.status-badge.delivered{background:rgba(22,163,74,.1);color:var(--green-text)}
.action-btn{background:none;border:1px solid var(--border);border-radius:6px;padding:4px 10px;font-size:.72rem;color:var(--text3);transition:all .2s ease}
.action-btn:hover{border-color:var(--green);color:var(--green-text)}

/* ===== VOICE FEED ===== */

.voice-feed{display:flex;flex-direction:column;gap:0;max-height:360px;overflow-y:auto}
.vf-item{padding:14px 18px;border-bottom:1px solid var(--border);transition:background .2s ease}
.vf-item:hover{background:rgba(22,163,74,.02)}
.vf-item.new{border-left:3px solid var(--green)}
.vf-top{display:flex;align-items:center;gap:8px;margin-bottom:6px}
.vf-lang{font-size:.65rem;font-weight:700;padding:2px 7px;border-radius:100px;letter-spacing:.05em}
.vf-lang.ar{background:rgba(37,99,235,.1);color:#1d4ed8}
.vf-lang.en{background:rgba(22,163,74,.1);color:var(--green-text)}
.vf-lang.hi{background:rgba(234,88,12,.1);color:#c2410c}
.vf-customer{font-size:.8rem;font-weight:600;color:var(--text1);flex:1}
.vf-time{font-size:.7rem;color:var(--text4)}
.vf-raw{font-size:.8rem;color:var(--text2);line-height:1.5;margin-bottom:4px;font-style:italic}
.vf-parsed{font-size:.75rem;color:var(--green-text);font-weight:500;margin-bottom:6px}
.vf-conf{display:flex;align-items:center;gap:8px}
.conf-bar{height:4px;background:linear-gradient(90deg,var(--green),#34d399);border-radius:100px;display:block}
.vf-conf span{font-size:.68rem;color:var(--green-text);font-weight:600;white-space:nowrap}

/* ===== ANALYTICS ===== */

.analytics-body{padding:16px;display:flex;flex-direction:column;gap:20px}
.an-chart-title{font-size:.72rem;font-weight:600;text-transform:uppercase;letter-spacing:.08em;color:var(--text4);margin-bottom:10px}
.bar-chart{display:flex;align-items:flex-end;gap:4px;height:80px}
.bc-bar{flex:1;background:rgba(22,163,74,.2);border-radius:4px 4px 0 0;display:flex;align-items:flex-end;justify-content:center;padding-bottom:4px;transition:background .3s ease;cursor:default;min-width:0}
/* --green-d to match .bc-peak: .bc-bar:hover span goes white, and white on
   --green is 3.30:1. ADR-025 fixed the peak bar and missed this hover state. */
.bc-bar:hover{background:var(--green-d)}
.bc-bar span{font-size:.6rem;color:var(--text4)}
/* The highlighted bar is a solid green fill, and at 8.4px its label needs 4.5:1.
   --text4 on --green is 3.13 and white is 3.30 - both fail, so recolouring the
   label alone cannot fix it. Darkening the bar to --green-d and putting the label
   in white gives 5.02:1. The label differing from the pale bars' labels is
   expected: those sit on a 20% tint, this one sits on a solid fill. */
.bc-bar.bc-peak{background:var(--green-d)!important}
.bc-bar.bc-peak span{color:#fff}
.bc-bar:hover span{color:#fff}
.lang-bars{display:flex;flex-direction:column;gap:8px}
.lb-row{display:grid;grid-template-columns:60px 1fr 36px;align-items:center;gap:8px}
.lb-lbl{font-size:.75rem;color:var(--text3)}
.lb-bar{height:8px;background:var(--bg);border-radius:100px;overflow:hidden}
.lb-fill{height:100%;border-radius:100px;transition:width 1s ease}
.ar-fill{background:linear-gradient(90deg,#2563eb,#60a5fa)}
.en-fill{background:linear-gradient(90deg,var(--green),#34d399)}
.hi-fill{background:linear-gradient(90deg,#ea580c,#fb923c)}
.lb-row span:last-child{font-size:.75rem;font-weight:600;color:var(--text2);text-align:right}
.top-items{background:var(--bg);border-radius:var(--radius);padding:12px}
.ti-list{display:flex;flex-direction:column;gap:0}
.ti-row{display:flex;align-items:center;justify-content:space-between;padding:8px 0;border-bottom:1px solid var(--border);font-size:.8rem;color:var(--text2)}
.ti-row:last-child{border-bottom:none}
.ti-cnt{font-weight:700;color:var(--green-text);background:rgba(22,163,74,.1);padding:2px 8px;border-radius:100px;font-size:.72rem}

/* ===== ORDER DETAIL ===== */

/* Was `@media(min-width:1100px){.order-detail-panel{display:none}}`, which hid this
   panel at every desktop width - so clicking an order selected the row and filled
   #odContent, but nothing was ever visible. The 2x2 grid had no free slot for it
   (.orders-panel spans both rows of col 1; voice + analytics fill col 2), so it
   takes an explicit third row in col 1. It renders here exactly as it already does
   below 1100px. Note the old pair also both matched at exactly 1100px. */
/* .topbar is position:sticky at 60px tall, so scrollIntoView({block:'start'})
   would slide the panel header underneath it. scroll-margin-top is the property
   scrollIntoView honours for exactly this; 76px clears the bar with 16px to spare. */
.order-detail-panel{scroll-margin-top:76px}

.od-empty{display:flex;flex-direction:column;align-items:center;gap:12px;padding:40px;color:var(--text4)}
.od-empty-icon{font-size:2.5rem}
.od-content{padding:20px;display:flex;flex-direction:column;gap:16px}
.od-header-info{display:flex;flex-wrap:wrap;gap:12px;padding-bottom:16px;border-bottom:1px solid var(--border)}
.od-badge{font-size:.72rem;font-weight:600;padding:4px 10px;border-radius:100px}
.od-items{display:flex;flex-direction:column;gap:0}
.od-item-row{display:grid;grid-template-columns:1fr auto auto;gap:12px;align-items:center;padding:10px 0;border-bottom:1px solid var(--border);font-size:.85rem}
.od-item-name{font-weight:500;color:var(--text1)}
.od-item-qty{color:var(--text3)}
.od-item-price{font-weight:600;color:var(--text1)}
.od-total{display:flex;justify-content:space-between;font-size:.9rem;font-weight:700;color:var(--text1);padding-top:8px}
.od-actions{display:flex;gap:8px}
.od-btn{flex:1;padding:10px;border-radius:8px;font-size:.82rem;font-weight:600;border:none;cursor:pointer;transition:all .2s ease}
/* --green-d, not --green: white on #16a34a is 3.30:1 and this label is 11.5px,
   so it needs 4.5. --green-d is already in the palette (it is .btn-primary's
   hover) and takes it to 5.02:1 at hue 142.4 against the brand's 142.1 - the
   same colour family, no new value. Hover goes one step darker again. */
.od-btn.confirm{background:var(--green-d);color:#fff}
.od-btn.confirm:hover{background:var(--green-text)}
.od-btn.reject{background:var(--bg);color:var(--text2);border:1px solid var(--border)}
.od-btn.reject:hover{border-color:#ef4444;color:#ef4444}

/* ===== MULTI-VIEW NAVIGATION (audit #9) =====
   The sidebar used to move .active and rewrite the page title while the content
   underneath never changed, so the page claimed to be on Inventory while showing
   the orders table. Each link now owns a real view. Everything below is new-class
   only - no existing selector is modified - which is what keeps the default Orders
   view byte-identical to the single-view version. See DECISIONS.md ADR-013. */
.dash-view[hidden]{display:none}
.view-pad{padding:20px 24px 24px;display:flex;flex-direction:column;gap:16px}
/* min-width:auto floors a grid or flex item at its content width, so the wide
   Inventory and Customers tables pushed the whole shell out to 675px and 706px
   inside a 320px viewport - and .orders-table-wrap's overflow-x:auto never got
   the chance to scroll, because nothing above it was allowed to be narrower than
   its content. The floor has to be lifted at BOTH levels: .dash-main is a grid
   item of body, .dash-view is a flex item of .dash-main. Putting it only on the
   inner elements changes nothing, which is what a first attempt here proved.
   Invisible until #28 removed body{overflow:hidden}, which had been clipping it
   - the same masking that hid #33/#34/#35. Same root cause as #5 and #32. */
.dash-main{min-width:0}
.dash-view{min-width:0}
.live-stats{display:grid;grid-template-columns:repeat(3,1fr);gap:16px}
@media(max-width:900px){.live-stats{grid-template-columns:repeat(2,1fr)}}
@media(max-width:500px){.live-stats{grid-template-columns:1fr}}
.an-cols{display:grid;grid-template-columns:repeat(3,1fr);gap:16px;align-items:start}
@media(max-width:1100px){.an-cols{grid-template-columns:1fr}}

/* A table that is presentational, not a list of clickable rows: .orders-table
   sets cursor:pointer and a hover tint because its rows open the detail panel.
   Inventory and Customers rows do nothing, so they must not advertise that they do. */
.orders-table.static tr{cursor:default}
.orders-table.static tr:hover td{background:transparent}

/* Inventory */
.inv-item{font-weight:500;color:var(--text1);white-space:nowrap}
.inv-cat{color:var(--text3)}
.stock-cell{display:flex;align-items:center;gap:10px;min-width:150px}
.stock-bar{flex:1;height:6px;background:var(--bg);border-radius:100px;overflow:hidden}
.stock-fill{display:block;height:100%;border-radius:100px;background:var(--green)}
.stock-fill.low{background:#b45309}
.stock-fill.out{background:#b91c1c}
/* Out-of-stock borrowed .status-badge.new (blue) while its own stock bar was red.
   Same state, two colours. Give it a badge that matches the bar. */
.status-badge.out{background:rgba(185,28,28,.1);color:#b91c1c}
.stock-qty{font-variant-numeric:tabular-nums;color:var(--text2);min-width:22px;text-align:right}

/* Customers */
.cu-cell{display:flex;align-items:center;gap:10px}
.cu-av{width:28px;height:28px;border-radius:50%;flex:none;display:grid;place-items:center;
  font-size:.68rem;font-weight:700;color:#fff;background:linear-gradient(135deg,#16a34a,#059669)}
.cu-phone{font-family:'Courier New',monospace;font-size:.78rem;color:var(--text3);white-space:nowrap}
.cu-last{color:var(--text3);white-space:nowrap}

/* Settings */
.set-group{display:flex;flex-direction:column}
.set-row{display:flex;align-items:center;gap:16px;padding:14px 20px;border-bottom:1px solid var(--border)}
.set-row:last-child{border-bottom:none}
.set-label{flex:1;display:flex;flex-direction:column;gap:2px}
.set-name{font-size:.85rem;font-weight:500;color:var(--text1)}
.set-desc{font-size:.75rem;color:var(--text3)}
.set-val{font-size:.82rem;color:var(--text2);text-align:right}
.toggle{width:40px;height:22px;flex:none;position:relative;border:none;border-radius:100px;
  background:#9ca3af;transition:background .2s ease}
.toggle::after{content:'';position:absolute;top:3px;left:3px;width:16px;height:16px;
  border-radius:50%;background:#fff;transition:left .2s ease}
.toggle[aria-checked='true']{background:var(--green)}
.toggle[aria-checked='true']::after{left:21px}

/* Visually hidden: gives each bare toggle an accessible name without changing the UI. */
.vh{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;
  clip:rect(0 0 0 0);clip-path:inset(50%);white-space:nowrap;border:0}

/* Analytics: the weekly chart wants more height than the compact in-panel one. */
/* .bc-bar is flex:1, sized for the 320px side panel. Across a full-width panel
   that stretches each bar to ~160px and the chart reads as a row of blocks, so
   the tall variant caps bar width and centres the group. */
.bar-chart.tall{height:180px;gap:10px;justify-content:center}
.bar-chart.tall .bc-bar{max-width:110px}

.demo-note{font-size:.78rem;color:var(--text3);line-height:1.6;padding:4px 2px}

/* The dedicated Live view is the whole page, not a 320px side rail, so it should
   not inherit .voice-feed's 360px cap. */
.voice-feed.full{max-height:none}

/* ===== KEYBOARD FOCUS (audit #12) =====
   .filter-sel set outline:none with no replacement and was the one control on
   either page with no focus indicator at all - verified as 0% pixel change on
   keyboard focus, against 11-14% for every other control. Everything else fell
   back to Chrome's UA ring, which is drawn from currentColor: white on
   .btn-primary, green on .btn-outline, and different again in other browsers.
   So the real defect was one dead control plus an inconsistent, UA-dependent
   indicator everywhere else. One brand ring fixes both.

   :focus-visible, not :focus - a mouse click on a button should not leave a ring
   behind, which is why the UA distinguishes them.
   Specificity (0,1,0) beats the `input,select,textarea` element selector that
   carries outline:none, and this block sits last in the file so it also wins on
   source order against .filter-sel's own (0,1,0) rule.
   outline-offset keeps the ring clear of the control, so it stays visible on the
   green .btn-primary fill by sitting on the surface behind it. */
:focus-visible{outline:3px solid var(--green);outline-offset:2px}

/* Ring contrast against the surfaces it lands on: 3.30:1 on white and 5.25:1 on
   the dark sidebar/hero, both clearing the 3:1 WCAG 1.4.11 asks of non-text UI. */

/* ===== REDUCED MOTION (audit #14) =====
   Three infinite loops here: the sidebar status dot, the topbar live dot and the
   pulsing LIVE pill. All three are decorative - they signal "live" but carry no
   information the static state does not. Both pulse keyframes rest at opacity:1,
   so stopping them leaves the indicator fully visible rather than faded. */
@media (prefers-reduced-motion: reduce){
  *,*::before,*::after{
    animation-duration:1ms!important;
    animation-iteration-count:1!important;
    transition-duration:1ms!important;
    scroll-behavior:auto!important
  }
  .sb-dot,.live-dot,.live-pill{animation:none}
}

/* The skip link that used to live here was removed 2026-09-03 by owner
   decision - see ADR-032. Deliberate, not an oversight: it is how these two
   pages satisfied WCAG 2.4.1 (Bypass Blocks, Level A), and they no longer do.
   <main id="main"> is still in the markup, so restoring it is markup + this
   block. Do not re-add it as an "accessibility fix" without asking. */
