  /* Brand fonts — used by mini-app previews (Products tab / Designer
     Sections, future popup previews). Same files /cms loads. */
  /* Bear Serif's digits are thin/geometric and don't match its chunky letters.
     Lilita One is mapped over the digit/currency range so digits render with
     matching weight. The bear-serif file covers everything else. */
  @font-face { font-family: 'Bear Serif';   src: url('/assets/fonts/LilitaOne.ttf')      format('truetype'); unicode-range: U+0024, U+0025, U+0030-0039, U+00A5; font-display: swap; }
  @font-face { font-family: 'Bear Serif';   src: url('/assets/fonts/BearSerif.otf')      format('opentype'); font-display: swap; }
  @font-face { font-family: 'Toni Grotesk'; src: url('/assets/fonts/ToniGroteskPro.otf') format('opentype'); font-display: swap; }
  @font-face { font-family: 'League Gothic';src: url('/assets/fonts/LeagueGothic.ttf')   format('truetype'); font-display: swap; }
  @font-face { font-family: 'Aleo';         src: url('/assets/fonts/Aleo-Regular.otf')   format('opentype'); font-display: swap; }
  @font-face { font-family: 'DF Yuan';      src: url('/assets/fonts/DFYuanW5.ttf')       format('truetype'); font-weight: 500; font-display: swap; }
  @font-face { font-family: 'DF Yuan';      src: url('/assets/fonts/DFYuanW7.ttf')       format('truetype'); font-weight: 700; font-display: swap; }

  /* ═══════════════════════════════════════════════════════════════════
     DESIGN TOKENS (mirror of /cms)
     ═══════════════════════════════════════════════════════════════════ */
  :root {
    /* ── Full Circle palette · dark-mode-first ──
       Ink Charcoal canvas, Deeper Paper type, strict Tea + Brass accents.
       Tea = active / live / positive; Brass = primary action / attention. */
    --bg-app:     #12161A;   /* Ink Charcoal */
    --bg-pane:    #1A1F24;   /* lifted Ink — cards / topbar */
    --bg-elev-1:  #1F252B;
    --bg-elev-2:  #262E35;
    --bg-input:   #12161A;
    --bg-overlay: rgba(8, 10, 12, .72);

    --line-soft:  rgba(232, 229, 220, .06);
    --line:       rgba(232, 229, 220, .10);
    --line-strong: rgba(232, 229, 220, .16);
    --ring:       rgba(163, 180, 159, .40);   /* Tea focus */

    --text-1:     #E8E5DC;   /* Deeper Paper */
    --text-2:     #AEABA2;
    --text-3:     #7d7a72;
    --text-4:     #56544f;

    /* Tacet remap of the two Full Circle brand colours. Tea Green → Tacet
       sage (same "active / live / positive" role); Aged Brass → graphite
       (labels/markers) — genuine attention/status surfaces set amber locally.
       Theme-aware via --td-* so one mapping serves dark + light. */
    --accent:     var(--td-sage-ink);   /* was Tea Green #6B8E6F */
    --accent-hi:  var(--td-sage);
    --accent-ink: #12161A;
    --brass:      var(--td-amber);       /* was Aged Brass — attention/action → Tacet amber */
    --brass-hi:   var(--td-amber-ink);
    --lime:       var(--td-sage-ink);   /* legacy alias → sage */
    --danger:     #e8556a;
    --danger-soft: rgba(232, 85, 106, .14);
    --warn:       var(--td-amber);       /* attention = Tacet amber (was Brass) */
    --success:    var(--td-sage-ink);    /* positive = Tacet sage (was Tea) */

    /* Order status colors — mapped onto the Tacet sage + amber + neutral
       system. Sage = money-in / done (paid, delivered); amber = needs-action
       / in-flight (pending, preparing, dispatched, awaiting restock);
       neutral = closed (cancelled, refunded). */
    --st-pending_payment: var(--td-amber);
    --st-paid:            var(--td-sage-ink);
    --st-preparing:       var(--td-amber);
    /* Rider coming (Meituan): a rider is en route to the store to collect.
       In-motion blue so it reads distinct from the amber Preparing/Dispatched. */
    --st-rider_pickup:    #4A72B0;
    --st-dispatched:      var(--td-amber);
    --st-delivered:       var(--td-sage-ink);
    --st-cancelled:       #8a8780;
    --st-refunded:        #8a8780;
    /* Awaiting-restock: order paid + parked, waiting for missing items to
       arrive before it can move to preparing. Amber = attention/standby. */
    --st-awaiting_restock: var(--td-amber);

    --s1: 4px; --s2: 8px; --s3: 12px; --s4: 16px; --s5: 20px; --s6: 24px; --s7: 32px;
    --r-sm: 4px; --r-md: 6px; --r-lg: 10px; --r-xl: 14px;

    --sh-1: 0 1px 2px rgba(0, 0, 0, .25);
    --sh-2: 0 4px 16px rgba(0, 0, 0, .25);
    --sh-3: 0 24px 60px rgba(0, 0, 0, .5);

    /* Tacet base type — Inter everywhere (was IBM Plex Sans/Mono, the Full
       Circle stack). Flipping the base tokens migrates the body type of every
       un-remapped tab at once; the orders-skin + dashboard scopes already set
       Inter locally, so this just brings the remaining tabs in line. */
    --f-sans:  'Inter', 'IBM Plex Sans SC', 'PingFang SC', 'Hiragino Sans GB', system-ui, sans-serif;
    --f-mono:  'Inter', 'IBM Plex Sans SC', 'PingFang SC', 'Hiragino Sans GB', system-ui, sans-serif;

    --ease-out: cubic-bezier(.16, 1, .3, 1);
    --t-fast: 120ms;
    --t-med:  220ms;
  }

  /* ═══════════════════════════════════════════════════════════════════
     LIGHT THEME — opt-in via <html data-theme="light">. Pre-render
     script at the top of <head> sets the attribute before first paint
     so there is no flash on load. Toggle persists to localStorage.
     ═══════════════════════════════════════════════════════════════════ */
  :root[data-theme="light"] {
    /* Full Circle light — Deeper Paper page, white cards, Ink type.
       Tea + Brass carry across themes unchanged (just darkened a touch
       for legibility on the light ground). */
    --bg-app:     #E8E5DC;   /* Deeper Paper */
    --bg-pane:    #FFFFFF;    /* white cards / chrome */
    --bg-elev-1:  #F1EEE7;
    --bg-elev-2:  #E3DFD5;
    --bg-input:   #FFFFFF;
    --bg-overlay: rgba(18, 22, 26, .46);

    /* Ink-alpha lines (brand: 1px @ 8% borders instead of shadows). */
    --line-soft:   rgba(18, 22, 26, .07);
    --line:        rgba(18, 22, 26, .12);
    --line-strong: rgba(18, 22, 26, .20);
    --ring:        rgba(163, 180, 159, .40);   /* Tea focus */

    --text-1: #12161A;   /* Ink Charcoal */
    --text-2: #43413a;
    --text-3: #6a675c;
    --text-4: #9a978c;

    --accent:     var(--td-sage-ink);   /* was Tea Green */
    --accent-hi:  var(--td-sage);
    --accent-ink: #12161A;
    --brass:      var(--td-amber);       /* was Aged Brass → Tacet amber */
    --brass-hi:   var(--td-amber-ink);
    --lime:       var(--td-sage-ink);   /* legacy alias → sage */
    --warn:       var(--td-amber);
    --success:    var(--td-sage-ink);
    --danger-soft: rgba(232, 85, 106, .11);

    /* Warm-tinted shadows so cards feel like paper sitting on a desk
       rather than floating elements with bluish drops. */
    --sh-1: 0 1px 3px rgba(40, 30, 5, .10);
    --sh-2: 0 6px 18px rgba(40, 30, 5, .12);
    --sh-3: 0 24px 60px rgba(40, 30, 5, .24);
  }
  :root[data-theme="light"] body { color-scheme: light; }
  :root[data-theme="light"] input[type=date],
  :root[data-theme="light"] input[type=datetime-local],
  :root[data-theme="light"] input[type=time],
  :root[data-theme="light"] select { color-scheme: light; }
  /* Native dropdown chevron — swap the stroke colour so it stays visible. */
  :root[data-theme="light"] select {
    background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%23767369' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'><polyline points='6 9 12 15 18 9'></polyline></svg>");
  }
  /* Tacet mark has no backplate — it inherits --text-1 (ink on light). */
  :root[data-theme="light"] .brand-mark {
    background: none;
    box-shadow: none;
  }
  /* Tier / status pills used a translucent overlay tuned for the dark bg.
     Bump opacity so they read on white. SEED = Tea per Full Circle. */
  :root[data-theme="light"] .cu-pill                { background: rgba(18, 22, 26, .06); color: var(--text-3); }
  :root[data-theme="light"] .cu-pill.brass          { background: rgba(201, 149, 74, .12);  border-color: rgba(201, 149, 74, .30); color: #8A6425; }
  :root[data-theme="light"] .cu-pill.tier-seed      { background: rgba(163, 180, 159, .16);  border-color: rgba(163, 180, 159, .30); color: #4A6249; }
  :root[data-theme="light"] .cu-pill.tier-sprout    { background: rgba(163, 180, 159, .14);  border-color: rgba(163, 180, 159, .28); color: #4A6249; }
  :root[data-theme="light"] .cu-pill.tier-root      { background: rgba(201, 149, 74, .16);   border-color: rgba(201, 149, 74, .28); color: #8A6425; }
  :root[data-theme="light"] .cu-pill.tier-tree      { background: rgba(201, 149, 74, .20);   border-color: rgba(201, 149, 74, .34); color: #8A6425; }
  :root[data-theme="light"] .cu-pill.blocked        { background: rgba(232, 85, 106, .16);  border-color: rgba(232, 85, 106, .32); color: #a01f33; }

  * { margin: 0; padding: 0; box-sizing: border-box; }
  /* Kill the browser's default mobile tap-flash (a translucent rectangle that
     reads as a "glow/blob" behind rounded controls). Every control already has
     its own :active feedback, so this only removes the stray overlay. */
  * { -webkit-tap-highlight-color: transparent; }
  html, body { height: 100%; }
  /* Reserve the scrollbar gutter so locking page scroll (when a modal/drawer
     is open) doesn't shift the layout sideways. */
  html { scrollbar-gutter: stable; }
  body {
    font: 13px var(--f-sans);
    background: var(--bg-app);
    color: var(--text-1);
    min-height: 100vh;
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
    /* Disable accidental text selection across the backend UI. On mobile a
       long-press used to select stray text (e.g. a stat number) which pops up
       Android Chrome's "Tap to see search results" Google bar. Inputs and
       anything explicitly marked selectable opt back in below. */
    -webkit-user-select: none;
    -moz-user-select: none;
    user-select: none;
    -webkit-touch-callout: none;
  }
  /* Re-enable selection where it's genuinely useful: form fields and any
     element that deliberately sets user-select: text (e.g. the openid). */
  input, textarea, [contenteditable], [contenteditable] *,
  [style*="user-select: text"], [style*="user-select:text"] {
    -webkit-user-select: text;
    -moz-user-select: text;
    user-select: text;
  }
  button { font: inherit; }
  /* Brand the native form controls — the default browser blue clashes with the
     Ink/Brass/Tea palette. Tea Green matches the "on/live" switches. */
  input[type="checkbox"], input[type="radio"] { accent-color: var(--accent); }
  ::selection { background: var(--ring); color: var(--text-1); }

  /* ── Rail width — the ONE source of truth for the left rail's width ────
     Consumed by three rules: .tabs width, main margin-left, and the embedded
     editors' left. body.nav-collapsed swaps it to the folded width, so those
     consumers fold in lockstep with the rail. (The topbar deliberately does
     NOT read this — it is locked full-width; see .topbar below.) This is a
     LAYOUT variable and deliberately lives outside the --td-* colour tokens. */
  :root { --rail-w: 236px; }

  /* ═══════════════════════════════════════════════════════════════════
     TOPBAR
     ═══════════════════════════════════════════════════════════════════ */
  .topbar {
    height: 56px;
    background: var(--bg-pane);
    border-bottom: 1px solid var(--line-soft);
    display: flex; align-items: center;
    /* Left inset is 14px (not the symmetric --s5) so the brand mark keeps the
       exact x it had as the rail head — the wordmark doesn't jump when it moves
       from the rail into this now-full-width bar. Right keeps --s5. */
    padding: 0 var(--s5) 0 14px;
    gap: var(--s4);
    /* LOCKED FULL-WIDTH. left:0 unconditionally — the topbar spans the whole
       window and never moves; folding the rail changes only the rail. It stays
       position:fixed (pinned to the viewport, the most drift-proof anchor) and
       z-index:12 sits ABOVE the rail (z:11), so its opaque pane covers the
       rail's top 56px — which is why the rail's own brand head is retired to
       this bar (see .nav-brand / .topbar .brand below).
       ⚠ DO NOT put the brand (or anything else meant to stay put) in the rail
       head and expect this topbar to stay full-width. The two are mutually
       exclusive by geometry, not by this stacking choice: the rail (.tabs) is
       OPAQUE and its width is --rail-w, which is fold-dependent (236px open,
       62px collapsed). Whatever occupies the rail's top 56px band — regardless
       of which layer is "above" the other — visually consumes that band, and
       the band's width changes when the rail folds. Raise the rail above this
       topbar (as tried 2026-07-24, commit 65230ada) and the topbar's visible
       left edge tracks the fold, reading as "the topbar folding" even though
       this box never moves. There is no z-index fix for that — moving the
       brand into the rail necessarily makes SOMETHING at that corner
       fold-dependent. Reverted in 52ef0b28; brand-in-rail is retired for good
       (2026-07-24). If this comment is gone, the bug is back. */
    position: fixed; top: 0; left: 0; right: 0; z-index: 12;
  }
  .brand {
    display: flex; align-items: center; gap: 10px;
    font-size: 13px; color: var(--text-1);
  }
  /* Tacet mark — the bare settling-waveform (no backplate); stroke follows
     the text colour so it reads ink on paper / paper on ink. Dusk dot fixed. */
  .brand-mark {
    display: inline-flex; align-items: center; justify-content: center;
    color: var(--text-1);
    background: none; border: 0; box-shadow: none;
  }
  /* 20px tall (§8a). Width auto → uniform scale 20/110; stroke-width 8.8 in the
     340×110 viewBox renders at 1.6px, the optical hold that keeps the decay
     visible at chrome size (a viewBox-scaled 3.5 would vanish). */
  .brand-mark svg { height: 20px; width: auto; display: block; }
  .brand .brand-name {
    /* The one sanctioned Fraunces in product UI — the wordmark. 24px is the
       design-system minimum wordmark height; Light 300, 0.2em tracking. */
    font-family: 'Fraunces', Georgia, serif; font-weight: 300; font-size: 24px;
    line-height: 1; letter-spacing: .20em; text-transform: none; color: var(--text-1);
  }
  .brand .divider {
    width: auto; height: auto; background: none; margin: 0 1px;
    color: var(--td-text2); font-size: 13px; line-height: 1;
  }
  .brand .divider::before { content: '·'; }
  .brand .sub {
    font-family: var(--f-mono); color: var(--text-3);
    font-weight: 400; font-size: 10px;
    letter-spacing: .18em; text-transform: uppercase;
  }
  .grow { flex: 1; }
  .who { font-size: 12px; color: var(--text-3); }
  .topbar-link {
    color: var(--text-2); text-decoration: none; font-size: 12px;
    padding: 6px 10px; border-radius: var(--r-md);
    transition: background var(--t-fast) ease, color var(--t-fast) ease;
  }
  .topbar-link:hover { background: var(--bg-elev-1); color: var(--text-1); }

  /* Topbar control cluster — Inbox + live dot + sound, pulled up from the rail
     foot. Tighter gap than the topbar's own so the three read as one group. */
  .tb-cluster { display: flex; align-items: center; gap: 2px; flex: none; }
  .topbar-ico {
    position: relative; flex: none;
    width: 34px; height: 34px; padding: 0;
    display: inline-flex; align-items: center; justify-content: center;
    border: 0; background: transparent; cursor: pointer;
    border-radius: var(--r-md); color: var(--text-2);
    transition: background var(--t-fast) ease, color var(--t-fast) ease;
  }
  .topbar-ico:hover { background: var(--bg-elev-1); color: var(--text-1); }
  .topbar-ico svg { width: 18px; height: 18px; }
  /* Unread badge → corner pip on the icon (it was an inline dot in the rail). */
  .topbar-ico .tab-badge { position: absolute; top: 4px; right: 4px; margin: 0; }

  /* Search — a 320px field-shaped button (structure here; colours in the Tacet
     reskin block). Looks like an input, is a button: opens the ⌘K palette. */
  .gsearch-trigger {
    display: inline-flex; align-items: center; gap: 8px; flex: none;
    width: 320px; height: 36px; padding: 0 8px 0 12px;
    border-radius: 8px; cursor: pointer;
    background: var(--td-raised); border: 1px solid var(--td-border);
    color: var(--td-text2); font: 500 13.5px var(--f-sans);
    transition: border-color var(--t-fast) ease;
  }
  .gsearch-ico { width: 16px; height: 16px; flex: none; }
  .gsearch-label { flex: 1 1 auto; text-align: left; }
  .gsearch-kbd {
    flex: none; font: 500 11px var(--f-sans); line-height: 1;
    padding: 3px 6px; border-radius: 5px;
    border: 1px solid var(--td-border); background: var(--td-surface);
  }

  /* ── Account widget — Google-style profile circle + dropdown ───────────── */
  .acct { position: relative; flex: none; }
  .acct-btn {
    display: inline-flex; align-items: center; justify-content: center;
    padding: 0; border: none; background: transparent; cursor: pointer;
    border-radius: 50%; line-height: 0;
  }
  .acct-circle {
    width: 32px; height: 32px; flex: none; border-radius: 50%; overflow: hidden;
    display: inline-flex; align-items: center; justify-content: center;
    background: var(--accent); color: #fff;
    font-size: 13px; font-weight: 500; letter-spacing: .02em;
    text-transform: uppercase; user-select: none;
    box-shadow: 0 0 0 1px var(--line) inset;
    transition: box-shadow var(--t-fast) ease;
  }
  .acct-circle img { width: 100%; height: 100%; object-fit: cover; display: block; }
  .acct-btn:hover .acct-circle,
  .acct-btn:focus-visible .acct-circle,
  .acct-btn[aria-expanded="true"] .acct-circle { box-shadow: 0 0 0 2px var(--accent); }
  .acct-btn:focus-visible { outline: none; }
  .acct-circle-lg { width: 40px; height: 40px; font-size: 15px; }

  .acct-menu {
    position: absolute; top: calc(100% + 8px); right: 0;
    min-width: 232px; z-index: 250;
    background: var(--bg-pane); border: 1px solid var(--line);
    border-radius: var(--r-lg); padding: 6px;
    box-shadow: 0 10px 30px rgba(0, 0, 0, .22);
    animation: zen-pop-in 220ms cubic-bezier(.16, 1, .3, 1);
  }
  .acct-menu[hidden] { display: none; }
  .acct-head { display: flex; align-items: center; gap: 10px; padding: 8px 10px 10px; }
  .acct-meta { min-width: 0; }
  .acct-name {
    font-size: 13px; font-weight: 600; color: var(--text-1);
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 150px;
  }
  /* Groups separate by space alone (no rule lines) — see .acct-gap below. */
  .acct-gap { margin-top: 6px; }
  .acct-item {
    display: flex; align-items: center; gap: 10px; width: 100%;
    padding: 9px 10px; border: none; background: transparent; cursor: pointer;
    border-radius: var(--r-md); text-align: left; text-decoration: none;
    font-size: 13px; color: var(--text-2); font-family: inherit;
    transition: background var(--t-fast) ease, color var(--t-fast) ease;
  }
  .acct-item svg { width: 16px; height: 16px; flex: none; opacity: .8; }
  .acct-item:hover { background: var(--bg-elev-1); color: var(--text-1); }
  .acct-danger { color: var(--danger); }
  .acct-danger:hover { background: rgba(232, 85, 106, .1); color: var(--danger); }
  /* Right-aligned current-value chip for the in-menu preference rows
     (Appearance / Text size / Language) — mono, muted, pushed to the edge. */
  .acct-val {
    margin-left: auto; padding-left: 12px; flex: none;
    font-size: 12px; color: var(--text-3);
    font-family: var(--f-mono); letter-spacing: .02em;
  }

  /* ── Profile pop-up — hosts /backend/account in an iframe so "Profile"
     opens as a centred modal instead of navigating away. Same scrim/panel
     pattern as the flash-sale composer's .fsd-scrim/.fsd-drawer. ── */
  .prof-scrim { position: fixed; inset: 0; background: var(--bg-overlay); opacity: 0; pointer-events: none; transition: opacity .25s var(--ease-out); z-index: 1200; }
  .prof-scrim.on { opacity: 1; pointer-events: auto; }
  .prof-modal { position: fixed; left: 50%; top: 50%; transform: translate(-50%, -46%) scale(.98); opacity: 0; pointer-events: none; width: min(860px, 96vw); height: min(720px, 92vh); background: var(--bg-app); border: 1px solid var(--line); border-radius: 18px; box-shadow: 0 24px 64px rgba(17, 26, 43, .22); overflow: hidden; z-index: 1201; display: flex; flex-direction: column; transition: opacity .3s var(--ease-out), transform .3s var(--ease-out); }
  .prof-modal.on { opacity: 1; pointer-events: auto; transform: translate(-50%, -50%) scale(1); }
  .prof-x { position: absolute; top: 14px; right: 14px; z-index: 3; width: 32px; height: 32px; border-radius: 9px; border: 1px solid var(--line); background: var(--bg-pane); display: grid; place-items: center; cursor: pointer; color: var(--text-3); }
  .prof-x:hover { border-color: var(--text-3); color: var(--text-1); }
  .prof-x svg { width: 15px; height: 15px; }
  .prof-frame { flex: 1; width: 100%; border: none; background: var(--bg-app); }
  @media (prefers-reduced-motion: reduce) { .prof-scrim, .prof-modal { transition: none; } }

  /* ── Command-search trigger (topbar pill) ── */
  .gsearch-trigger {
    display: inline-flex; align-items: center; gap: 7px; cursor: pointer;
    border: 1px solid var(--line); background: var(--bg-input);
  }
  .gsearch-trigger:hover { border-color: var(--ring); }
  .gsearch-label { font-weight: 600; letter-spacing: .01em; }
  .gsearch-kbd {
    font-size: 10px; font-weight: 700; line-height: 1; color: var(--text-3);
    border: 1px solid var(--line); border-radius: 5px; padding: 3px 5px;
    background: var(--bg-pane);
  }
  @media (max-width: 720px) { .gsearch-label, .gsearch-kbd { display: none; } }

  /* ── Command-search palette (Tacet) ───────────────────────────────
     One quiet question: "where do you want to go?" A framed field, a
     hushed backdrop, one soft shadow — no glass, no black slab. The
     selected row alone carries the tea accent (this admin's "here"). */
  .gsearch-overlay {
    position: fixed; inset: 0; z-index: 9000;
    background: var(--bg-overlay);
    display: flex; align-items: flex-start; justify-content: center;
    padding: 7vh 16px 24px;
    animation: gsearchBackdrop 350ms cubic-bezier(.16, 1, .3, 1);
    /* Re-point this panel off the leftover Full Circle Tea green onto Tacet
       Dusk — not Amber. Every already-migrated Tacet input (.pa-toolbar,
       #pa-filter, .pu2-search, .td-store-sel, .db-ask-box, …) focuses with
       --td-dusk; Amber is reserved for status/attention (Tacet: "colour is
       status, never decoration"). A command palette invokes the system, which
       is exactly what Dusk means, so this also reads as the *more* correct
       choice, not just the consistent one. Scoped here so every
       var(--accent)/var(--ring) inside the palette (focus ring, active-row
       icon, jump flash) migrates in one place; the rest of the legacy chrome
       keeps its own --accent untouched. */
    --accent: var(--td-dusk);
    --ring: var(--td-dusk-tint);
  }
  @keyframes gsearchBackdrop { from { opacity: 0; } to { opacity: 1; } }
  .gsearch-overlay[hidden] { display: none; }
  .gsearch-box {
    width: 100%; max-width: 680px; background: var(--bg-pane);
    border: 1px solid var(--line); border-radius: 14px;
    box-shadow: 0 20px 56px rgba(0, 0, 0, .28);
    overflow: hidden; display: flex; flex-direction: column;
    animation: gsearchPop 350ms cubic-bezier(.16, 1, .3, 1);
  }
  @keyframes gsearchPop { from { transform: translateY(8px); opacity: 0; } to { transform: none; opacity: 1; } }

  /* Search field — a real Tacet input: 1px border, 10r, focus = accent
     border + a soft tea ring (a tint, not a neon glow). */
  .gsearch-inputrow {
    display: flex; align-items: center; gap: 12px;
    margin: 16px; padding: 15px 16px;
    border: 1px solid var(--line); border-radius: 10px;
    background: var(--bg-input); color: var(--text-3);
    transition: border-color 200ms cubic-bezier(.16, 1, .3, 1),
                box-shadow 200ms cubic-bezier(.16, 1, .3, 1);
  }
  .gsearch-inputrow:focus-within {
    border-color: var(--accent);
    box-shadow: 0 0 0 3px var(--ring);
  }
  #gsearch-input {
    flex: 1; border: none; background: transparent; outline: none;
    font-size: 17px; color: var(--text-1); font-family: inherit;
  }
  #gsearch-input::placeholder { color: var(--text-3); }
  .gsearch-esc {
    font-size: 10px; font-weight: 600; letter-spacing: .04em; color: var(--text-3);
    border: 1px solid var(--line); border-radius: 6px; padding: 3px 7px;
  }
  /* Tall enough that the full page/settings list shows without a scrollbar on
     a normal window; still caps on short viewports so the panel can't overrun
     the screen. */
  .gsearch-results {
    max-height: 72vh; overflow-y: auto; padding: 6px 10px 12px;
    border-top: 1px solid var(--line);
  }
  .gsearch-group-head {
    font-size: 10.5px; font-weight: 600; letter-spacing: .08em; text-transform: uppercase;
    color: var(--text-3); padding: 16px 12px 6px;
  }
  .gsearch-item {
    display: flex; align-items: center; gap: 14px; width: 100%;
    padding: 12px; border-radius: 8px; cursor: pointer;
    border: none; background: transparent; text-align: left; color: var(--text-1);
    font-family: inherit; font-size: 14.5px;
    transition: background 160ms cubic-bezier(.16, 1, .3, 1);
  }
  .gsearch-item .gs-ico { color: var(--text-3); flex: 0 0 auto; display: inline-flex; }
  .gsearch-item .gs-body { min-width: 0; flex: 1; }
  .gsearch-item .gs-title { font-weight: 500; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
  .gsearch-item .gs-crumb { font-size: 11.5px; color: var(--text-3); margin-top: 1px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
  .gsearch-item .gs-tag {
    flex: 0 0 auto; font-size: 10px; font-weight: 600; letter-spacing: .02em; color: var(--text-3);
    border: 1px solid var(--line); border-radius: 6px; padding: 2px 7px;
  }
  .gsearch-item.active, .gsearch-item:hover { background: var(--bg-elev-1); }
  .gsearch-item.active .gs-ico { color: var(--accent); }
  .gsearch-empty { padding: 28px 16px; text-align: center; color: var(--text-3); font-size: 13.5px; }
  .gsearch-foot {
    display: flex; gap: 18px; padding: 11px 16px; border-top: 1px solid var(--line);
    font-size: 11.5px; color: var(--text-3);
  }
  .gsearch-foot kbd {
    font-family: inherit; font-size: 10px; border: 1px solid var(--line);
    border-radius: 5px; padding: 1px 6px; margin-right: 4px; color: var(--text-2);
  }
  /* Brief highlight pulse on a jumped-to target */
  @keyframes gsearchFlash {
    0% { box-shadow: 0 0 0 2px var(--accent), 0 0 0 6px var(--ring); }
    100% { box-shadow: 0 0 0 0 transparent, 0 0 0 0 transparent; }
  }
  .gsearch-flash { animation: gsearchFlash 1.4s ease-out; border-radius: 10px; }

  /* Light theme — trade the modal slab for a soft Ink shadow. */
  :root[data-theme="light"] .gsearch-box {
    box-shadow: 0 16px 48px rgba(17, 26, 43, .14);
  }

  /* ═══════════════════════════════════════════════════════════════════
     TABS
     ═══════════════════════════════════════════════════════════════════ */
  .tabs {
    display: flex; flex-direction: column; gap: 2px; align-items: stretch;
    padding: 10px 10px 14px;
    background: var(--bg-pane);
    border-right: 1px solid var(--line-soft);
    position: fixed; top: 0; left: 0; bottom: 0; width: var(--rail-w); z-index: 11;
    overflow-y: auto; overflow-x: hidden;
  }
  /* Rail brand head — RETIRED to a 56px spacer (2026-07-24). The brand (the
     wordmark, or a tenant's co-brand logo) now lives in the full-width topbar
     (.topbar .brand). This box is kept only so the rail's nav clears the fixed
     topbar, whose opaque pane covers the rail's top 56px. Its mark / wordmark /
     logo are hidden on desktop; on phones the whole rail is display:none, so it
     never shows there either. */
  .nav-brand { flex: none; height: 56px; margin: -10px -10px 6px; }
  .nav-brand .brand-mark, .nav-brand .brand-name, .nav-brand .brand-mark-img { display: none; }
  /* ── Co-brand identity — a tenant's uploaded logo replaces the waveform +
     wordmark in the TOPBAR brand (desktop and phones alike, now that the topbar
     is full-width). It shows in EVERY rail state — open or folded — because the
     topbar is independent of the fold; the old "only on the folded rail" mark is
     gone. Logo images are hidden by default; body.cobrand (set once the logo
     loads, by applyBrand()) reveals it. */
  .brand-logo, .brand-mark-img { display: none; object-fit: contain; }
  /* Co-brand is a LOCKUP, not a swap (§8b, decided 2026-07-25): the tacet mark +
     wordmark always stay; a tenant's logo is ADDED to their right behind a
     hairline. The old white-label swap (hiding .brand-mark / .brand-name under
     body.cobrand) is removed — it showed a logo-merchant no tacet at all. */
  body.cobrand .brand .brand-logo { display: block; }
  body.cobrand .brand .brand-rule { display: block; }
  /* Hairline between the tacet lockup and the merchant logo — 1px, 24px tall,
     16px either side (§8b). .brand's 10px flex gap sits on both sides too, so the
     margin is 6px to total 16px; if that gap changes, retune this. */
  .brand-rule {
    display: none; flex: none; width: 1px; height: 24px;
    margin: 0 6px; background: var(--td-border);
  }
  /* Merchant logo fit (§8b): scale to fit inside max 24px tall / 210px wide,
     preserving aspect (object-fit:contain above) — never crop, never stretch.
     The 14px min-height FLOOR is delivered by the 210px width ceiling (a 15:1
     banner lands at exactly 14px), NOT a literal min-height, which would letterbox
     a wide logo. Past 15:1 the ceiling wins and it renders shorter — ugly, bounded. */
  .brand .brand-logo { max-height: 24px; max-width: 210px; width: auto; height: auto; }
  /* The topbar carries the brand on desktop now that it is full-width — the
     .brand base (display:flex) applies; it sits at the topbar's far left and
     overhangs the folded 62px rail, by design. (It was display:none here while
     the rail owned the desktop brand; that rule is gone.) The mobile block
     still shows it explicitly for phones, where the rail is hidden. */
  .tabs button, .tabs a {
    position: relative;
    background: transparent;
    color: var(--text-2);
    border: 0;
    padding: 11px 12px;
    /* Inter to match the Tacet reskin + the Fraunces wordmark; the rest of the
       legacy chrome is still IBM Plex Sans, but the rail is the loudest nav so
       it leads the type migration. */
    font-family: 'Inter', var(--f-sans);
    font-size: 14.5px; font-weight: 500;
    cursor: pointer;
    border-radius: var(--r-lg);
    transition: color var(--t-fast) ease, background var(--t-fast) ease, box-shadow var(--t-fast) ease;
    display: flex; align-items: center; gap: 13px;
    width: 100%; text-align: left;
    /* Never wrap a label to a second line — keep every rail row single-line. */
    white-space: nowrap;
    text-decoration: none;
    box-sizing: border-box;
  }
  .tabs button:hover, .tabs a:hover { color: var(--text-1); background: var(--bg-elev-1); }
  /* Active row — cream fill + a short rounded Amber mark on the left edge and a
     heavier label (Tacet: Amber is the warm status/attention hue; the old --brass
     was leftover Full Circle). Popover children opt out of the mark below. */
  .tabs button.active, .tabs a.active { color: var(--text-1); background: var(--bg-elev-1); font-weight: 600; box-shadow: none; }
  .tabs button.active::before, .tabs a.active::before {
    content: ""; position: absolute; left: 0; top: 9px; bottom: 9px;
    width: 3px; border-radius: 0 3px 3px 0; background: var(--td-amber);
  }
  /* Rail unseen badge → calm Amber (matches the topbar inbox), never the red
     alarm pulse. Scoped to .tabs so it doesn't touch the mobile bottom-nav. */
  .tabs .tab-badge { background: var(--td-amber); animation: none; }
  .tabs-popover button.active::before { content: none; }
  .tabs button svg, .tabs a svg { width: 20px; height: 20px; flex: none; }
  /* Group/parent chevron sits at the far right of its row. */
  .tabs .tabs-chev { margin-left: auto; }

  /* Section headers — RUN / SELL / SET UP. Quiet uppercase Inter labels; their
     order in the rail is set by flex `order` on each header div (1 / 7 / 13). */
  .nav-sec {
    font-family: 'Inter', var(--f-sans);
    font-size: 10.5px; font-weight: 600; letter-spacing: .13em;
    text-transform: uppercase; color: var(--text-4);
    padding: 16px 12px 6px; white-space: nowrap; user-select: none;
  }
  /* ── Collapsible rail ──────────────────────────────────────────────
     A topbar toggle folds the rail down to an icon-only strip; the main
     content reflows to fill the reclaimed width. State persists in
     localStorage (restored pre-paint by an inline snippet in the page so
     there's no flash). Collapsed, each label is zeroed to font-size:0 —
     every rail button keeps its explicit svg size, so only the text
     disappears — and the group chevrons hide. Clicking a folder while folded
     unfolds the rail AND drills into it in one gesture (there is no folded
     drilled state: children carry no icons, so there would be nothing to
     draw), and folding FROM a drilled level returns to root first. Both are
     handled in setNavCollapsed / the trigger's click handler. Hidden on
     phones, where the rail is already swapped for the fixed bottom nav. */
  /* The rail-foot fold row (.nav-foot / .nav-fold-chev) was removed 2026-07-07 —
     the single fold control is now the seam dot (#railSeamFold, styled below). */

  .tabs { transition: width var(--t-fast) ease, padding var(--t-fast) ease; }
  main    { transition: margin-left var(--t-fast) ease; }
  /* No .topbar transition — it is locked full-width and never moves. */

  /* Fold = swap the rail-width variable once; .tabs/main/editors all read it,
     so they narrow in lockstep. The consuming properties still carry their own
     transitions above, so the fold still animates. The topbar does NOT ride the
     rail — it stays full-width. */
  body.nav-collapsed { --rail-w: 62px; }
  body.nav-collapsed .tabs { width: var(--rail-w); padding-left: 8px; padding-right: 8px; }
  body.nav-collapsed main   { margin-left: var(--rail-w); }
  /* The fixed embedded editors ride the rail's width so they stay flush against
     its right edge when it folds to icons. */
  body.nav-collapsed #tab-mini-app-editor,
  body.nav-collapsed #tab-product-editor { left: var(--rail-w); }
  /* Collapsed rail brand rules retired 2026-07-24: the rail brand is now a blank
     spacer (see .nav-brand above) and the brand — including any tenant co-brand
     logo — lives in the always-full-width topbar, so there is nothing to hide or
     re-center on fold. The old body.nav-collapsed .nav-brand .brand-name /
     .brand-mark hides and the folded-only .cobrand .brand-mark-img mark are gone. */
  body.nav-collapsed .tabs button, body.nav-collapsed .tabs a {
    font-size: 0; gap: 0; justify-content: center;
    padding-left: 0; padding-right: 0;
  }
  body.nav-collapsed .tabs .tabs-chev { display: none; }
  /* No folded rule is needed for the children list: folding returns the rail to
     root first, so a folded rail is never drilled. */
  /* Collapsed: section headers shrink to a centered dot; the auto marker hides. */
  body.nav-collapsed .nav-sec { text-align: center; padding: 14px 0 6px; }
  body.nav-collapsed .nav-sec span { display: none; }
  body.nav-collapsed .nav-sec::after { content: "·"; color: var(--text-4); font-size: 15px; }
  /* The fold control lives inside .tabs, which is hidden wholesale on phones
     (the rail is replaced by the bottom nav), so no mobile rule is needed. */

  /* ── Rail fold control ─────────────────────────────────────────────
     The primary rail-fold control. It lives INSIDE the rail (re-parented out of
     the topbar earlier), in its own zone at the top of the rail below the brand
     head and above the nav items. The side-panel glyph STATES WHAT IS TRUE
     NOW rather than what a click will do: the left column is filled while the
     rail is open, hollow once folded (see .sf-col). One icon, one region
     changing — no chevron, no icon swap. Open, it aligns with the nav rows;
     folded, it centres in the 62px rail. */
  /* All rules scope under `.tabs` so they outrank `.tabs button` (this control
     is a <button> in the rail): a bare `.seam-fold` (0,1,0) would lose to
     `.tabs button` (0,1,1) and inherit border:0, killing the focus/hover
     border. `.tabs .seam-fold` is (0,2,0) and wins. Row geometry matches the
     nav buttons (11px 12px) so the fold row aligns with them; margin-bottom
     gives it its own zone above the nav. */
  .tabs .seam-fold {
    order: 0;
    flex: none; box-sizing: border-box;
    display: flex; align-items: center;
    width: 100%; margin: 0 0 8px; padding: 11px 12px;
    border: 1px solid transparent; border-radius: var(--r-lg);
    background: none; color: var(--text-2); cursor: pointer;
    transition: color var(--t-fast) ease, background var(--t-fast) ease, border-color var(--t-fast) ease;
  }
  /* Hover: a quiet fill + border shift (no swell). */
  .tabs .seam-fold:hover { color: var(--text-1); background: var(--bg-elev-1); border-color: var(--line); }
  /* Focus: a Dusk border, no glow. */
  .tabs .seam-fold:focus-visible { outline: none; border-color: #6E67AF; }
  .tabs .seam-dot { display: inline-flex; align-items: center; justify-content: center; color: inherit; }
  .tabs .seam-dot svg { width: 19px; height: 19px; display: block; }
  /* The filled left column reads the "open" state; folding hollows it out. */
  .tabs .sf-col { transition: opacity var(--t-fast) ease; }
  body.nav-collapsed .tabs .sf-col { opacity: 0; }
  /* Folded: the control centres in the 62px rail like the nav icons do. */
  body.nav-collapsed .tabs .seam-fold { justify-content: center; padding-left: 0; padding-right: 0; }
  /* Phones swap the rail for the bottom nav, so the fold control has no home. */
  @media (max-width: 720px) { .tabs .seam-fold { display: none; } }
  /* Respect reduced motion for the whole fold: rail width/padding, the content
     reflow, and the control's own hover/glyph transitions all settle instantly. */
  @media (prefers-reduced-motion: reduce) {
    .tabs, main, .tabs .seam-fold, .tabs .sf-col { transition: none; }
  }

  /* Unseen-orders notification on the Orders tab. Hidden by default;
     JS adds .has-unseen when the unseen-count > 0. The dot pulses to grab
     attention without being intrusive; if there's a count (>1) we show it
     inside as a small badge instead of just a dot. */
  .tab-badge {
    display: none;
    position: relative;
    margin-left: 4px;
    min-width: 8px;
    height: 8px;
    padding: 0;
    border-radius: 99px;
    background: #dc2626;
    box-shadow: 0 0 0 0 rgba(220, 38, 38, 0.55);
    animation: tabBadgePulse 1.4s ease-out infinite;
    font-size: 10px;
    font-weight: 800;
    color: #fff;
    line-height: 1;
    align-items: center;
    justify-content: center;
  }
  /* Un-scoped from `.tabs button` so the same badge works on the topbar Inbox
     icon (.topbar-ico) and its dropdown items (.acct-item), not just rail tabs. */
  .has-unseen .tab-badge {
    display: inline-flex;
  }
  /* When the count is shown (≥1 with text content), expand the pill so the
     number fits without clipping. */
  .has-unseen .tab-badge:not(:empty) {
    min-width: 16px;
    height: 16px;
    padding: 0 5px;
  }
  @keyframes tabBadgePulse {
    0%   { box-shadow: 0 0 0 0   rgba(220, 38, 38, 0.55); transform: scale(1); }
    60%  { box-shadow: 0 0 0 8px rgba(220, 38, 38, 0);    transform: scale(1.08); }
    100% { box-shadow: 0 0 0 0   rgba(220, 38, 38, 0);    transform: scale(1); }
  }

  /* ══ Rail drill-down (Marketing, Loyalty & wallet, Content) ══════════════
     Rewritten 2026-07-29 — `tacet-rail-drilldown-amendment.md`. The flyout is
     GONE: a group no longer opens a full-height panel beside the rail, it
     REPLACES the rail's own contents with its children. One column, one level
     at a time. Two states only, and depth is permanently one — no group holds
     a group, so there is no recursive stack here and none should be built.

     Why not an accordion (the other way to kill the flyout): the rail is 14
     rows at root, and Marketing open would make it 21 ≈ 980px, which scrolls
     on the 1080p laptop most merchants use. Drill-down caps any level at 9.
     Re-open that decision only if the ROOT rail gets substantially shorter. */
  .tabs-group { position: relative; display: flex; flex-direction: column; }
  /* CURRENT (the group your active page lives in, sitting at root) = quiet fill
     + a 3px Ink edge bar. There is no "open" treatment any more: an open group
     is the only thing in the column, so nothing needs distinguishing from its
     siblings — it becomes the back row instead (see .rail-open below). */
  .tabs-group.has-active:not(.rail-open) > .tabs-group-trigger {
    background: rgba(var(--td-heat-rgb), .05); color: var(--td-text);
    font-weight: 500;
  }
  .tabs-group.has-active:not(.rail-open) > .tabs-group-trigger::before {
    content: ""; position: absolute; left: 0; top: 7px; bottom: 7px;
    width: 3px; border-radius: 0 3px 3px 0; background: var(--td-text);
  }
  /* The trailing chevron is the ONLY thing marking a folder apart from a
     destination at root, so it stays exactly as it was. It goes at the drilled
     level (nothing left to open) and on a flattened group (no folder at all). */
  .tabs-chev { width: 10px !important; height: 10px !important; opacity: .6; }
  .tabs-group.rail-open .tabs-chev,
  .tabs-group.rail-flat .tabs-chev { display: none; }
  /* The back arrow is the mirror: hidden at root, and at the drilled level it
     takes the group icon's slot so `← Marketing` reads as one control — the
     level title AND the way out, one row, not two. */
  .tabs-back { display: none; }
  .tabs-group.rail-open > .tabs-group-trigger > .tabs-back { display: block; }
  .tabs-group.rail-open > .tabs-group-trigger > .tabs-gicon { display: none; }

  /* ── Drilled: the rail shows one folder and nothing else ────────────────
     Root rows are hidden outright (not scrolled away) — the fold control and
     the brand spacer stay, because folding from a drilled level is a legal
     move (it returns to root first, in setNavCollapsed). */
  .tabs.rail-drilled > button:not(.seam-fold),
  .tabs.rail-drilled > .tabs-group:not(.rail-open),
  .tabs.rail-drilled > .nav-sec { display: none; }
  /* No `order` override for the open group, and do not add one: every rail row
     carries its order INLINE (style="order:11"), which beats any stylesheet rule.
     None is needed — with the rest of the column hidden, the only flex items left
     are the brand spacer and the fold control (both order:0, both above it), so
     the level lands directly under the fold whatever its own order is. */
  /* Back row: plain, no fill, no edge bar — it is a title, not a destination,
     and the level below it is the thing being pointed at. */
  .tabs-group.rail-open > .tabs-group-trigger {
    background: none; color: var(--td-text); font-weight: 500;
  }
  .tabs-group.rail-open > .tabs-group-trigger::before { content: none; }
  .tabs-group.rail-open > .tabs-group-trigger:hover {
    background: var(--bg-elev-1); color: var(--td-text);
  }

  /* ── The children list ──────────────────────────────────────────────────
     A plain in-flow list under the back row, separated by one hairline. Not a
     panel: no surface of its own, no shadow, no border-radius — it IS the rail
     now. Hidden until its group is the open level. */
  .tabs-popover {
    display: none;
    flex-direction: column;
    gap: 2px;
    margin: 7px 0 0;
    padding: 7px 0 0;
    border-top: 1px solid var(--td-border);
  }
  .tabs-group.rail-open > .tabs-popover { display: flex; }
  /* Children indent so their labels sit on the SAME text edge as the back row's
     label — 12px row padding + a 20px icon + the 13px flex gap = 45px. The level
     title and its items then read as one unit instead of a detached list hanging
     to the left of the title it belongs to. If the rail's row padding, icon size
     or gap ever change, retune this with them.
     The empty left gutter is intentional and reads as calm: children carry NO
     icons (six of the sixteen would need invented glyphs), which is also why
     there is no folded drilled state — there would be nothing to draw. */
  .tabs-popover button {
    width: 100%;
    padding: 9px 12px 9px 45px;
    font-size: 14px;
    border-radius: var(--r-lg);
    justify-content: flex-start;
    font-weight: 400;
    color: var(--td-text2);
    background: none;
    position: relative;
  }
  /* The svg stays in the markup — the phone More sheet clones it (buildMore) —
     it just never shows in the column. */
  .tabs-popover button svg { display: none; }
  .tabs-popover button:hover { color: var(--td-text); background: rgba(var(--td-heat-rgb), .04); }
  /* The current sub-page ("here") — quiet fill, no amber bar (override the rail's
     active ::before) — matching the approved study. */
  .tabs-popover button.active { color: var(--td-text); background: rgba(var(--td-heat-rgb), .05); font-weight: 500; }
  .tabs-popover button.active::before { content: none; }

  /* ── Flattened group (§2.5): one visible child after all three gates ─────
     The folder collapses into a direct button to that child — the child's
     label wearing the parent's icon. One click instead of two, and the
     operator never learns a folder existed. .tabs-flat-label is filled by
     applyCapGates (and refreshed on language change); the group's own label
     steps aside for it. */
  .tabs-flat-label { display: none; }
  .tabs-group.rail-flat > .tabs-group-trigger > .tabs-glabel { display: none; }
  .tabs-group.rail-flat > .tabs-group-trigger > .tabs-flat-label { display: inline; }

  /* ── Motion ─────────────────────────────────────────────────────────────
     A horizontal slide at 200ms ease-out: short, light and frequent, which is
     why it is deliberately NOT the drawer's 350ms settle (a heavier gesture
     over a longer distance, once per record). Recorded here so a later pass
     does not "correct" the two into agreement.
     No `fill: both` — a persisted transform on .tabs-group would make it a
     containing block and clip any fixed menu opened from inside the rail. */
  @keyframes rail-drill-in { from { opacity: 0; transform: translateX(12px); } to { opacity: 1; transform: none; } }
  @keyframes rail-back-in  { from { opacity: 0; transform: translateX(-12px); } to { opacity: 1; transform: none; } }
  .tabs[data-rail-anim="drill"] > .tabs-group.rail-open { animation: rail-drill-in 200ms ease-out; }
  .tabs[data-rail-anim="back"] > button:not(.seam-fold),
  .tabs[data-rail-anim="back"] > .tabs-group { animation: rail-back-in 200ms ease-out; }
  @media (prefers-reduced-motion: reduce) {
    .tabs[data-rail-anim] > button,
    .tabs[data-rail-anim] > .tabs-group { animation: none; }
  }

  /* The level announcer — spoken, never seen. */
  .sr-only {
    position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0;
    overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap;
  }
  /* Live-poll dot + sound toggle now live in the TOPBAR cluster (see
     .tb-cluster), not the rail foot — their selectors below are un-scoped
     from `.tabs >` so they style the buttons wherever they sit. */
  /* Tiny live-poll status dot — sits in the tab bar just before the
     settings gear. Green pulsing when live, dim/static when paused.
     Click toggles. No label — the dot IS the indicator.
     We override `.tabs button { padding: 12px 14px }` explicitly so
     the parent button shrinks to a true 28×28 hit target instead of
     stretching the inner dot into a sliver. */
  .tabs-live-dot {
    box-sizing: border-box;
    width: 32px; height: 32px;
    min-width: 32px; max-width: 32px;
    padding: 0;
    border: 0;
    background: transparent;
    cursor: pointer;
    border-radius: 999px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border-bottom: 2px solid transparent; /* parity with .tabs button to avoid baseline shift */
    transition: background var(--t-fast) ease;
    align-self: center;
  }
  .tabs-live-dot:hover { background: var(--bg-elev-1); color: inherit; }
  .tabs-live-dot .dot {
    display: block;
    width: 10px; height: 10px;
    min-width: 10px; min-height: 10px;
    flex: 0 0 10px;
    border-radius: 50%;
    background: var(--accent);   /* Tea Green — the system is live */
    box-shadow: 0 0 0 0 rgba(163, 180, 159, .6);
    animation: tabs-live-dot-pulse 2.5s ease-in-out infinite;
  }
  .tabs-live-dot.paused .dot {
    background: var(--text-4);
    animation: none;
    box-shadow: none;
  }
  @keyframes tabs-live-dot-pulse {
    0%, 100% { box-shadow: 0 0 0 0   rgba(163, 180, 159, .45); }
    50%      { box-shadow: 0 0 0 8px rgba(163, 180, 159, 0); }
  }

  /* New-order sound toggle — same 32×32 icon-button chrome as the live dot. */
  .tabs-sound-toggle {
    box-sizing: border-box;
    width: 32px; height: 32px;
    min-width: 32px; max-width: 32px;
    padding: 0;
    border: 0;
    background: transparent;
    cursor: pointer;
    border-radius: 999px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border-bottom: 2px solid transparent;
    color: var(--text-3);
    transition: background var(--t-fast) ease, color var(--t-fast) ease;
    align-self: center;
  }
  .tabs-sound-toggle:hover { background: var(--bg-elev-1); color: var(--text-2); }
  .tabs-sound-toggle svg { width: 15px; height: 15px; }
  /* Show speaker-on icon when enabled, muted icon when off. Default markup
     shows both; JS flips the `.muted` class on the button. */
  .tabs-sound-toggle .ico-off { display: none; }
  .tabs-sound-toggle.muted .ico-on { display: none; }
  .tabs-sound-toggle.muted .ico-off { display: inline; }
  .tabs-sound-toggle.muted { color: var(--text-4); }

  /* ═══════════════════════════════════════════════════════════════════
     LAYOUT
     ═══════════════════════════════════════════════════════════════════ */
  main { padding: calc(var(--s6) + 56px) var(--s6) var(--s7); max-width: 1440px; margin: 0 0 0 var(--rail-w); }
  section { display: none; }
  /* Embedded editors (Mini App / Product Page) — pin the iframe to the full
     viewport below the topbar (56px) + tab nav (~42px) so it uses the whole
     screen, not main's centered column. position:fixed keeps it flush to the
     rail: left reads --rail-w, so it rides the fold; z-index stays under the
     topbar (12) + nav. (Both are real <iframe>s at width/height:100%, so the
     embedded doc reflows to the new width when the rail folds.) */
  #tab-mini-app-editor, #tab-product-editor {
    position: fixed; top: 56px; left: var(--rail-w); right: 0; bottom: 0;
    margin: 0; padding: 0; z-index: 1;
  }
  .tab-embed-editor .ed-frame {
    display: block; width: 100%; height: 100%; border: 0;
    background: var(--bg-app);
  }
  /* Tab switch (Dashboard / Orders / Invoices …) → the new section drifts up
     and fades in on the same zen ease as the drawers, so navigation feels
     alive rather than a hard cut. display:none→block restarts the anim each
     time a section becomes active. */
  @keyframes tab-fade-in { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: none; } }
  /* No fill-forwards: a held `both`/`forwards` animation that touched transform
     keeps the section as a lingering stacking context, which traps modals
     (.pu-editor-bg, z 300) *inside* the section so a root-level drawer (z 200)
     paints on top of them. The end keyframe equals the section's base style
     (opacity 1, transform none), so dropping the fill changes nothing visually
     but lets the stacking context dissolve once the entrance settles. */
  section.active { display: block; animation: tab-fade-in 1.004s cubic-bezier(.16, 1, .3, 1); }
  @media (prefers-reduced-motion: reduce) { section.active { animation: none; } }

  /* Tacet section label. Was a Full Circle marker (IBM Plex Mono + Aged Brass);
     retargeted to the canonical Tacet eyebrow (--f-sans + muted graphite, mirrors
     .pu2-eyebrow) so every un-migrated tab drops the dead brand look at once.
     Size/margins unchanged → no reflow. Migrated tabs already override this. */
  h2 {
    font-family: var(--f-sans);
    font-size: 11px; font-weight: 500;
    letter-spacing: .14em; text-transform: uppercase;
    color: var(--td-text2);
    margin: var(--s6) 0 var(--s3);
  }
  h2:first-child { margin-top: var(--s2); }
  h3 { font-size: 14px; font-weight: 600; margin-bottom: var(--s2); }

  .row { display: flex; gap: var(--s3); align-items: center; flex-wrap: wrap; }
  /* Base card surface for every tab. Real edge (--line, not the near-invisible
     --line-soft) + the two-layer lift shadow already used on the dashboard KPIs,
     order-detail and customer tiles — so the rest of the backend reads as
     contained/elevated, not as flat rectangles floating on the page. */
  .card {
    background: var(--bg-pane);
    border: 1px solid var(--line);
    border-radius: var(--r-lg);
    padding: var(--s5);
    margin-bottom: var(--s3);
    box-shadow: 0 1px 2px rgba(0,0,0,.04), 0 4px 14px rgba(0,0,0,.05);
    transition: box-shadow .35s var(--ease-calm), transform .35s var(--ease-calm);
  }
  /* Gentle hover raise on every card — the same hand as the dashboard tiles, so
     order/invoice/zone/setting cards all feel alive under the cursor instead of
     reading as flat panels. (Calm 1px lift; suppressed for reduced-motion.) */
  .card:hover {
    box-shadow: 0 2px 4px rgba(18,22,26,.06), 0 12px 28px rgba(18,22,26,.11);
    transform: translateY(-1px);
  }
  /* The dashboard hero is a large live panel, not a tappable tile — keep it
     planted. (The small bento tiles + deep-dive cards still lift via their own
     rules; this only opts the one big panel out of the global card raise.) */
  .db-hero:hover {
    transform: none;
    box-shadow: 0 1px 2px rgba(0,0,0,.04), 0 4px 14px rgba(0,0,0,.05);
  }

  /* ═══════════════════════════════════════════════════════════════════
     SHARED COMPONENT LAYER (.fc-*) — promoted from the dashboard db-* pass
     so every tab speaks one design language: mono-caps section markers,
     lift-shadow tiles with a hover raise, headline tabular numbers, calm
     deltas (down = clay, never alarm-red), inline magnitude bars, scrubbable
     sparklines, and one calm-motion easing. Values are copied verbatim from
     the dashboard so the look is identical; pages opt in by using these
     classes instead of inventing per-tab prefixes.
     ═══════════════════════════════════════════════════════════════════ */

  /* Calm-motion tokens — the long, settling easing the dashboard uses for
     expands/slide-ins. One var so every page animates with the same hand. */
  :root { --ease-calm: cubic-bezier(.33,1,.68,1); }

  /* ── Page head: title row + actions (lifted from .db-head) ───────────── */
  .fc-pagehead {
    display: flex; align-items: center; justify-content: space-between;
    flex-wrap: wrap; gap: var(--s3); margin-bottom: var(--s4);
  }
  .fc-pagehead h2, .fc-pagehead h3 { margin: 0; }
  .fc-pagehead-actions { display: flex; align-items: center; gap: var(--s2); flex-wrap: wrap; }

  /* The signature section marker — Aged-Brass-neutral mono caps. Use on any
     small "this is a section" label (was .db-tile-head h4 / .cu-tile-title). */
  .fc-eyebrow {
    font-family: var(--f-mono);
    font-size: 10px; font-weight: 700; letter-spacing: .08em;
    text-transform: uppercase; color: var(--text-2);
  }

  /* In-page section header — slightly larger sibling of .fc-eyebrow for the
     <h3>-level "this card is a section" labels (Delivery's group cards etc.).
     Mono caps so section rhythm matches the dashboard. */
  .fc-sec {
    font-family: var(--f-mono);
    font-size: 11px; font-weight: 700; letter-spacing: .08em;
    text-transform: uppercase; color: var(--text-2);
  }

  /* Helper lede directly beneath a page title — one consistent pull-up so the
     descriptive line hugs the heading the same way on every page. */
  .fc-lede { margin-top: calc(-1 * var(--s2)); margin-bottom: var(--s4); }

  /* ── Tile: the elevated card with a mono-caps head + hover raise ─────── */
  .fc-tile {
    position: relative; display: flex; flex-direction: column;
    background: var(--bg-pane);
    border: 1px solid var(--line);
    border-radius: var(--r-lg);
    overflow: hidden; min-height: 0;
    box-shadow: 0 1px 2px rgba(18,22,26,.05), 0 8px 22px rgba(18,22,26,.07);
    transition: box-shadow .35s var(--ease-calm), transform .35s var(--ease-calm);
  }
  .fc-tile:hover {
    box-shadow: 0 2px 4px rgba(18,22,26,.06), 0 12px 28px rgba(18,22,26,.11);
    transform: translateY(-1px);
  }
  .fc-tile-head {
    display: flex; align-items: center; justify-content: space-between;
    gap: 8px; padding: 9px 14px; border-bottom: 1px solid var(--line-soft);
    flex-shrink: 0;
  }
  .fc-tile-head h4 {
    margin: 0; font-family: var(--f-mono);
    font-size: 10px; font-weight: 700; letter-spacing: .08em;
    text-transform: uppercase; color: var(--text-2);
  }
  .fc-tile-ttl { display: inline-flex; align-items: center; gap: 6px; min-width: 0; }
  .fc-tile-ic { flex: none; width: 13px; height: 13px; color: var(--text-3); display: inline-flex; }
  .fc-tile-ic svg { width: 13px; height: 13px; }
  .fc-tile-body { flex: 1 1 auto; min-height: 0; padding: 12px 14px; overflow-y: auto; }
  .fc-tile-num { font-size: 28px; font-weight: 600; letter-spacing: -.02em; color: var(--text-1); line-height: 1; font-variant-numeric: tabular-nums; }
  .fc-tile-num.accent { color: var(--accent); }
  .fc-tile-num.good   { color: var(--accent); }
  .fc-tile-num.warn   { color: var(--warn); }
  .fc-tile-sub { font-size: 11px; color: var(--text-3); margin-top: 4px; }
  .fc-tile-foot { font-size: 10px; color: var(--text-3); font-family: var(--f-mono); letter-spacing: .04em; margin-top: 8px; padding-top: 6px; border-top: 1px solid var(--line-soft); }
  /* Attention state — a left brass rail + a faint head wash (was .db-tile.alert). */
  .fc-tile.alert { border-color: rgba(201,149,74,.42); }
  .fc-tile.alert::before { content: ""; position: absolute; left: 0; top: 0; bottom: 0; width: 3px; background: var(--warn); z-index: 1; }
  .fc-tile.alert .fc-tile-head { background: rgba(201,149,74,.08); }

  /* ── KPI strip: the summary numbers that head a data page ────────────── */
  .fc-kpis { display: grid; grid-template-columns: repeat(auto-fit, minmax(170px, 1fr)); gap: var(--s3); margin-bottom: var(--s4); }
  .fc-kpi {
    position: relative; background: var(--bg-pane);
    border: 1px solid var(--line); border-radius: var(--r-lg);
    padding: 14px 16px; display: flex; flex-direction: column; gap: 6px;
    box-shadow: 0 1px 2px rgba(18,22,26,.05), 0 8px 22px rgba(18,22,26,.07);
    transition: box-shadow .35s var(--ease-calm), transform .35s var(--ease-calm);
  }
  /* Gentle hover raise — same hand as .fc-tile / the dashboard tiles, so every
     summary card feels alive under the cursor, not a flat panel. */
  .fc-kpi:hover {
    box-shadow: 0 2px 4px rgba(18,22,26,.06), 0 12px 28px rgba(18,22,26,.11);
    transform: translateY(-1px);
  }
  .fc-kpi .lbl { font-family: var(--f-mono); font-size: 10px; font-weight: 700; letter-spacing: .08em; text-transform: uppercase; color: var(--text-3); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
  .fc-kpi .val { font-size: 30px; font-weight: 500; letter-spacing: -.02em; color: var(--text-1); font-variant-numeric: tabular-nums; line-height: 1; white-space: nowrap; }
  .fc-kpi .sub { font-size: 11px; color: var(--text-3); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
  .fc-kpi-foot { display: flex; align-items: baseline; gap: 6px; min-height: 15px; min-width: 0; }
  .fc-kpi-foot .sub { min-width: 0; }
  .fc-kpi.accent::before { content: ""; position: absolute; left: 0; top: 12px; bottom: 12px; width: 3px; border-radius: 0 3px 3px 0; background: var(--accent); }
  .fc-kpi.accent .val { color: var(--accent); }
  /* Attention state — same brass rail language as .fc-tile.alert, for an
     SLA-style "this one's been waiting" signal (e.g. oldest pending fapiao). */
  .fc-kpi.alert { border-color: rgba(201,149,74,.42); }
  .fc-kpi.alert::before { content: ""; position: absolute; left: 0; top: 12px; bottom: 12px; width: 3px; border-radius: 0 3px 3px 0; background: var(--warn); }
  .fc-kpi.alert .val { color: var(--warn); }

  /* ═══════════════════════════════════════════════════════════════════
     SALES — channel overview (#tab-sales)
     ═══════════════════════════════════════════════════════════════════ */
  /* Small gold section label, reused for SUMMARY and CHANNELS headers. */
  .sales-eyebrow {
    font-family: var(--f-mono); font-size: 10px; font-weight: 700;
    letter-spacing: .12em; text-transform: uppercase; color: var(--brass);
    margin: var(--s5) 0 var(--s3);
  }
  #tab-sales .fc-kpis + .sales-eyebrow { margin-top: var(--s6); }
  .sales-of { font-size: 16px; color: var(--text-3); font-weight: 400; }

  /* Compact cube grid — packs 2–3 channels per row so the whole channel list
     fits without deep scrolling; collapses to 1 column on narrow screens. */
  .sales-grid {
    display: grid; gap: var(--s3);
    grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
    align-items: start;
  }
  .sales-ch {
    background: var(--bg-pane);
    border: 1px solid var(--line); border-radius: var(--r-lg);
    padding: 13px 14px;
    box-shadow: 0 1px 2px rgba(18,22,26,.05), 0 8px 22px rgba(18,22,26,.07);
    transition: opacity var(--t-fast) ease, box-shadow .35s var(--ease-calm), transform .35s var(--ease-calm);
  }
  .sales-ch:hover {
    box-shadow: 0 2px 4px rgba(18,22,26,.06), 0 12px 28px rgba(18,22,26,.11);
    transform: translateY(-1px);
  }
  /* A disabled (off / not-connected) channel reads dimmer. */
  .sales-ch[data-on="0"] { opacity: .62; }
  .sales-ch-top { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
  .sales-ch-id { display: flex; align-items: center; gap: 10px; min-width: 0; }
  .sales-ch-ico {
    width: 38px; height: 38px; border-radius: 11px; flex: none;
    display: flex; align-items: center; justify-content: center;
    background: var(--bg-elev-1); color: var(--text-2);
  }
  .sales-ch-ico svg { width: 20px; height: 20px; }
  .sales-ch-ico.wc { background: #E1F0E4; color: #2E7D46; }
  .sales-ch-ico.wb { background: #E8ECF1; color: #33506E; }
  .sales-ch-ico.mt { background: #FBE7D6; color: #C2410C; }
  .sales-ch-ico.gy { background: #E2EBDA; color: #3B6D11; }
  .sales-ch-ico.el { background: #E4ECF5; color: #185FA5; }
  /* A quiet violet: the five above already take green, slate, orange, olive
     and blue, and a channel card is identified at a glance by this chip. */
  .sales-ch-ico.ps { background: #ECE7F2; color: #5C4A75; }
  .sales-ch-name { font-size: 14px; font-weight: 500; color: var(--text-1); line-height: 1.2; }
  .sales-ch-stat { font-size: 11px; color: var(--text-3); margin-top: 1px; display: flex; align-items: center; gap: 5px; }
  .sales-dot { width: 6px; height: 6px; border-radius: 50%; flex: none; display: inline-block; }
  .sales-dot.ok { background: var(--ok, #3B6D11); }
  .sales-dot.off { background: var(--text-4, #b4b2a9); }
  .sales-dot.warn { background: var(--brass); }
  .sales-dot.err { background: var(--danger, #e8556a); }

  /* On/off switch — DISABLED for now (channel open/close API not yet wired).
     Rendered so the control's place and intent are visible; not-allowed cursor
     + a tooltip make the "coming soon" state clear. */
  .sales-toggle {
    width: 44px; height: 26px; border-radius: 999px; flex: none;
    background: var(--line); border: 0; position: relative; padding: 0;
    cursor: pointer; transition: background var(--t-fast) ease;
  }
  .sales-toggle.on { background: var(--success); }
  /* Mixed state: open in some locations, closed in others. */
  .sales-toggle.partial { background: linear-gradient(90deg, var(--success) 50%, var(--line) 50%); }
  .sales-toggle .knob {
    position: absolute; top: 3px; left: 3px; width: 20px; height: 20px;
    border-radius: 50%; background: #fff; transition: left var(--t-fast) ease;
    box-shadow: 0 1px 2px rgba(0,0,0,.2);
  }
  .sales-toggle.on .knob { left: 21px; }
  .sales-toggle.partial .knob { left: 12px; }
  /* Locked = channel-status API not yet loaded; unlocked at runtime by csRender. */
  .sales-toggle.is-locked,
  .sales-toggle:disabled { opacity: .55; cursor: not-allowed; }

  /* 2×2 metric block — fits the narrow cube; a wide single row would overflow. */
  .sales-ch-metrics {
    display: grid; grid-template-columns: repeat(2, 1fr); gap: 8px 12px;
    margin: 11px 0; padding: 10px 0;
    border-top: 1px solid var(--line-soft); border-bottom: 1px solid var(--line-soft);
  }
  .sales-ch-metrics .m-l { font-size: 10px; color: var(--text-3); }
  .sales-ch-metrics .m-n { font-size: 15px; font-weight: 500; color: var(--text-1); margin-top: 2px; font-variant-numeric: tabular-nums; }
  .sales-ch-foot { display: flex; gap: 8px; flex-wrap: wrap; }
  .sales-ch-foot .btn { display: inline-flex; align-items: center; gap: 6px; }

  /* ═══════════════════════════════════════════════════════════════════
     DELIVERY HUB (#tab-delivery) — card hub + sub-views (2026-07-03)
     Reuses the Sales-page .sales-ch card language; dlvGo() in 06-delivery.js
     flips [hidden] on the .dlv-view wrappers. Desktop-only: the phone hub
     (#rm-dlv-hub) swallows all of this under 720px.
     ═══════════════════════════════════════════════════════════════════ */
  #tab-delivery .dlv-view[hidden] { display: none !important; }
  /* The zones KPI strip is a DIRECT child of the section (the ≤720px phone
     rule needs it there) — on desktop it belongs to the zones view only. */
  @media (min-width: 721px) {
    #tab-delivery:not([data-dlv="zones"]) > #dz-kpis { display: none; }
  }
  /* Hub cards: equal heights, blurb fills, footer pinned with a soft rule. */
  #tab-delivery .sales-ch { display: flex; flex-direction: column; }
  #tab-delivery .sales-ch-blurb { font-size: 12.5px; color: var(--text-2); line-height: 1.5; padding: 8px 0 10px; flex: 1; }
  #tab-delivery .sales-ch-foot { border-top: 1px solid var(--line-soft); padding-top: 11px; margin-top: auto; align-items: center; }
  #tab-delivery .sales-ch-foot .meta { font-size: 11px; color: var(--text-4); margin-left: auto; text-align: right; }
  #tab-delivery .sales-eyebrow { margin-top: var(--s5); }

  /* Sub-view back link. */
  .dlv-back {
    display: inline-flex; align-items: center; gap: 6px;
    background: none; border: none; padding: 2px 0; margin-bottom: 6px;
    font-family: var(--f-sans); font-size: 12.5px; color: var(--text-3); cursor: pointer;
  }
  .dlv-back:hover { color: var(--text-1); }
  .dlv-back svg { width: 13px; height: 13px; }

  /* New-store setup strip (hidden once a zone or the default fallback exists). */
  .dlv-setup {
    display: flex; align-items: center; flex-wrap: wrap; gap: 10px 0;
    background: var(--bg-pane); border: 1px solid var(--line);
    border-radius: var(--r-lg); padding: 14px 18px; margin-bottom: var(--s4);
  }
  .dlv-setup .lead { font-size: 13px; font-weight: 600; color: var(--text-1); margin-right: 18px; }
  .dlv-setup .lead .m { display: block; font-size: 11px; font-weight: 400; color: var(--text-3); }
  .dlv-step {
    display: flex; align-items: center; gap: 9px; min-width: 0;
    background: none; border: 1px solid transparent; border-radius: var(--r-md);
    padding: 6px 10px; margin: -6px 0; font: inherit; text-align: left;
    cursor: pointer; transition: background .12s, border-color .12s;
  }
  .dlv-step:hover { background: var(--bg-hover); border-color: var(--line); }
  .dlv-step:hover .n { border-color: var(--brass); color: var(--brass); }
  .dlv-step .n {
    width: 22px; height: 22px; border-radius: 50%; flex: none;
    border: 1.5px solid var(--line-strong); color: var(--text-3);
    font-family: var(--f-mono); font-size: 11px; font-weight: 700;
    display: flex; align-items: center; justify-content: center;
    transition: border-color .12s, color .12s;
  }
  .dlv-step .t { font-size: 12.5px; color: var(--text-2); line-height: 1.3; }
  .dlv-step .t small { display: block; font-size: 11px; color: var(--text-4); }
  .dlv-step-arrow { color: var(--text-4); margin: 0 14px; flex: none; }

  /* Delivery hub card in its "off / not set up" state — dimmed so the pickup
     card reads as the live one and delivery as the opt-in. */
  .dlv-hc.dlv-hc-off { background: var(--bg-elev-1); }
  .dlv-hc.dlv-hc-off .sales-ch-blurb { color: var(--text-3); }

  /* ── Delivery set-up wizard (dlvWiz*) ── */
  .dlvwiz-ov {
    position: fixed; inset: 0; z-index: 4200; display: none;
    align-items: center; justify-content: center; padding: 20px;
    background: rgba(10,12,14,.55);
  }
  .dlvwiz-ov.open { display: flex; }
  .dlvwiz {
    background: var(--bg-pane); border: 1px solid var(--line);
    border-radius: var(--r-xl, 14px); width: 100%; max-width: 480px;
    box-shadow: 0 24px 70px rgba(0,0,0,.4); overflow: hidden;
    animation: dlvwiz-in .18s ease;
  }
  @keyframes dlvwiz-in { from { transform: translateY(8px) scale(.98); opacity: 0; } to { transform: none; opacity: 1; } }
  .dlvwiz-head { padding: 18px 20px 0; }
  .dlvwiz-head b { font-size: 16px; color: var(--text-1); display: block; }
  .dlvwiz-steps { display: flex; gap: 6px; margin-top: 14px; }
  .dlvwiz-steps i { height: 4px; border-radius: 99px; flex: 1; background: var(--line-strong, var(--line)); transition: background .2s; }
  .dlvwiz-steps i.done { background: var(--brass); }
  .dlvwiz-body { padding: 16px 20px 4px; }
  .dlvwiz-q { font-size: 15px; font-weight: 700; color: var(--text-1); margin: 0 0 4px; }
  .dlvwiz-sub { font-size: 12.5px; color: var(--text-3); margin: 0 0 14px; line-height: 1.5; }
  .dlvwiz-choice {
    display: flex; align-items: flex-start; gap: 12px; width: 100%; text-align: left;
    border: 1.5px solid var(--line); border-radius: 12px; padding: 13px 14px;
    background: var(--bg-pane); cursor: pointer; margin-bottom: 10px;
    font: inherit; color: var(--text-1); transition: border-color .13s, background .13s;
  }
  .dlvwiz-choice:hover { border-color: var(--text-3); }
  .dlvwiz-choice.sel { border-color: var(--brass); background: color-mix(in srgb, var(--brass) 8%, var(--bg-pane)); box-shadow: 0 0 0 3px color-mix(in srgb, var(--brass) 18%, transparent); }
  .dlvwiz-radio {
    width: 18px; height: 18px; border-radius: 50%; border: 2px solid var(--line-strong, var(--line));
    flex: none; margin-top: 1px; position: relative; transition: border-color .13s;
  }
  .dlvwiz-choice.sel .dlvwiz-radio { border-color: var(--brass); }
  .dlvwiz-choice.sel .dlvwiz-radio::after { content: ""; position: absolute; inset: 3px; border-radius: 50%; background: var(--brass); }
  .dlvwiz-cl { flex: 1; min-width: 0; }
  .dlvwiz-ct { font-size: 14px; font-weight: 700; color: var(--text-1); }
  .dlvwiz-cs { font-size: 12px; color: var(--text-3); margin-top: 2px; line-height: 1.45; }
  .dlvwiz-inl { display: none; margin-top: 10px; flex-wrap: wrap; gap: 6px; align-items: center; font-size: 12.5px; font-weight: 600; color: var(--text-2); }
  .dlvwiz-choice.sel .dlvwiz-inl { display: flex; }
  .dlvwiz-in {
    border: 1px solid var(--line); border-radius: 8px; background: var(--bg-elev-1);
    color: var(--text-1); font: inherit; font-size: 13.5px; font-weight: 700;
    padding: 6px 8px; width: 66px;
  }
  .dlvwiz-in.wide { width: 150px; font-weight: 600; }
  .dlvwiz-in:focus { outline: none; border-color: var(--brass); }
  .dlvwiz-foot {
    display: flex; align-items: center; justify-content: space-between;
    gap: 10px; padding: 14px 20px 18px; margin-top: 6px; border-top: 1px solid var(--line);
  }

  /* "Advanced" pocket on the zones view — rare settings, collapsed. */
  .dlv-adv { margin: var(--s3) 0 var(--s4); }
  .dlv-adv > summary {
    cursor: pointer; font-size: 12.5px; color: var(--text-3);
    padding: 8px 2px; user-select: none;
  }
  .dlv-adv > summary:hover { color: var(--text-1); }
  .dlv-adv[open] > summary { color: var(--text-1); margin-bottom: 4px; }

  /* "Close a whole location" bar — injected by csRenderLocations(). A soft card
     holding one pill per location; the pill fills brass when that location is
     fully closed. */
  .sales-locbar {
    display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
    background: var(--bg-pane); border: 1px solid var(--line);
    border-radius: var(--r-xl); padding: 13px 16px; margin: 0 0 var(--s4);
    box-shadow: 0 1px 2px rgba(18,22,26,.05);
  }
  .sales-locbar-lead {
    font-weight: 600; font-size: 13px; color: var(--text-1);
    display: inline-flex; align-items: center; gap: 8px;
  }
  .sales-locbar-lead svg { width: 17px; height: 17px; color: var(--brass); }
  .sales-locbar-pills { display: flex; gap: 10px; flex-wrap: wrap; margin-left: auto; }
  .sales-locpill {
    display: inline-flex; align-items: center; gap: 9px;
    border: 1px solid var(--line); background: var(--bg-elev-1);
    border-radius: 999px; padding: 8px 15px; cursor: pointer;
    font: inherit; font-size: 13px; color: var(--text-1);
    transition: border-color var(--t-fast) ease, background var(--t-fast) ease;
  }
  .sales-locpill:hover { border-color: var(--text-3); }
  .sales-locpill .nm { font-weight: 600; }
  .sales-locpill .act { color: var(--text-3); }
  .sales-locpill.closed { background: var(--brass); border-color: var(--brass); color: #fff; }
  .sales-locpill.closed .act { color: rgba(255,255,255,.85); }

  /* Clickable catalogue-health tiles double as quick status filters. The
     cursor + a slightly firmer hover say "press me"; .pa-kpi-on is the
     selected state (accent ring), cleared by clicking the same tile again. */
  .fc-kpi.is-clickable { cursor: pointer; user-select: none; }
  .fc-kpi.is-clickable:active { transform: translateY(0); }
  .fc-kpi.is-clickable:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
  .fc-kpi.pa-kpi-click { cursor: pointer; user-select: none; }
  .fc-kpi.pa-kpi-click:active { transform: translateY(0); }
  .fc-kpi.pa-kpi-on { border-color: var(--accent); box-shadow: 0 0 0 1px var(--accent) inset, 0 2px 4px rgba(18,22,26,.06), 0 12px 28px rgba(18,22,26,.11); }
  .fc-kpi.pa-kpi-on.alert { border-color: var(--warn); box-shadow: 0 0 0 1px var(--warn) inset, 0 2px 4px rgba(18,22,26,.06), 0 12px 28px rgba(18,22,26,.11); }

  /* ── Delta: change vs the prior period. The calm rule — a down delta is
     soft clay, never alarm-red, so the page never reads as an emergency. ── */
  .fc-delta {
    display: inline-flex; align-items: center; gap: 3px;
    font-size: 11px; font-weight: 600; line-height: 1; white-space: nowrap;
    font-variant-numeric: tabular-nums; font-family: var(--f-mono);
    letter-spacing: .02em; cursor: help;
  }
  .fc-delta.is-up   { color: var(--accent); }
  .fc-delta.is-down { color: #bf8a6e; }            /* soft clay — a calm "down" */
  .fc-delta.is-flat { color: var(--text-3); opacity: .7; }

  /* ── Magnitude-bar list: a chart without the chart. Each row gets a
     left-anchored tint scaled to the tile's max via inline --mag (0–1). ── */
  .fc-barlist { font-size: 12px; }
  .fc-barrow { display: flex; align-items: center; gap: 8px; padding: 5px 0; border-bottom: 1px dotted var(--line-soft); }
  .fc-barrow:last-child { border-bottom: 0; }
  .fc-barrow .name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .fc-barrow .val  { color: var(--text-2); font-variant-numeric: tabular-nums; flex: none; font-weight: 500; }
  .fc-barrow.has-bar { position: relative; }
  .fc-barrow.has-bar::before {
    content: ""; position: absolute; left: -6px; top: 2px; bottom: 2px;
    width: calc(var(--mag, 0) * (100% + 6px));
    background: color-mix(in srgb, var(--accent) 13%, transparent);
    border-radius: 0 3px 3px 0; z-index: 0; pointer-events: none;
    transition: width .5s cubic-bezier(.22,.61,.36,1);
  }
  .fc-barrow.has-bar .name, .fc-barrow.has-bar .val { position: relative; z-index: 1; }
  .fc-barrow.is-zero .name, .fc-barrow.is-zero .val { color: var(--text-3); opacity: .7; }
  .fc-barrow.is-zero.has-bar::before { display: none; }

  /* ── Dotted list rows (name + value), the plain sibling of the bar list ─ */
  .fc-listrow { display: flex; align-items: center; gap: 8px; padding: 4px 0; border-bottom: 1px dotted var(--line-soft); font-size: 12px; }
  .fc-listrow:last-child { border-bottom: 0; }
  .fc-listrow .name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .fc-listrow .val  { color: var(--text-2); font-variant-numeric: tabular-nums; flex: none; font-weight: 500; }
  .fc-list-empty { font-size: 12px; color: var(--text-3); padding: 10px 0; text-align: center; }

  /* ── Sparkline: small inline trend with a breathing "you-are-here" dot,
     and an optional hover-scrub tip (lifted from .db-spark*). ──────────── */
  .fc-spark-wrap { position: relative; margin-top: 8px; height: 24px; color: var(--td-text2); cursor: crosshair; }
  .fc-spark-wrap.sm { height: 16px; margin-top: 6px; opacity: .85; }
  .fc-spark { display: block; width: 100%; height: 100%; }
  .fc-spark-dot { position: absolute; width: 5px; height: 5px; margin: -2.5px 0 0 -2.5px; border-radius: 50%; background: var(--accent); pointer-events: none; }
  /* Tacet flat spark — thin graphite line + ink end-dot, no filled wash (the
     shared dashSparkline emits .db-spark-* here; match .td-spark on the
     dashboard instead of the old tea-green filled look). */
  .fc-spark-wrap .db-spark-area { display: none; }
  .fc-spark-wrap .db-spark-line { opacity: .7; }
  .fc-spark-wrap .db-spark-dot { background: var(--td-text); }
  .fc-spark-dot.is-breathing { box-shadow: 0 0 0 0 rgba(163,180,159,.45); animation: fcSparkBreathe 2.6s ease-out infinite; }
  .fc-spark-dot.is-active { animation: none; transform: scale(1.5); transition: left .06s linear, top .06s linear; box-shadow: 0 0 0 3px rgba(163,180,159,.18); }
  @keyframes fcSparkBreathe { 0% { box-shadow: 0 0 0 0 rgba(163,180,159,.45); } 70% { box-shadow: 0 0 0 6px rgba(163,180,159,0); } 100% { box-shadow: 0 0 0 0 rgba(163,180,159,0); } }
  .fc-spark-tip { position: absolute; z-index: 60; transform: translate(-50%, -100%); margin-top: -4px; padding: 8px 11px; background: var(--td-text, #111A2B); color: var(--td-paper, #FBF9F4); border: 0; border-radius: 8px; font: 400 12px var(--f-sans); line-height: 1.4; font-variant-numeric: tabular-nums; white-space: nowrap; pointer-events: none; opacity: 0; transition: opacity .12s ease; box-shadow: 0 4px 12px rgba(17,26,43,.08), 0 12px 32px rgba(17,26,43,.18); }
  .fc-spark-tip.show { opacity: 1; }

  /* ── Calm "new row" slide-in — fires once when a fresh row arrives ───── */
  @keyframes fc-row-in { from { opacity: 0; transform: translateY(-6px); } to { opacity: 1; transform: translateY(0); } }
  .fc-row-in { animation: fc-row-in 2s var(--ease-calm) 1; }

  @media (prefers-reduced-motion: reduce) {
    .fc-tile, .fc-kpi, .card { transition: none; }
    .fc-tile:hover, .fc-kpi:hover, .card:hover { transform: none; }
    .fc-spark-dot.is-breathing { animation: none; }
    .fc-row-in { animation: none; }
    .fc-barrow.has-bar::before { transition: none; }
  }

  /* ═══════════════════════════════════════════════════════════════════
     DASHBOARD TAB — channel analytics overview
     ═══════════════════════════════════════════════════════════════════ */
  .db-head {
    display: flex; align-items: center; justify-content: space-between;
    flex-wrap: wrap; gap: var(--s3); margin-bottom: var(--s4);
  }
  .db-range { display: flex; align-items: center; gap: var(--s2); flex-wrap: wrap; }
  /* Range bar relocated below the live hero, heading the range-driven cards. */
  .db-rangebar { display: flex; align-items: center; flex-wrap: wrap; gap: var(--s2) var(--s3); }
  .db-range-note { font-size: 11px; color: var(--text-3); margin-left: auto; }
  .db-range .seg {
    display: inline-flex; background: var(--bg-elev-1);
    border: 1px solid var(--line-soft); border-radius: var(--r-md); overflow: hidden;
  }
  .db-range .seg button {
    background: transparent; border: 0; color: var(--text-2);
    padding: 6px 12px; font-size: 12px; cursor: pointer;
    border-right: 1px solid var(--line-soft);
  }
  .db-range .seg button:last-child { border-right: 0; }
  .db-range .seg button.active { background: var(--accent); color: var(--accent-ink); font-weight: 600; }
  .db-range input[type=date] {
    background: var(--bg-input); border: 1px solid var(--line); color: var(--text-1);
    border-radius: var(--r-md); padding: 5px 8px; font-size: 12px; color-scheme: dark;
  }
  :root[data-theme="light"] .db-range input[type=date] { color-scheme: light; }
  .db-select {
    background: var(--bg-input); border: 1px solid var(--line); color: var(--text-1);
    border-radius: var(--r-md); padding: 5px 8px; font-size: 12px; cursor: pointer;
  }
  .db-select:hover { border-color: var(--line-strong); }
  .db-btn {
    background: var(--bg-elev-2); border: 1px solid var(--line); color: var(--text-1);
    border-radius: var(--r-md); padding: 6px 12px; font-size: 12px; cursor: pointer;
  }
  .db-btn:hover { border-color: var(--line-strong); }
  .db-btn.primary { background: var(--brass); color: var(--accent-ink); border-color: var(--brass); font-weight: 600; }

  /* Reports KPI cards (Tacet) — a quiet 4-across grid: small sentence-case
     label, 26px figure, one 12.5px foot line. No accent card, no colour on the
     numbers; the deltas carry the only tint (sage up, amber down — never red). */
  .db-kpis {
    display: grid; grid-template-columns: repeat(4, 1fr);
    gap: 16px; margin-bottom: 16px;
  }
  @media (max-width: 900px) { .db-kpis { grid-template-columns: repeat(2, 1fr); } }
  .db-kpi {
    position: relative;
    background: var(--td-surface); border: 1px solid var(--td-border);
    border-radius: 14px; padding: 18px 20px;
    display: flex; flex-direction: column;
    box-shadow: 0 1px 2px rgba(17,26,43,.05), 0 4px 16px rgba(17,26,43,.04);
  }
  .db-kpi .lbl {
    font-size: 12px; color: var(--td-text2); margin-bottom: 6px;
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  }
  .db-kpi .val { font-size: 26px; font-weight: 500; letter-spacing: -.01em; color: var(--td-text); font-variant-numeric: tabular-nums; line-height: 1.2; white-space: nowrap; }
  .db-kpi .sub { font-size: 12.5px; color: var(--td-text2); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
  /* One uniform foot row per card (delta and/or sub on a single baseline) so
     every card is exactly 3 rows tall — keeps the headline numbers in line. */
  .db-kpi-foot { display: flex; align-items: baseline; gap: 6px; min-height: 19px; min-width: 0; margin-top: 4px; }
  .db-kpi-foot .sub { min-width: 0; }
  .db-delta { display: inline-flex; align-items: center; gap: 3px; font-size: 12.5px; font-weight: 400; font-variant-numeric: tabular-nums; }
  .db-delta.up   { color: var(--td-sage-ink); }
  .db-delta.down { color: var(--td-amber-ink); }
  .db-delta.flat { color: var(--td-text2); }

  /* Right-now hero — sits above the historical KPI strip. Live presence on the
     left, today's mini-KPIs in the middle, activity feed on the right. Polls
     /dashboard/presence (15s) and /dashboard/activity (20s) while the
     dashboard tab is visible. Phase 3 will reshape this into the full bento. */
  /* `display: block` is required to override the global `section { display:none }`
     (the tab system hides every <section>; the hero is a <section> nested inside the
     active dashboard tab, so without its own display it inherits none → collapses to
     0×0. The bento tiles escape this via `.db-bento .db-tile{display:flex}`.) */
  .db-hero { display: block; padding: 14px 18px; margin-bottom: 14px; }
  .db-hero-head { display: flex; align-items: baseline; justify-content: space-between; gap: 8px; margin-bottom: 10px; }
  .db-hero-head h3 { margin: 0; font-size: 12px; letter-spacing: .08em; text-transform: uppercase; color: var(--text-2); font-weight: 700; }
  .db-hero-sync { font-size: 11px; color: var(--text-3); font-variant-numeric: tabular-nums; }
  .db-hero-grid { display: grid; grid-template-columns: 200px 1fr 1.3fr; gap: 14px; align-items: stretch; }
  @media (max-width: 960px) { .db-hero-grid { grid-template-columns: 1fr; } }
  .db-hero-pane { background: var(--bg-app); border: 1px solid var(--line-soft); border-radius: 10px; padding: 12px 14px; display: flex; flex-direction: column; min-width: 0; }
  .db-hero-h { font-size: 10px; letter-spacing: .08em; text-transform: uppercase; color: var(--text-3); font-weight: 700; margin-bottom: 8px; font-family: var(--f-mono); }
  /* Activity feed "show all / collapse" — rows past the preview live in a
     collapsible wrapper that eases open/closed via a grid-rows transition
     (0fr → 1fr), so the list breathes rather than snapping. */
  .db-feed-more { display: grid; grid-template-rows: 0fr; transition: grid-template-rows 1.4s cubic-bezier(.33,1,.68,1); }
  /* Expand is the meditative direction — slower than the collapse. The
     transition of the *target* state wins, so opening uses these longer
     durations while closing falls back to the base 1.4s above. */
  .db-feed-pane.is-expanded .db-feed-more { grid-template-rows: 1fr; transition: grid-template-rows 2.2s cubic-bezier(.33,1,.68,1); }
  .db-feed-more-inner { overflow: hidden; min-height: 0; display: flex; flex-direction: column; gap: 2px; opacity: 0; transform: translateY(-4px); transition: opacity 1.2s ease, transform 1.2s cubic-bezier(.33,1,.68,1); }
  .db-feed-pane.is-expanded .db-feed-more-inner { opacity: 1; transform: translateY(0); transition: opacity 1.9s ease .3s, transform 1.9s cubic-bezier(.33,1,.68,1) .3s; }
  /* The toggle hugs the rows as a quiet footer action with a hairline above. */
  .db-feed-toggle { display: inline-flex; align-items: center; gap: 6px; width: 100%; margin-top: 6px; padding: 8px 2px 0; border: 0; border-top: 1px solid var(--line-soft); background: transparent; cursor: pointer; font-family: var(--f-mono); font-size: 10px; letter-spacing: .06em; text-transform: uppercase; font-weight: 700; color: var(--text-3); }
  .db-feed-toggle:hover { color: var(--accent); }
  .db-feed-toggle::after { content: ""; width: 6px; height: 6px; border-right: 1.4px solid currentColor; border-bottom: 1.4px solid currentColor; transform: rotate(45deg); transition: transform 1.1s cubic-bezier(.33,1,.68,1); margin-top: -2px; }
  .db-feed-toggle.is-open::after { transform: rotate(225deg); margin-top: 2px; transition: transform 1.9s cubic-bezier(.33,1,.68,1); }
  /* Live count — pulsing dot when there are viewers, grey when zero. */
  .db-live { justify-content: center; align-items: center; gap: 4px; }
  /* LIVE ring gauge — a faint track with the viewer count centred; a green arc
     fades in + breathes when viewers are present. This is BOTH the live cue and
     the brand's circle motif, so there's no separate always-on dot (which had
     collided with the tab-bar refresh + orders indicators). */
  .db-live-ring-wrap { position: relative; width: 92px; height: 92px; display: flex; align-items: center; justify-content: center; margin: 2px 0 4px; }
  .db-live-ring { position: absolute; inset: 0; width: 100%; height: 100%; transform: rotate(-90deg); transform-origin: center; animation: db-live-breath 6.5s ease-in-out infinite; }
  /* The inked ring slowly turns on its own axis (a calm, ensō "drawing" drift)
     while the svg above handles the breathing pulse — kept on the inner group
     so the two transforms don't collide. */
  .db-live-ring g { transform-box: view-box; transform-origin: 22px 22px; animation: db-ring-spin 48s linear infinite; }
  @keyframes db-ring-spin { to { transform: rotate(360deg); } }
  .db-live-ring .track { fill: none; stroke: var(--line); stroke-width: 2.5; }
  .db-live-ring .arc { fill: none; stroke: var(--accent); stroke-width: 2.5; stroke-linecap: round; stroke-dasharray: 119.38; stroke-dashoffset: 119.38; opacity: 0; transition: stroke-dashoffset .6s ease, opacity .45s ease; }
  .db-live-ring.live .arc { opacity: 1; stroke-dashoffset: 30; animation: db-ring-breathe 3.6s ease-in-out infinite; }
  /* The ring itself carries a slow ambient breath even at idle — the live
     monitor's "heartbeat" (the 0 + caption still state the literal truth). Once
     viewers are present a brighter green arc breathes over it. */
  @keyframes db-live-breath { 0%, 100% { transform: rotate(-90deg) scale(1); } 50% { transform: rotate(-90deg) scale(1.046); } }
  @keyframes db-ring-breathe { 0%, 100% { opacity: .425; } 50% { opacity: 1; } }
  @media (prefers-reduced-motion: reduce) { .db-live-ring, .db-live-ring g, .db-live-ring.live .arc { animation: none; } }
  .db-live-num { font-size: 38px; line-height: 1; font-weight: 600; letter-spacing: -.02em; color: var(--text-1); font-variant-numeric: tabular-nums; }
  .db-live-num.live-on { color: var(--accent); }
  .db-live-lbl { font-size: 11px; color: var(--text-3); margin-top: 2px; }
  .db-live-sub { font-size: 11px; color: var(--text-3); font-variant-numeric: tabular-nums; margin-top: 4px; }
  /* Today mini-KPI strip — three small cells. */
  /* Revenue is the day's hero number — it spans the top, accented in Tea
     Green so the eye lands here first; orders + new sit smaller beneath. */
  .db-today-grid { display: grid; grid-template-columns: 1fr 1fr; grid-template-areas: "rev rev" "ord new"; gap: 8px; flex: 1; }
  .db-today-cell { background: var(--bg-pane); border: 1px solid var(--line-soft); border-radius: 8px; padding: 8px 10px; display: flex; flex-direction: column; justify-content: center; min-width: 0; }
  .db-today-cell .val { font-size: 18px; font-weight: 600; letter-spacing: -.01em; color: var(--text-1); font-variant-numeric: tabular-nums; line-height: 1.1; }
  .db-today-cell .lbl { font-size: 10px; color: var(--text-3); margin-top: 3px; letter-spacing: .04em; text-transform: uppercase; font-family: var(--f-mono); }
  .db-today-cell.hero { grid-area: rev; position: relative; padding-left: 16px; background: rgba(163,180,159,.07); border-color: rgba(163,180,159,.28); }
  .db-today-cell.hero::before { content: ""; position: absolute; left: 0; top: 10px; bottom: 10px; width: 3px; border-radius: 0 3px 3px 0; background: var(--accent); }
  .db-today-cell.hero .val { font-size: 34px; color: var(--accent); letter-spacing: -.02em; }
  /* Label row pairs the small-caps label with a calm delta-vs-yesterday badge. */
  .db-today-lblrow { display: flex; align-items: baseline; justify-content: space-between; gap: 6px; margin-top: 3px; }
  .db-today-lblrow .lbl { margin-top: 0; }
  .db-delta { position: relative; font-size: 10px; font-weight: 600; line-height: 1; white-space: nowrap; font-variant-numeric: tabular-nums; font-family: var(--f-mono); letter-spacing: .02em; cursor: help; }
  .db-delta.is-up   { color: var(--accent); }
  .db-delta.is-down { color: #bf8a6e; }   /* soft clay — a calm "down", never alarm-red */
  .db-delta.is-flat { color: var(--text-3); opacity: .7; }
  /* Hover warms the cell border toward the accent — the same ensō cue as the live ring. */
  .db-today-cell { transition: border-color .5s ease, background-color .5s ease; }
  .db-today-cell:hover { border-color: rgba(163,180,159,.45); cursor: default; }
  .db-today-cell.hero:hover { background: rgba(163,180,159,.11); }
  /* Activity feed — scrollable list inside the pane, rows hover-highlight + click → drawer. */
  .db-feed-list { display: flex; flex-direction: column; gap: 2px; max-height: 200px; overflow-y: auto; padding-right: 4px; margin-right: -4px; }
  .db-feed-list::-webkit-scrollbar { width: 6px; }
  .db-feed-list::-webkit-scrollbar-thumb { background: var(--line); border-radius: 3px; }
  .db-feed-row { display: flex; align-items: center; gap: 8px; padding: 5px 8px; border-radius: 6px; cursor: pointer; font-size: 12px; line-height: 1.4; border: 1px solid transparent; }
  .db-feed-row:hover { background: var(--bg-elev-1, rgba(0,0,0,.04)); border-color: var(--line-soft); }
  .db-feed-ts { color: var(--text-3); font-variant-numeric: tabular-nums; flex: none; min-width: 38px; font-size: 11px; }
  /* Thin-stroke line icons (not OS emoji) so the feed matches the flat,
     editorial chrome. Tinted by meaning: green = money in, brass = needs
     attention, red = stock alert, muted = informational. */
  .db-feed-icon { flex: none; width: 18px; display: inline-flex; align-items: center; justify-content: center; }
  .db-feed-icon svg { width: 15px; height: 15px; }
  .db-feed-icon.db-fi-ok    { color: var(--success); }
  .db-feed-icon.db-fi-warn  { color: var(--warn); }
  .db-feed-icon.db-fi-bad   { color: var(--danger); }
  .db-feed-icon.db-fi-muted { color: var(--text-3); }
  .db-feed-text { flex: 1; min-width: 0; color: var(--text-1); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .db-feed-text b { font-weight: 600; }
  /* Milestone pill on a paid-order row — first order or unusually big basket. */
  .db-feed-badge { display: inline-flex; align-items: center; gap: 3px; padding: 0 5px; margin-left: 4px; border-radius: 999px; font-size: 10px; font-weight: 600; vertical-align: middle; }
  .db-feed-badge svg { width: 10px; height: 10px; }
  .db-feed-badge.db-fb-first { color: var(--brass); background: color-mix(in srgb, var(--brass) 14%, transparent); }
  .db-feed-badge.db-fb-big   { color: var(--success); background: color-mix(in srgb, var(--success) 14%, transparent); }
  .db-feed-sku { font-size: 11px; color: var(--text-3); font-variant-numeric: tabular-nums; }
  .db-feed-empty { font-size: 12px; color: var(--text-3); padding: 12px 8px; text-align: center; }
  /* New row eases in from the top with a soft highlight that decays — the
     "something just happened" signal, only on rows the latest poll added.
     This is the feed's whole liveness cue; no second always-on dot (the hero
     "Live" pane + tab-bar auto-refresh dot already cover steady liveness). */
  @keyframes db-row-in {
    0%   { opacity: 0; transform: translateY(-6px); background: color-mix(in srgb, var(--success) 16%, transparent); }
    60%  { opacity: 1; transform: translateY(0); background: color-mix(in srgb, var(--success) 16%, transparent); }
    100% { opacity: 1; transform: translateY(0); background: transparent; }
  }
  .db-feed-row.is-new { animation: db-row-in 2s ease-out 1; }
  @media (prefers-reduced-motion: reduce) {
    .db-feed-row.is-new { animation: none; }
  }
  /* Drawer-specific tweaks — the activity drawer reuses the global .side-drawer. */
  #db-activity-drawer .db-dw-meta { display: grid; grid-template-columns: max-content 1fr; gap: 6px 14px; font-size: 13px; margin-bottom: 16px; }
  #db-activity-drawer .db-dw-meta dt { color: var(--text-3); font-size: 11px; text-transform: uppercase; letter-spacing: .06em; font-family: var(--f-mono); align-self: center; }
  #db-activity-drawer .db-dw-meta dd { margin: 0; color: var(--text-1); }
  #db-activity-drawer .db-dw-quote { background: var(--bg-app); border: 1px solid var(--line-soft); border-left: 3px solid var(--brass); border-radius: 6px; padding: 10px 14px; font-size: 13px; line-height: 1.55; color: var(--text-1); white-space: pre-wrap; }

  /* Phase 3 — bento grid above the fold. 4 cols × 3 rows. Hero spans cols
     1–3 × rows 1–2 (big and tall); tiles A/B stack in col 4 above it; tiles
     C–F fill row 3. Below the bento sits the Deep-dive collapse holding the
     legacy KPI strip + 12 chart cards. Layout collapses to a 2-col stack
     under 1080px, full single column under 720px. */
  /* Hero spans cols 1–3 with two tiles stacked beside it (col 4); the range bar
     is a slim full-width row beneath the hero block, separating the always-live
     hero from the range-driven cards below. */
  .db-bento {
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    grid-template-rows: minmax(120px, auto) minmax(120px, auto) auto minmax(190px, auto);
    gap: var(--s3);
    margin-bottom: var(--s4);
  }
  .db-bento #db-hero { grid-column: 1 / 4; grid-row: 1 / 3; margin-bottom: 0; }
  .db-bento .db-tile[data-slot="a"] { grid-column: 4; grid-row: 1; }
  .db-bento .db-tile[data-slot="b"] { grid-column: 4; grid-row: 2; }
  .db-bento #db-rangebar { grid-column: 1 / -1; grid-row: 3; }
  .db-bento .db-tile[data-slot="c"] { grid-column: 1; grid-row: 4; }
  .db-bento .db-tile[data-slot="d"] { grid-column: 2; grid-row: 4; }
  .db-bento .db-tile[data-slot="e"] { grid-column: 3; grid-row: 4; }
  .db-bento .db-tile[data-slot="f"] { grid-column: 4; grid-row: 4; }
  @media (max-width: 1080px) {
    .db-bento { grid-template-columns: repeat(2, 1fr); grid-template-rows: auto; grid-auto-rows: minmax(190px, auto); }
    .db-bento #db-hero { grid-column: 1 / 3; grid-row: auto; }
    .db-bento #db-rangebar { grid-column: 1 / -1; grid-row: auto; }
    .db-bento .db-tile { grid-column: auto; grid-row: auto; }
  }
  @media (max-width: 720px) {
    .db-bento { grid-template-columns: 1fr; }
    .db-bento #db-hero { grid-column: 1; }
  }

  /* Hero internals — when the hero grows tall in the bento, reshape its
     internal layout so the activity feed takes the lower full-width row
     and the live + today panes share the top. Phase 2 used a 1-row 3-col
     layout; this overrides that when sitting inside .db-bento. */
  .db-bento .db-hero-grid {
    grid-template-columns: 220px 1fr;
    grid-template-rows: auto 1fr;
    grid-template-areas:
      "live  today"
      "feed  feed";
    height: 100%;
  }
  .db-bento .db-hero .db-live          { grid-area: live;  }
  .db-bento .db-hero .db-today-pane    { grid-area: today; }
  .db-bento .db-hero .db-feed-pane     { grid-area: feed; min-height: 200px; }
  .db-bento .db-hero .db-feed-list     { max-height: 340px; flex: 0 1 auto; min-height: 0; }
  @media (max-width: 960px) {
    .db-bento .db-hero-grid {
      grid-template-columns: 1fr;
      grid-template-rows: auto auto auto;
      grid-template-areas: "live" "today" "feed";
    }
  }

  /* Bento tile chrome — title bar + ⋯ swap menu + content body.
     Scoped under .db-bento because .db-tile is also used by the legacy
     `dashRenderTiers` mini-cards inside the Deep dive. */
  .db-bento .db-tile { position: relative; display: flex; flex-direction: column; padding: 0; min-height: 0; overflow: hidden; }
  /* Depth — lift the bento cards off the page so they read as objects, not
     flat panels, and float a touch more on hover. The hero sits forward of
     the rest as the centrepiece. */
  .db-bento .db-tile, .db-bento #db-hero {
    box-shadow: 0 1px 2px rgba(18,22,26,.05), 0 6px 20px rgba(18,22,26,.07);
    border-color: var(--line);
    transition: box-shadow .18s ease, transform .18s ease;
  }
  .db-bento #db-hero { box-shadow: 0 2px 6px rgba(18,22,26,.06), 0 16px 38px rgba(18,22,26,.09); }
  .db-bento .db-tile:hover { box-shadow: 0 2px 4px rgba(18,22,26,.06), 0 12px 28px rgba(18,22,26,.11); transform: translateY(-1px); }
  .db-bento .db-tile .db-tile-head { display: flex; align-items: center; justify-content: space-between; padding: 9px 14px; border-bottom: 1px solid var(--line-soft); flex-shrink: 0; }
  .db-bento .db-tile .db-tile-head h4 { margin: 0; font-size: 10px; letter-spacing: .08em; text-transform: uppercase; font-family: var(--f-mono); color: var(--text-2); font-weight: 700; }
  .db-bento .db-tile .db-tile-menu { border: none; background: transparent; cursor: pointer; color: var(--text-3); padding: 0 6px; border-radius: 4px; font-size: 16px; line-height: 1; height: 22px; }
  .db-bento .db-tile .db-tile-menu:hover { color: var(--text-1); background: var(--bg-elev-1); }
  .db-bento .db-tile .db-tile-body { flex: 1 1 auto; min-height: 0; padding: 10px 14px; overflow-y: auto; }
  .db-bento .db-tile .db-tile-num { font-size: 28px; font-weight: 600; letter-spacing: -.02em; color: var(--text-1); line-height: 1; font-variant-numeric: tabular-nums; }
  .db-bento .db-tile .db-tile-num.accent { color: var(--accent); }
  /* Status colour — green when all-clear, brass when something needs the
     operator's attention (low stock, refunds waiting). */
  .db-bento .db-tile .db-tile-num.good { color: var(--accent); }
  .db-bento .db-tile .db-tile-num.warn { color: var(--warn); }
  /* Tiles that need attention carry a brass left edge + a tinted header so
     the eye catches them before the "nice to know" tiles. */
  .db-bento .db-tile.alert { border-color: rgba(201,149,74,.42); }
  .db-bento .db-tile.alert::before { content: ""; position: absolute; left: 0; top: 0; bottom: 0; width: 3px; background: var(--warn); z-index: 1; }
  .db-bento .db-tile.alert .db-tile-head { background: rgba(201,149,74,.08); }
  .db-bento .db-tile .db-tile-sub { font-size: 11px; color: var(--text-3); margin-top: 4px; }
  .db-tile-list { font-size: 12px; }
  .db-tile-list-row { display: flex; align-items: center; gap: 8px; padding: 4px 0; border-bottom: 1px dotted var(--line-soft); }
  .db-tile-list-row:last-child { border-bottom: 0; }
  .db-tile-list-row .name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .db-tile-list-row .val  { color: var(--text-2); font-variant-numeric: tabular-nums; flex: none; font-weight: 500; }
  /* Clickable rows (e.g. Low stock → open that product's editor). */
  .db-tile-list-row.is-link { cursor: pointer; margin: 0 -6px; padding: 4px 6px; border-radius: 6px; transition: background .15s ease; }
  .db-tile-list-row.is-link:hover, .db-tile-list-row.is-link:focus-visible { background: var(--line-soft); outline: none; }
  .db-tile-list-row.is-link:hover .name { color: var(--accent); }
  .db-tile-empty { font-size: 12px; color: var(--text-3); padding: 8px 0; text-align: center; }
  .db-tile-tabs { display: flex; gap: 4px; margin-bottom: 8px; }
  .db-tile-tab { font-size: 11px; font-weight: 500; padding: 3px 8px; border-radius: 20px; border: 1px solid var(--line-soft); background: none; color: var(--text-3); cursor: pointer; transition: background .15s, color .15s, border-color .15s; }
  .db-tile-tab:hover { background: var(--line-soft); color: var(--text-1); }
  .db-tile-tab.active { background: var(--accent); color: #fff; border-color: var(--accent); }

  /* ── Delivery-alerts banner ─────────────────────────────────────────
     Full-width strip above the bento. Rendered only when a dispatched
     order's carrier reported a problem (can't-reach / exception /
     returning), so its mere presence is the alert. Danger-toned rail;
     calm fade-in to match the platform's motion language. */
  .db-delivery-alerts {
    background: var(--bg-pane); border: 1px solid var(--line);
    border-left: 3px solid var(--danger); border-radius: var(--r-lg);
    padding: var(--s3) var(--s4); margin: 0 0 var(--s4);
    box-shadow: var(--sh-1);
    animation: da-fade var(--t-med) var(--ease-out) both;
  }
  @keyframes da-fade { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: none; } }
  @media (prefers-reduced-motion: reduce) { .db-delivery-alerts { animation: none; } }
  .db-da-head { display: flex; align-items: center; gap: var(--s2); margin-bottom: var(--s2); font-size: 13px; font-weight: 600; color: var(--text); }
  .db-da-head b { margin-left: 2px; color: var(--danger); font-variant-numeric: tabular-nums; }
  .db-da-bell { display: inline-flex; align-items: center; }
  .db-da-rows { display: flex; flex-direction: column; gap: 2px; }
  .db-da-row {
    display: flex; align-items: center; gap: var(--s3); width: 100%;
    text-align: left; background: none; border: 0; border-radius: var(--r-md);
    padding: 6px var(--s2); cursor: pointer; color: var(--text); font: inherit;
    transition: background var(--t-fast) ease;
  }
  .db-da-row:hover, .db-da-row:focus-visible { background: var(--line-soft); outline: none; }
  .db-da-ic   { flex: none; display: inline-flex; align-items: center; justify-content: center; width: 18px; }
  .db-da-no   { flex: none; font-family: var(--f-mono); font-size: 12px; font-weight: 600; }
  .db-da-who  { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--text-2); font-size: 12px; }
  .db-da-state{ flex: none; font-size: 12px; font-weight: 600; }
  .db-da-more { padding: 4px var(--s2); font-size: 11px; color: var(--text-2); }
  /* Mini heatmap for the bento variant — squished cells, no labels. */
  /* Mini heatmap — day-of-week rows (labelled) × 24 hour cells, with an hour
     axis beneath. The "Busiest …" caption carries the actual takeaway. */
  /* Let the heatmap fill the tile's full height instead of sitting at its
     small intrinsic (square-cell) size — row-3 tiles stretch to the tallest
     sibling, so otherwise the grid leaves dead space below. */
  .db-bento .db-tile .db-tile-body:has(.db-heat-mini) { display: flex; flex-direction: column; }
  .db-bento .db-tile .db-heat-mini { display: grid; grid-template-columns: 14px 1fr; grid-template-rows: repeat(7, 1fr) auto; gap: 4px 6px; align-items: stretch; flex: 1 1 auto; min-height: 0; }
  .db-bento .db-tile .db-heat-day { font-size: 10px; line-height: 1; color: var(--text-3); font-family: var(--f-mono); text-align: right; align-self: center; }
  .db-bento .db-tile .db-heat-cells { display: grid; grid-template-columns: repeat(24, 1fr); gap: 2px; height: 100%; }
  .db-bento .db-tile .db-heat-cells > i { display: block; width: 100%; height: 100%; min-height: 12px; border-radius: 2px; background: var(--line-soft); }
  .db-bento .db-tile .db-heat-axis { display: flex; justify-content: space-between; font-size: 9px; line-height: 1; color: var(--text-3); font-family: var(--f-mono); align-self: center; }

  /* ── Magnitude bars, period captions, tile icons (data-viz pass) ───────── */
  /* A left-anchored tint behind each list row, scaled to the tile's own max
     (--mag 0–1, set inline by dashBarRows) so rows rank at a glance — a chart
     without the chart. */
  .db-tile-list-row.has-bar { position: relative; }
  .db-tile-list-row.has-bar::before {
    content: ""; position: absolute; left: -6px; top: 2px; bottom: 2px;
    width: calc(var(--mag, 0) * (100% + 6px));
    background: color-mix(in srgb, var(--accent) 13%, transparent);
    border-radius: 0 3px 3px 0; z-index: 0; pointer-events: none;
    transition: width .5s cubic-bezier(.22,.61,.36,1);
  }
  .db-tile-list-row.has-bar .name, .db-tile-list-row.has-bar .val { position: relative; z-index: 1; }
  /* Mute zero rows — "no data", not signal. */
  .db-tile-list-row.is-zero .name, .db-tile-list-row.is-zero .val { color: var(--text-3); opacity: .7; }
  .db-tile-list-row.is-zero.has-bar::before { display: none; }
  /* Period caption anchoring a range tile to the window it's showing. */
  .db-tile-foot { font-size: 10px; color: var(--text-3); font-family: var(--f-mono); letter-spacing: .04em; margin-top: 8px; padding-top: 6px; border-top: 1px solid var(--line-soft); }
  /* Tile-header icon — a separate span so the i18n TreeWalker never rewrites it. */
  .db-tile-ttl { display: inline-flex; align-items: center; gap: 6px; min-width: 0; }
  .db-tile-ic { flex: none; width: 13px; height: 13px; color: var(--text-3); display: inline-flex; }
  .db-tile-ic svg { width: 13px; height: 13px; }

  /* Intraday sparkline under the hero "Today" revenue — revenue-by-hour. The
     last point carries a "you-are-here" dot; the hero's softly breathes so the
     day reads as still-live, the orders one stays static to keep the calm. */
  .db-spark-wrap { position: relative; margin-top: 8px; height: 24px; color: var(--accent); }
  .db-spark-wrap.db-spark-sm { height: 16px; margin-top: 6px; opacity: .85; }
  .db-spark { display: block; width: 100%; height: 100%; }
  .db-spark-dot { position: absolute; width: 5px; height: 5px; margin: -2.5px 0 0 -2.5px; border-radius: 50%; background: var(--accent); pointer-events: none; }
  .db-spark-dot.is-breathing { box-shadow: 0 0 0 0 rgba(163,180,159,.45); animation: dbSparkBreathe 2.6s ease-out infinite; }
  @keyframes dbSparkBreathe { 0% { box-shadow: 0 0 0 0 rgba(163,180,159,.45); } 70% { box-shadow: 0 0 0 6px rgba(163,180,159,0); } 100% { box-shadow: 0 0 0 0 rgba(163,180,159,0); } }
  @media (prefers-reduced-motion: reduce) { .db-spark-dot.is-breathing { animation: none; } }
  /* Hover-scrub: the chart is draggable to read each hour. The dot tracks the
     cursor and a small chip names the hour + value. */
  .db-spark-wrap { cursor: crosshair; }
  .db-spark-dot.is-active { animation: none; transform: scale(1.5); transition: left .06s linear, top .06s linear; box-shadow: 0 0 0 3px rgba(163,180,159,.18); }
  .db-spark-tip { position: absolute; z-index: 60; transform: translate(-50%, -100%); margin-top: -4px; padding: 8px 11px; background: var(--td-text, #111A2B); color: var(--td-paper, #FBF9F4); border: 0; border-radius: 8px; font: 400 12px var(--f-sans); line-height: 1.4; font-variant-numeric: tabular-nums; white-space: nowrap; pointer-events: none; opacity: 0; transition: opacity .12s ease; box-shadow: 0 4px 12px rgba(17,26,43,.08), 0 12px 32px rgba(17,26,43,.18); }
  .db-spark-tip.show { opacity: 1; }
  .db-today-row { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; }

  /* Flatten the right-now panes — inside the bento hero they read as
     card-in-card-in-card; drop their fills/borders so the hero is one surface,
     keeping only the today mini-cells framed. Thin dividers stand in for the
     old pane borders. */
  .db-bento .db-hero .db-hero-pane { background: transparent; border: 0; border-radius: 0; padding: 4px 8px; }
  .db-bento .db-hero .db-today-pane { border-left: 1px solid var(--line-soft); padding-left: 16px; }
  .db-bento .db-hero .db-feed-pane { border-top: 1px solid var(--line-soft); padding-top: 10px; margin-top: 2px; }
  @media (max-width: 960px) {
    .db-bento .db-hero .db-today-pane { border-left: 0; padding-left: 8px; }
  }

  /* The circle motif now lives solely on the breathing LIVE ring around the
     viewer counter — no corner ornament. */

  /* Subtle paper grain in the dashboard gutters — light theme only, so the
     dark theme stays clean. */
  :root[data-theme="light"] #db-bento {
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23n)' opacity='0.04'/%3E%3C/svg%3E");
  }

  /* Tile-swap popover. Floats above on click, dismissed by outside-click. */
  /* Shared "zen" entrance for every floating window that isn't a drawer or the
     centered .pu-editor modal — popovers, dropdowns, anchored menus. One slow
     soft-deceleration beat (the drawer/modal ease) so a window glides open
     instead of snapping. translateY-only (no scale) so JS that measures a
     popover's rect right after opening still reads its true width/height. */
  @keyframes zen-pop-in { from { opacity: 0; transform: translateY(-6px); } to { opacity: 1; transform: none; } }
  @media (prefers-reduced-motion: reduce) { @keyframes zen-pop-in { from { opacity: 1; } to { opacity: 1; } } }
  .db-tile-pop { position: fixed; min-width: 220px; max-width: 280px; background: var(--bg-pane); border: 1px solid var(--line); border-radius: 8px; box-shadow: 0 6px 24px rgba(0,0,0,.18); padding: 6px; z-index: 250; overflow-y: auto; overscroll-behavior: contain; animation: zen-pop-in 540ms cubic-bezier(.16, 1, .3, 1); }
  .db-tile-pop .db-tile-pop-head { font-size: 10px; letter-spacing: .08em; text-transform: uppercase; color: var(--text-3); padding: 6px 8px; font-family: var(--f-mono); }
  .db-tile-pop button { display: flex; align-items: center; gap: 8px; width: 100%; text-align: left; padding: 6px 8px; border: none; background: transparent; color: var(--text-1); font: inherit; font-size: 12px; cursor: pointer; border-radius: 4px; }
  .db-tile-pop button:hover { background: var(--bg-elev-1); }
  .db-tile-pop button.active { background: var(--bg-app); color: var(--accent); font-weight: 600; }

  /* Deep-dive disclosure — wraps the legacy KPI strip + 12 chart cards.
     Starts collapsed; toggle persisted nowhere (always opens fresh, per
     the project recommendation to keep front-door clean). */
  .db-deepdive { margin-top: var(--s4); border: 1px solid var(--line-soft); border-radius: var(--r-lg); background: var(--bg-pane); overflow: hidden; }
  .db-deepdive > summary { list-style: none; cursor: pointer; padding: 12px 16px; display: flex; align-items: center; gap: 10px; font-size: 13px; font-weight: 600; color: var(--text-1); user-select: none; }
  .db-deepdive > summary::-webkit-details-marker { display: none; }
  .db-deepdive > summary::before { content: "▸"; color: var(--text-3); font-size: 10px; transition: transform .2s ease; display: inline-block; }
  .db-deepdive[open] > summary::before { transform: rotate(90deg); }
  .db-deepdive-range { font-weight: 500; font-size: 11px; color: var(--text-2); font-variant-numeric: tabular-nums; padding: 2px 8px; border: 1px solid var(--line-soft); border-radius: 999px; background: var(--bg-elev-1); white-space: nowrap; cursor: pointer; transition: border-color .15s ease, color .15s ease, background-color .15s ease; }
  .db-deepdive-range:hover { color: var(--text-1); border-color: var(--line); background: var(--bg-pane); }
  .db-deepdive-range:empty { display: none; }
  .db-deepdive-hint { color: var(--text-3); font-weight: 400; font-size: 11px; margin-left: auto; }
  .db-deepdive-body { padding: 16px; border-top: 1px solid var(--line-soft); display: flex; flex-direction: column; gap: var(--s3); }
  .db-deepdive-body .db-kpis { margin: 0; }

  .db-grid-2 { display: grid; grid-template-columns: 1fr 1fr; gap: var(--s3); }
  @media (max-width: 980px) { .db-grid-2 { grid-template-columns: 1fr; } }
  .db-card-head { display: flex; align-items: center; justify-content: space-between; gap: var(--s3); margin-bottom: var(--s4); }
  .db-card-head h3 { font-size: 13px; font-weight: 600; color: var(--text-1); margin: 0; }
  /* Tacet mini-seg (the trend's Revenue/Orders/Visits switch) — a quiet pill
     rail; the active segment inverts to ink-on-paper. */
  .db-metric-btns { display: inline-flex; border: 1px solid var(--td-border); border-radius: 999px; overflow: hidden; }
  .db-metric-btns button {
    background: transparent; border: 0; color: var(--td-text2);
    padding: 2px 12px; font-size: 12px; cursor: pointer; font-family: inherit;
    transition: color .2s cubic-bezier(.16,1,.3,1);
  }
  .db-metric-btns button:hover { color: var(--td-text); }
  .db-metric-btns button.active { background: var(--td-text); color: var(--td-paper); }

  /* SVG trend chart — ink line over a faint ink wash, dashed guides, a solid
     baseline, and an end dot (the Tacet mockup). */
  .db-chart { width: 100%; height: auto; display: block; }
  .db-chart .grid-line { stroke: var(--td-border); stroke-width: 1; }
  .db-chart .grid-line.dash { stroke-dasharray: 2 6; }
  .db-chart .area { fill: rgba(var(--td-heat-rgb), .04); }
  .db-chart .line { fill: none; stroke: var(--td-text); stroke-width: 2; stroke-linejoin: round; }
  .db-chart .dot,
  .db-chart .enddot { fill: var(--td-text); }
  .db-chart .ax   { fill: var(--td-text2); font-size: 11px; }
  .db-chart .hovercol { fill: transparent; }
  .db-chart .hovercol:hover,
  .db-chart .hovercol.on { fill: rgba(var(--td-heat-rgb), .05); }
  /* Cursor-following tooltip for the trend chart. Replaces the unreliable
     native SVG <title> (which only shows if the pointer holds dead-still
     and resets on every column while sweeping). */
  #db-trend { position: relative; }
  .db-trend-tip {
    /* position:fixed + body-level (see dashTrendTip) so no overflow:hidden
       ancestor can crop it; positioned in viewport coords. */
    position: fixed; display: none; pointer-events: none; z-index: 9999;
    background: #12161A; color: #E8E5DC; border-radius: 10px; padding: 9px 12px;
    font-size: 12px; line-height: 1.5; white-space: nowrap; min-width: 168px;
    border: 1px solid rgba(232,229,220,.12);
    box-shadow: 0 10px 30px rgba(0,0,0,.28); font-variant-numeric: tabular-nums;
  }
  .db-trend-tip .b { font-weight: 700; margin-bottom: 5px; color: var(--accent); }
  .db-trend-tip .r { display: flex; justify-content: space-between; gap: 18px; }
  .db-trend-tip .r span { color: rgba(255,255,255,.6); }
  .db-trend-tip .r b { font-weight: 700; }

  /* Horizontal bar list (splits, funnel, geo, products) */
  /* Bar lists (Tacet) — label / value on one baseline, a thin 5px ink bar
     spanning the full row beneath. Muted fills (delivery modes) drop to 40%. */
  .db-bars { display: grid; gap: 10px; }
  .db-bars-gap { height: 8px; }
  .db-bar-row { display: grid; grid-template-columns: 1fr auto; gap: 4px 12px; align-items: baseline; font-size: 13.5px; }
  .db-bar-row .k { color: var(--td-text); min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; display: flex; align-items: baseline; gap: 8px; }
  .db-bar-row .v { color: var(--td-text2); font-variant-numeric: tabular-nums; font-size: 13px; text-align: right; white-space: nowrap; }
  .db-bar-row .sub { color: var(--td-text2); font-weight: 400; font-size: 12px; }
  .db-track { grid-column: 1 / -1; height: 5px; background: var(--td-raised); border-radius: 999px; overflow: hidden; }
  .db-track > span { display: block; height: 100%; background: var(--td-text); opacity: .75; border-radius: 999px; }
  .db-track > span.muted { opacity: .35; }

  /* Status pill rows (Order status) — tinted status pill · count · revenue. */
  .db-status-rows { display: grid; gap: 8px; }
  .db-status-row { display: flex; align-items: baseline; gap: 10px; font-size: 13.5px; color: var(--td-text); }
  .db-status-row .n { font-variant-numeric: tabular-nums; }
  .db-status-row .rv { margin-left: auto; color: var(--td-text2); font-size: 13px; font-variant-numeric: tabular-nums; }
  .db-status-row .sp { font-size: 12px; font-weight: 500; padding: 1px 10px; border-radius: 999px; flex: none; }
  .sp-sage  { background: var(--td-sage-tint);  color: var(--td-sage-ink); }
  .sp-amber { background: var(--td-amber-tint); color: var(--td-amber-ink); }
  .sp-brick { background: var(--td-brick-tint); color: var(--td-brick-ink); }
  .sp-mute  { background: var(--td-raised);     color: var(--td-text2); }

  /* Revenue composition — one two-segment ink bar + a legend line. */
  .db-comp-bar { display: flex; height: 10px; border-radius: 999px; overflow: hidden; background: var(--td-raised); margin: 14px 0 10px; }
  .db-comp-bar .c1 { background: var(--td-text); opacity: .8; }
  .db-comp-bar .c2 { background: var(--td-text); opacity: .3; }
  .db-comp-legend { display: flex; gap: 24px; flex-wrap: wrap; font-size: 13px; color: var(--td-text2); }
  .db-comp-legend b { color: var(--td-text); font-weight: 500; font-variant-numeric: tabular-nums; }
  .db-comp-legend em { font-style: normal; }
  .db-comp-legend .net { margin-left: auto; }
  /* Wallet line under the composition legend — prepaid top-ups received (cash
     in, counted in revenue) vs store credit spent on orders (a memo). */
  .db-comp-wallet { display: flex; gap: 24px; flex-wrap: wrap; margin-top: 10px;
    padding-top: 10px; border-top: 1px solid var(--td-line, var(--td-raised));
    font-size: 13px; color: var(--td-text2); }
  .db-comp-wallet b { color: var(--td-text); font-weight: 500; font-variant-numeric: tabular-nums; }
  .db-comp-wallet em { font-style: normal; }

  /* Ranked lists (top customers / top products) — index · name · value. */
  .db-rank { display: grid; gap: 10px; }
  .db-rank-row { display: flex; align-items: baseline; gap: 10px; font-size: 13.5px; color: var(--td-text); }
  .db-rank-row .idx { color: var(--td-text2); font-size: 12px; width: 16px; flex: none; font-variant-numeric: tabular-nums; }
  .db-rank-row .name { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .db-rank-row .rv { margin-left: auto; color: var(--td-text2); font-size: 13px; font-variant-numeric: tabular-nums; flex: none; }

  /* Small in-card group heading (Vouchers / Entry scenes). */
  .db-group-h { font-size: 12px; font-weight: 500; color: var(--td-text2); margin: 16px 0 10px; }
  .db-funnel-foot { margin-top: 14px; font-size: 12.5px; color: var(--td-text2); }

  /* Retention decay curve — a thin ink line over one baseline. */
  .db-ret-curve { display: block; width: 100%; height: 72px; margin-top: 14px; }
  .db-ret-curve .base { stroke: var(--td-border); stroke-width: 1; }
  .db-ret-curve .line { fill: none; stroke: var(--td-text); stroke-width: 1.8; vector-effect: non-scaling-stroke; }

  /* Product / customer rows */
  .db-list { display: flex; flex-direction: column; }
  .db-row {
    display: flex; align-items: center; gap: var(--s3);
    padding: var(--s2) 0; border-bottom: 1px solid var(--line-soft); font-size: 12px;
  }
  .db-row:last-child { border-bottom: 0; }
  .db-row .rank { width: 18px; color: var(--text-4); font-variant-numeric: tabular-nums; text-align: right; }
  .db-row .thumb { width: 34px; height: 34px; border-radius: var(--r-sm); object-fit: cover; background: var(--bg-elev-2); flex: 0 0 auto; }
  .db-row .nm { flex: 1; min-width: 0; }
  .db-row .nm b { display: block; color: var(--text-1); font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
  .db-row .nm span { color: var(--text-3); font-size: 11px; }
  .db-row .mny { text-align: right; font-variant-numeric: tabular-nums; }
  .db-row .mny b { color: var(--text-1); }
  .db-row .mny span { display: block; color: var(--accent); font-size: 9px; text-transform: uppercase; letter-spacing: .12em; font-weight: 500; }

  /* Stat tiles (Customer mix / Retention / Marketing) — bare left-aligned
     figures in a row, no boxes (the Tacet mockup's cohort tiles). */
  .db-tiles { display: flex; gap: 32px; flex-wrap: wrap; margin-bottom: 14px; }
  .db-tile { min-width: 0; }
  .db-tile .t-val { font-size: 21px; font-weight: 500; color: var(--td-text); font-variant-numeric: tabular-nums; }
  .db-tile .t-lbl { font-size: 12px; color: var(--td-text2); }
  .db-muted { color: var(--td-text2); font-size: 13px; padding: var(--s3) 0; }
  .db-pill { display: inline-block; padding: 1px 7px; border-radius: 99px; font-size: 10px; font-weight: 600; background: var(--bg-elev-2); color: var(--text-2); }

  /* Demand heatmap — hour × day-of-week grid */
  /* Demand heatmap — compact Tacet grid: single-letter day rail + 7×24
     ink-tinted cells, "busiest" foot + fewer→more legend. */
  .db-heat { display: grid; grid-template-columns: 18px 1fr; gap: 6px; align-items: start; }
  .db-heat .days { display: grid; gap: 3px; font-size: 10.5px; color: var(--td-text2); line-height: 1; }
  .db-heat .days span { height: 14px; display: flex; align-items: center; }
  .db-heat .cells { display: grid; grid-template-columns: repeat(24, 1fr); gap: 3px; }
  .db-heat .cells i { height: 14px; border-radius: 3px; background: var(--td-raised); display: block; }
  .db-heat-foot { display: flex; align-items: center; gap: 12px; margin-top: 10px; font-size: 12px; color: var(--td-text2); }
  .db-heat-foot .legend { margin-left: auto; display: flex; align-items: center; gap: 4px; }
  .db-heat-foot .legend i { width: 12px; height: 12px; border-radius: 3px; display: inline-block; }

  /* ── Dashboard View tweaks (per-operator) — Trend bars, heatmap shape,
     headline number style. Defaults (curve / cells / light) = base look. ── */
  .db-chart .bar { fill: var(--accent); }
  #tab-dashboard.dv-num-display .db-kpi .val { font-weight: 600; font-size: 33px; letter-spacing: -.02em; }
  #tab-dashboard.dv-num-mono .db-kpi .val { font-family: var(--f-mono); font-weight: 500; font-size: 22px; letter-spacing: 0; }
  #tab-dashboard.dv-heat-dots .db-heat-cell { border-radius: 50%; }
  #tab-dashboard.dv-heat-dashes .db-heat-cell { border-radius: 1px; transform: scaleY(.5); }

  /* Cohort retention — curve + matrix */
  .db-cohort-curve { width: 100%; height: auto; display: block; margin-bottom: var(--s3); }
  .db-cohort-curve .grid-line { stroke: var(--line-soft); stroke-width: 1; }
  .db-cohort-curve .ax { fill: var(--text-3); font-size: 10px; }
  .db-cohort-curve .line { fill: none; stroke: var(--accent); stroke-width: 2; }
  .db-cohort-curve .area { fill: var(--accent); opacity: .12; }
  .db-cohort-curve .dot { fill: var(--accent); }
  .db-cohort-wrap { overflow-x: auto; }
  .db-cohort-tbl { border-collapse: separate; border-spacing: 3px; font-size: 11px; }
  .db-cohort-tbl th { color: var(--text-3); font-size: 10px; font-weight: 600; padding: 4px 6px; text-align: center; white-space: nowrap; }
  .db-cohort-tbl th.l { text-align: left; }
  .db-cohort-tbl td { text-align: center; padding: 6px 4px; border-radius: 4px; font-variant-numeric: tabular-nums; min-width: 38px; color: #fff; }
  .db-cohort-tbl td.lbl { background: var(--bg-elev-1); color: var(--text-1); font-weight: 600; text-align: left; white-space: nowrap; }
  .db-cohort-tbl td.empty { background: var(--bg-elev-1); color: var(--text-4); }
  .db-cohort-note { font-size: 11px; color: var(--text-3); line-height: 1.55; margin-top: var(--s3); }

  /* ═══ Deep-dive — display-options bar + reveal liveliness ════════════════
     The legacy KPI strip + 12 charts used to sit flat on the page. This pass
     lifts them to the bento's quality: resting depth + a hover lift on every
     card, plus motion that fires when the operator opens the fold — cards fade
     up in sequence, magnitude bars sweep out, trend/cohort lines draw on, and
     KPI numbers count up. All reveal motion is gated behind .dd-open-anim
     (toggled by the <details> `toggle` handler in dashInit) so it plays on
     reveal, not invisibly while the fold is still closed. Honors
     reduced-motion. */
  /* position+z-index lifts the whole toolbar's stacking context (and the
     popover it contains) above the KPI/chart cards that follow it in the DOM
     — otherwise those later siblings paint over the open popover. */
  .db-dd-bar { position: relative; z-index: 30; display: flex; align-items: center; justify-content: flex-end; gap: var(--s3); margin-top: -2px; }

  /* Lift the cards off the page so they read as objects, not flat panels —
     same depth + hover float language as the bento tiles above the fold. */
  .db-deepdive-body .card {
    box-shadow: 0 1px 2px rgba(18,22,26,.05), 0 6px 20px rgba(18,22,26,.07);
    border-color: var(--line);
    transition: box-shadow .18s ease, transform .18s ease;
  }
  .db-deepdive-body .card:hover {
    box-shadow: 0 2px 4px rgba(18,22,26,.06), 0 12px 28px rgba(18,22,26,.11);
    transform: translateY(-1px);
  }
  .db-deepdive-body .db-kpi { transition: box-shadow .18s ease, transform .18s ease; }
  .db-deepdive-body .db-kpi:hover {
    transform: translateY(-1px);
    box-shadow: 0 2px 4px rgba(18,22,26,.06), 0 12px 28px rgba(18,22,26,.11);
  }
  /* Product / customer rows read as interactive. */
  .db-deepdive-body .db-list .db-row { border-radius: 6px; transition: background .14s ease; }
  .db-deepdive-body .db-list .db-row:hover { background: var(--bg-elev-1); }

  /* Ambient tab-switch fade for the trend chart (Revenue/Orders/Visits) —
     the new series drifts up + fades in on the Mini App Editor's soft
     deceleration instead of hard-cutting. Re-triggered via .is-swap. */
  @keyframes db-swap-in { from { opacity: 0; transform: translateY(5px); } to { opacity: 1; transform: none; } }
  #db-trend.is-swap .db-chart { animation: db-swap-in .55s cubic-bezier(.16, 1, .3, 1) both; }
  @media (prefers-reduced-motion: reduce) { #db-trend.is-swap .db-chart { animation: none; } }

  @keyframes db-card-in { from { opacity: 0; transform: translateY(9px); } to { opacity: 1; transform: none; } }
  @keyframes db-bar-grow { from { transform: scaleX(0); } to { transform: scaleX(1); } }
  @keyframes db-line-draw { from { stroke-dashoffset: 1; } to { stroke-dashoffset: 0; } }

  .db-deepdive.dd-open-anim .db-deepdive-body > * { animation: db-card-in .5s cubic-bezier(.22,.61,.36,1) both; }
  .db-deepdive.dd-open-anim .db-deepdive-body > *:nth-child(1) { animation-delay: .02s; }
  .db-deepdive.dd-open-anim .db-deepdive-body > *:nth-child(2) { animation-delay: .07s; }
  .db-deepdive.dd-open-anim .db-deepdive-body > *:nth-child(3) { animation-delay: .12s; }
  .db-deepdive.dd-open-anim .db-deepdive-body > *:nth-child(4) { animation-delay: .17s; }
  .db-deepdive.dd-open-anim .db-deepdive-body > *:nth-child(5) { animation-delay: .22s; }
  .db-deepdive.dd-open-anim .db-deepdive-body > *:nth-child(n+6) { animation-delay: .27s; }
  /* Magnitude bars sweep out from the left as their card lands. */
  .db-deepdive.dd-open-anim .db-track > span { transform-origin: left center; animation: db-bar-grow .65s cubic-bezier(.22,.61,.36,1) both; }
  /* Trend + cohort lines draw on (pathLength="1" normalises any series length). */
  .db-deepdive.dd-open-anim .db-chart .line,
  .db-deepdive.dd-open-anim .db-cohort-curve .line { stroke-dasharray: 1; animation: db-line-draw .9s ease both; }

  @media (prefers-reduced-motion: reduce) {
    .db-deepdive.dd-open-anim .db-deepdive-body > *,
    .db-deepdive.dd-open-anim .db-track > span,
    .db-deepdive.dd-open-anim .db-chart .line,
    .db-deepdive.dd-open-anim .db-cohort-curve .line { animation: none; }
  }

  /* Closed Deep dive — a calm "come look inside" invitation. While the fold is
     closed the bar breathes a brass glow in and out, its border warms to brass
     at the peak, and the ▸ chevron gives one gentle nudge + brass flush near
     the end of each cycle (periodic motion reads less needy than a constant
     pulse). The :not([open]) selector self-cancels the moment it's opened and
     resumes when closed again. Honours prefers-reduced-motion. */
  @keyframes db-dd-breathe {
    0%, 100% { box-shadow: 0 0 0 0 rgba(201, 149, 74, 0);   border-color: var(--line-soft); }
    50%      { box-shadow: 0 4px 22px -4px rgba(201, 149, 74, .55); border-color: rgba(201, 149, 74, .55); }
  }
  @keyframes db-dd-nudge {
    0%, 78%, 100% { transform: translateX(0); color: var(--text-3); }
    86%           { transform: translateX(4px); color: var(--brass); }
    93%           { transform: translateX(0); color: var(--text-3); }
  }
  .db-deepdive:not([open]) {
    animation: db-dd-breathe 4.5s ease-in-out infinite;
  }
  .db-deepdive:not([open]) > summary::before {
    animation: db-dd-nudge 4.5s ease-in-out infinite;
  }
  @media (prefers-reduced-motion: reduce) {
    .db-deepdive:not([open]),
    .db-deepdive:not([open]) > summary::before { animation: none; }
  }

  /* Symmetric open/close — a native <details> snaps open and shut, so closing
     felt instant while opening looked slow (the content reveal). dashInit's
     summary-click handler drives the fold's body height both ways; this is the
     transition that envelope rides. Scoped to .dd-anim so it's live only mid-
     animation and never fights normal layout or the per-card reveal. */
  .db-deepdive.dd-anim .db-deepdive-body {
    overflow: hidden;
    transition: height .4s cubic-bezier(.4, 0, .2, 1);
  }
  /* Closing has no content-reveal or zen-scroll riding behind it, so a .4s
     collapse felt abrupt next to the open. Let it ease shut more slowly. */
  .db-deepdive.dd-anim.dd-closing .db-deepdive-body {
    transition-duration: .62s;
    transition-timing-function: cubic-bezier(.33, 0, .25, 1);
  }

  /* ── Bars come alive: every magnitude bar inside a mobile report card sweeps
     out from the left when its card mounts or unfolds — on dashboard refresh
     (default-open sections), on each accordion unfold, and in the report
     push/sheet screens. The .rm-barsweep trigger is (re)applied by JS so the
     sweep replays on every open. Covers both bar markups: the .db-track fill
     spans (Order status, Payment & delivery) and the --mag ::before bars (Top
     products / customers / tiers). Rows cascade via a small per-row stagger. */
  .rm-barsweep .db-track > span,
  .rm-barsweep .has-bar::before {
    transform-origin: left center;
    animation: db-bar-grow .7s cubic-bezier(.22,.61,.36,1) both;
  }
  .rm-barsweep .db-bar-row:nth-child(1) .db-track > span, .rm-barsweep .has-bar:nth-child(1)::before { animation-delay: .03s; }
  .rm-barsweep .db-bar-row:nth-child(2) .db-track > span, .rm-barsweep .has-bar:nth-child(2)::before { animation-delay: .09s; }
  .rm-barsweep .db-bar-row:nth-child(3) .db-track > span, .rm-barsweep .has-bar:nth-child(3)::before { animation-delay: .15s; }
  .rm-barsweep .db-bar-row:nth-child(4) .db-track > span, .rm-barsweep .has-bar:nth-child(4)::before { animation-delay: .21s; }
  .rm-barsweep .db-bar-row:nth-child(5) .db-track > span, .rm-barsweep .has-bar:nth-child(5)::before { animation-delay: .27s; }
  .rm-barsweep .db-bar-row:nth-child(6) .db-track > span, .rm-barsweep .has-bar:nth-child(6)::before { animation-delay: .33s; }
  .rm-barsweep .db-bar-row:nth-child(7) .db-track > span, .rm-barsweep .has-bar:nth-child(7)::before { animation-delay: .39s; }
  .rm-barsweep .db-bar-row:nth-child(n+8) .db-track > span, .rm-barsweep .has-bar:nth-child(n+8)::before { animation-delay: .45s; }
  @media (prefers-reduced-motion: reduce) {
    .rm-barsweep .db-track > span, .rm-barsweep .has-bar::before { animation: none; }
  }

  /* ── #1 takes a bow: when a Top-products / Best-sellers list lands (after the
     fold opens, or when a report screen mounts), the leading row briefly pops
     and lifts, then settles — drawing the eye to the top seller. Keyed on the
     same .rm-barsweep trigger that JS replays on every open, and delayed so it
     fires once the fold has finished, not mid-slide. ──────────────────────── */
  .rm-barsweep .db-list > .db-row:first-child {
    position: relative; z-index: 2; transform-origin: center;
    animation: dbRowPop 1.05s cubic-bezier(.34,1.4,.5,1) both;
    animation-delay: .5s;
  }
  @keyframes dbRowPop {
    0%   { transform: scale(1); box-shadow: 0 0 0 rgba(18,22,26,0); }
    35%  { transform: scale(1.05); box-shadow: 0 10px 26px rgba(18,22,26,.16); }
    100% { transform: scale(1); box-shadow: 0 0 0 rgba(18,22,26,0); }
  }
  @media (prefers-reduced-motion: reduce) {
    .rm-barsweep .db-list > .db-row:first-child { animation: none; }
  }

  /* ── EKG reveal: on the mobile dashboard the sparklines + revenue trend wipe
     in left→right like a heart monitor — the whole curve (line + area wash) is
     uncovered by an animating clip, so the line is ALWAYS fully drawn and can
     never look broken (an earlier stroke-dash approach left dotted gaps once the
     non-scaling stroke + stretched viewBox defeated pathLength). Fires on every
     (re)render since rmRenderDash rebuilds the SVGs — on refresh + range switch.
     The end-dot waits for the wipe to arrive, then fades/scales in. ───────── */
  #rm-dash .db-spark { animation: dbSparkWipe 1.15s cubic-bezier(.4,.1,.2,1) both; }
  #rm-dash .rm-dh-trend .db-spark { animation-duration: 1.5s; }   /* the big chart reveals a touch slower */
  #rm-dash .rm-dh-trend .db-spark-dot { animation: dbSparkDotIn .45s ease backwards; animation-delay: 1.35s; }
  @keyframes dbSparkWipe { from { clip-path: inset(0 100% 0 0); } to { clip-path: inset(0 0 0 0); } }
  @keyframes dbSparkDotIn { from { opacity: 0; transform: scale(.2); } to { opacity: 1; transform: scale(1); } }
  @media (prefers-reduced-motion: reduce) {
    #rm-dash .db-spark, #rm-dash .rm-dh-trend .db-spark-dot { animation: none; }
  }

  /* ═══════════════════════════════════════════════════════════════════
     PRODUCTS TAB — mini-app product surface editor
     ═══════════════════════════════════════════════════════════════════ */

  /* ═══════════════════════════════════════════════════════════════════
     PRODUCTS — the catalogue surface, built natively on Tacet.
     Spec: reference/products-page-mockup.html ("Products page — Tacet",
     locked). Rewritten 2026-07-10 from three stacked layers (a legacy
     --bg-pane/--line base, plus a #tab-products override block that
     re-stated every value) into this single layer written straight on the
     --td-* tokens. Same markup, same ids, same behaviour — one source of
     truth for the look, so a change lands in one place.

     Colour discipline, Tacet:
       · Ink is the only "chosen/active" colour on ordinary controls.
       · Dusk means "Tacet did this for you" — it is spent ONLY on the
         Guided-add wizard and on input focus (the Tacet input accent).
         The locked mockup painted the row checkboxes and the "N sizes"
         tag Dusk too; those are ordinary, so they are Ink/graphite here.
       · Sage = healthy, amber = running low, brick = sold out. Nothing
         else carries colour. A category is a filter, not a traffic light.
     ═══════════════════════════════════════════════════════════════════ */

  /* Paper ground bled over <main>'s padding so there's no grey frame ("crop"). */
  #tab-products {
    background: var(--td-paper);
    margin: calc(-1 * (var(--s6) + 56px)) calc(-1 * var(--s6)) calc(-1 * var(--s7));
    padding: calc(var(--s6) + 56px) var(--s6) var(--s7);
    min-height: 100vh;
  }
  /* Page head — eyebrow · title · lede. The h2 inherits its size/weight from the
     shared Tacet heading rule; only the rhythm is set here. */
  #tab-products > .t-ord-eyebrow { margin: 0 0 4px; }
  #tab-products > .pa-pagetitle { margin: 0; }
  /* Title row + ⓘ. The old standing lede is gone; its "what is this" copy now
     rides in the tooltip below, one-time teaching text kept out of the way. */
  #tab-products > .pa-head-row {
    display: flex; align-items: center; gap: 8px; margin: 0 0 22px;
  }
  #tab-products > .pa-head-row > .pa-pagetitle { margin: 0; }
  .pa-info-wrap { position: relative; display: inline-flex; align-items: center; }
  .pa-info {
    appearance: none; background: none; border: none; margin: 0; padding: 4px;
    display: inline-flex; align-items: center; justify-content: center;
    line-height: 0; border-radius: 50%; cursor: pointer;
    color: var(--td-text2, #6b6b60); transition: color .15s ease, background .15s ease;
  }
  .pa-info svg { width: 19px; height: 19px; display: block; }
  .pa-info:hover, .pa-info:focus-visible {
    color: var(--td-text, #1a1a17); background: var(--td-surface, #f0ede4); outline: none;
  }
  .pa-info-pop {
    position: absolute; top: calc(100% + 8px); left: 0; z-index: 60;
    width: max-content; max-width: min(340px, calc(100vw - 48px));
    background: var(--td-paper, #fbfaf6); color: var(--td-text2, #55554c);
    border: 1px solid var(--td-border, #e4e0d5); border-radius: 12px;
    box-shadow: 0 8px 28px rgba(20, 20, 15, .14);
    padding: 12px 14px; font-size: 13px; line-height: 1.55;
    opacity: 0; visibility: hidden; transform: translateY(-4px); pointer-events: none;
    transition: opacity .16s ease, transform .16s ease, visibility 0s linear .16s;
  }
  .pa-info-wrap:hover .pa-info-pop,
  .pa-info-wrap:focus-within .pa-info-pop,
  .pa-info-wrap.open .pa-info-pop {
    opacity: 1; visibility: visible; transform: none; pointer-events: auto;
    transition: opacity .16s ease, transform .16s ease;
  }
  @media (prefers-reduced-motion: reduce) {
    .pa-info-pop { transition: opacity .16s ease; transform: none; }
  }

  /* ── Inline field help — a compact ⓘ that sits beside a field label and
        reveals its "what is this" copy on hover / focus. Replaces the standing
        grey guide lines under inputs, keeping teaching text out of the way
        until it's wanted. Reuses the topbar ⓘ look at a smaller size. ── */
  .fhelp { position: relative; display: inline-flex; vertical-align: middle; }
  .fhelp-btn {
    appearance: none; background: none; border: none; margin: 0 0 0 5px; padding: 2px;
    display: inline-flex; align-items: center; justify-content: center;
    line-height: 0; border-radius: 50%; cursor: help;
    color: var(--td-text2, #8a8378); transition: color .15s ease, background .15s ease;
  }
  .fhelp-btn svg { width: 14px; height: 14px; display: block; }
  .fhelp-btn:hover, .fhelp-btn:focus-visible {
    color: var(--td-text, #1a1a17); background: var(--td-surface, #f0ede4); outline: none;
  }
  .fhelp-pop {
    position: absolute; top: calc(100% + 6px); left: 50%; transform: translateX(-50%) translateY(-4px);
    z-index: 60; width: max-content; max-width: min(280px, calc(100vw - 48px));
    background: var(--td-paper, #fbfaf6); color: var(--td-text2, #55554c);
    border: 1px solid var(--td-border, #e4e0d5); border-radius: 10px;
    box-shadow: 0 8px 28px rgba(20, 20, 15, .14);
    padding: 9px 11px; font-size: 12px; font-weight: 400; line-height: 1.5; text-align: left;
    white-space: normal;
    opacity: 0; visibility: hidden; pointer-events: none;
    transition: opacity .16s ease, transform .16s ease, visibility 0s linear .16s;
  }
  .fhelp:hover .fhelp-pop,
  .fhelp:focus-within .fhelp-pop {
    opacity: 1; visibility: visible; transform: translateX(-50%) translateY(0); pointer-events: auto;
    transition: opacity .16s ease, transform .16s ease;
  }
  @media (prefers-reduced-motion: reduce) {
    .fhelp-pop { transition: opacity .16s ease; transform: translateX(-50%); }
    .fhelp:hover .fhelp-pop, .fhelp:focus-within .fhelp-pop { transform: translateX(-50%); }
  }

  /* ── Recap band — one calm hero count, then one-click jumps to the three
        buckets that actually need someone. The numbers ARE the filters. ── */
  .pa-recap {
    display: flex; align-items: center; gap: 26px; flex-wrap: wrap;
    background: var(--td-surface); border: 1px solid var(--td-border);
    border-radius: 14px; padding: 22px 26px; margin-bottom: var(--s3);
  }
  .pa-recap-hero { flex-shrink: 0; }
  .pa-recap-cap {
    font-family: var(--f-sans); font-size: 11px; font-weight: 600;
    letter-spacing: .08em; text-transform: uppercase; color: var(--td-sage-ink);
  }
  .pa-recap-n {
    font-size: 44px; font-weight: 600; letter-spacing: -.02em; line-height: 1.05;
    color: var(--td-text); font-variant-numeric: tabular-nums; margin-top: 6px;
  }
  .pa-recap-u { font-size: 13px; font-weight: 500; color: var(--td-text2); letter-spacing: 0; }
  .pa-recap-u b { font-weight: 600; color: var(--td-text); font-variant-numeric: tabular-nums; }
  /* Proportion bar — the sage fill is the live share of the whole catalogue;
     the cream track behind it is everything disabled. A shape, not just a
     number, so "almost all live" or "half hidden" reads at a glance. */
  .pa-recap-bar { width: 220px; max-width: 100%; height: 8px; border-radius: 999px; background: var(--td-raised); overflow: hidden; margin-top: 12px; }
  .pa-recap-bar-fill { display: block; height: 100%; background: var(--td-sage); border-radius: 999px; transition: width .35s cubic-bezier(.16,1,.3,1); }
  .pa-recap-legend { display: flex; gap: 16px; margin-top: 8px; font-size: 12px; color: var(--td-text2); }
  .pa-recap-sw { width: 8px; height: 8px; border-radius: 2px; display: inline-block; margin-right: 5px; vertical-align: 0; }
  .pa-recap-sw.sw-active { background: var(--td-sage); }
  .pa-recap-sw.sw-dis { background: var(--td-raised); border: 1px solid var(--td-border); }
  .pa-recap-legend b { color: var(--td-text); font-weight: 600; font-variant-numeric: tabular-nums; margin-left: 3px; }
  .pa-recap-rule { width: 1px; align-self: stretch; background: var(--td-border); margin: 4px 0; }
  .pa-recap-jump { display: flex; align-items: center; gap: 4px; flex-wrap: wrap; }
  .pa-recap-jlbl {
    font-family: var(--f-sans); font-size: 11px; font-weight: 600;
    letter-spacing: .08em; text-transform: uppercase; color: var(--td-text2); margin-right: 8px;
  }
  .pa-jump {
    display: inline-flex; align-items: center; gap: 9px; padding: 8px 14px;
    border: 1px solid transparent; border-radius: 8px; background: none;
    cursor: pointer; color: var(--td-text); font-family: var(--f-sans); user-select: none;
    transition: background .18s cubic-bezier(.16,1,.3,1), border-color .18s cubic-bezier(.16,1,.3,1);
  }
  .pa-jump:hover { background: var(--td-raised); }
  .pa-jump:focus-visible { outline: 2px solid var(--td-dusk); outline-offset: 2px; }
  .pa-jump.pa-kpi-on { background: var(--td-raised); border-color: var(--td-border); }
  .pa-jump-dot { width: 8px; height: 8px; border-radius: 50%; flex: none; background: var(--td-sage); }
  .pa-jump-dot.is-out { background: var(--td-brick); }
  .pa-jump-dot.is-low { background: var(--td-amber); }
  .pa-jump-dot.is-draft { background: var(--td-text2); }
  .pa-jump-dot.is-dead { background: var(--td-brick); }
  .pa-jump-dot.is-promo { background: var(--td-sage); }
  .pa-jump-n { font-size: 23px; font-weight: 600; letter-spacing: -.01em; line-height: 1; font-variant-numeric: tabular-nums; }
  .pa-jump-l { font-size: 13.5px; color: var(--td-text2); font-weight: 500; white-space: nowrap; }
  .pa-jump.pa-kpi-on .pa-jump-l { color: var(--td-text); }

  /* Low-stock threshold knob — lives in the sub-bar, out of the scan path. */
  .pa-lowknob {
    margin-left: auto; display: inline-flex; align-items: center; gap: 7px;
    font-size: 12.5px; color: var(--td-text2); white-space: nowrap;
  }
  .pa-lowknob-lbl { font-weight: 500; color: var(--td-text2); }
  .pa-lowknob input {
    width: 52px; padding: 5px 6px; border: 1px solid var(--td-border); border-radius: 6px;
    font: 500 13px var(--f-sans); text-align: center; font-variant-numeric: tabular-nums;
    background: var(--td-surface); color: var(--td-text);
  }
  .pa-lowknob input:focus { outline: none; border-color: var(--td-dusk); }
  #pa-low-threshold-status { color: var(--td-sage-ink); font-size: 11px; }
  .pa-low-store-sel {
    font: 500 12px var(--f-sans); color: var(--td-text); background: var(--td-surface);
    border: 1px solid var(--td-border); border-radius: 6px; padding: 4px 22px 4px 8px; cursor: pointer;
    appearance: none;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='10' viewBox='0 0 24 24' fill='none' stroke='%2358606E' stroke-width='2'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");
    background-repeat: no-repeat; background-position: right 7px center;
  }
  .pa-low-store-sel:focus { outline: none; border-color: var(--td-dusk); }
  .pa-low-badge {
    display: inline-flex; align-items: center; height: 18px; padding: 0 8px; border-radius: 999px;
    font: 500 10.5px var(--f-sans); white-space: nowrap;
  }
  .pa-low-badge.is-default { background: transparent; color: var(--td-text2); border: 1px solid var(--td-border); }
  .pa-low-badge.is-custom { background: var(--td-raised); color: var(--td-text); }
  .pa-low-reset {
    background: none; border: 0; cursor: pointer; padding: 0; font: 500 11.5px var(--f-sans);
    color: var(--td-text2); text-decoration: underline; text-decoration-color: var(--td-border);
    text-underline-offset: 2px;
  }
  .pa-low-reset:hover { color: var(--td-text); }

  @media (max-width: 720px) {
    .pa-recap { gap: 14px; padding: 14px 16px; }
    .pa-recap-n { font-size: 30px; }
    .pa-recap-rule { display: none; }
    .pa-lowknob { margin-left: 0; width: 100%; }
  }

  /* ── Catalogue card — wraps the recap row. (Used to also wrap a separate
        highlights band; that band's two live filters — Going nowhere and On
        promotion — now live as plain pa-jump tiles in the row above.) ── */
  .pa-recap-card {
    background: var(--td-surface); border: 1px solid var(--td-border);
    border-radius: 14px; margin-bottom: var(--s3); overflow: hidden;
  }
  .pa-recap-card > .pa-recap {
    background: none; border: 0; border-radius: 0; margin-bottom: 0;
  }

  /* ── Toolbar — search · category · add · guided add · layout ───────────── */
  .pa-toolbar {
    display: flex; gap: 12px; align-items: center; flex-wrap: wrap;
    padding: 0; background: transparent; border: 0;
    margin: 22px 0 var(--s4);
  }
  .pa-toolbar input[type=text] {
    flex: 1; min-width: 220px;
    font: 400 15px var(--f-sans); color: var(--td-text); background: var(--td-surface);
    border: 1px solid var(--td-border); border-radius: 8px; padding: 11px 14px 11px 40px;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%2358606E' stroke-width='2'%3E%3Ccircle cx='11' cy='11' r='7'/%3E%3Cpath d='m20 20-3.5-3.5'/%3E%3C/svg%3E");
    background-repeat: no-repeat; background-position: left 14px center;
  }
  .pa-toolbar input[type=text]::placeholder { color: var(--td-text2); opacity: .7; }
  .pa-toolbar input[type=text]:focus { outline: none; border-color: var(--td-dusk); }
  #pa-filter, #pa-subfilter {
    font: 400 14px var(--f-sans); color: var(--td-text); background: var(--td-surface);
    border: 1px solid var(--td-border); border-radius: 8px; padding: 11px 34px 11px 14px; cursor: pointer;
    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='%2358606E' stroke-width='2'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");
    background-repeat: no-repeat; background-position: right 12px center;
  }
  #pa-filter:focus, #pa-subfilter:focus { outline: none; border-color: var(--td-dusk); }
  /* Secondary buttons on this page: quiet surface + hairline, ink text. */
  #tab-products .btn {
    font: 500 14px var(--f-sans); border-radius: 8px; border: 1px solid var(--td-border);
    background: var(--td-surface); color: var(--td-text); padding: 10px 16px; box-shadow: none;
  }
  #tab-products .btn:hover { background: var(--td-surface); border-color: var(--td-text2); }
  #tab-products .btn svg { width: 16px; height: 16px; }
  #tab-products .btn-sm { font-size: 13px; padding: 8px 14px; }
  /* The ONE Dusk element on the page: Guided add is the wizard doing the work. */
  #pa-wizard-btn.btn-primary {
    background: var(--td-dusk); border-color: var(--td-dusk); color: #fff;
    padding: 10px 16px; box-shadow: none;
  }
  #pa-wizard-btn.btn-primary:hover { background: var(--td-dusk-hi); border-color: var(--td-dusk-hi); }

  /* Layout switcher — list / grid. Compact + the display-options popover stay
     hidden (locked mockup shows two); unhide to bring them back. */
  .pa-view-seg {
    display: inline-flex; flex-shrink: 0;
    background: var(--td-surface); border: 1px solid var(--td-border);
    border-radius: 8px; padding: 3px; gap: 2px;
  }
  .pa-view-btn {
    display: inline-flex; align-items: center; justify-content: center;
    padding: 7px 9px; border-radius: 6px;
    background: none; color: var(--td-text2); border: none; cursor: pointer;
    transition: background .18s cubic-bezier(.16,1,.3,1), color .18s cubic-bezier(.16,1,.3,1);
  }
  .pa-view-btn:hover { background: var(--td-raised); color: var(--td-text); }
  .pa-view-btn.active { background: var(--td-raised); color: var(--td-text); }
  .pa-view-btn:disabled { opacity: .35; cursor: not-allowed; }        /* locked while the editor is docked */
  .pa-view-btn:disabled:hover { color: var(--td-text2); background: none; }
  .pa-view-btn svg { display: block; width: 16px; height: 16px; }
  #pa-view-seg .pa-view-btn[data-view="compact"] { display: none; }
  .pa-toolbar .ovp-wrap { display: none; }

  /* "N sizes" tag beside a variant product's name. Informational, so it is
     quiet cream — not Dusk (Dusk is reserved for automation). */
  .pa-sizes-pill {
    display: inline-flex; align-items: center; vertical-align: middle;
    margin-left: 8px; padding: 1px 9px; border-radius: 999px; font-size: 11px;
    font-weight: 500; background: var(--td-raised); color: var(--td-text2); white-space: nowrap;
  }

  /* ── Sub-bar — which store's stock we're reading, how many shown, the knob ── */
  .pa-subbar { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; margin-bottom: var(--s3); }
  .pa-subbar .pa-store-seg { margin: 0; }
  .pa-subbar .pa-count { font-size: 13px; color: var(--td-text2); font-variant-numeric: tabular-nums; margin-left: 0; }
  .pa-store-seg {
    display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
    margin: 0 0 var(--s3); font-family: var(--f-sans);
  }
  .pa-store-seg .pa-store-lbl {
    font-size: 12.5px; letter-spacing: 0; text-transform: none;
    color: var(--td-text2); font-weight: 500;
  }
  .pa-store-seg .pa-store-btns {
    display: inline-flex; gap: 2px; padding: 3px;
    border: 1px solid var(--td-border); border-radius: 999px; background: var(--td-raised);
  }
  .pa-store-seg .pa-store-btn {
    padding: 6px 15px; font-size: 13px; font-weight: 500; border: none; cursor: pointer;
    background: none; color: var(--td-text2); border-radius: 999px; font-family: var(--f-sans);
  }
  .pa-store-seg .pa-store-btn.active {
    background: var(--td-surface); color: var(--td-text);
    box-shadow: 0 1px 2px rgba(17,26,43,.06);
  }
  .pa-store-seg .pa-store-hint { font-size: 11px; color: var(--td-text2); }

  /* ── Grid view (cards) ─────────────────────────────────────────────────── */
  .pa-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(212px, 1fr));
    gap: 16px;
  }
  .pa-grid.pa-grid-compact { grid-template-columns: repeat(auto-fill, minmax(148px, 1fr)); }
  .pa-grid-compact .pa-card-body { padding: var(--s2) var(--s3) var(--s3); min-height: 0; }
  .pa-grid-compact .pa-card-name { font-size: 12px; -webkit-line-clamp: 1; }
  .pa-grid-compact .pa-card-sub  { font-size: 10px; }
  .pa-grid-compact .pa-pill { height: 18px; font-size: 8px; padding: 0 6px; }

  /* ── Compact row list (shown while the editor is docked beside it) ─────── */
  .pa-list { display: flex; flex-direction: column; gap: 6px; }
  .pa-row {
    display: flex; align-items: center; gap: var(--s3);
    padding: 8px 12px;
    background: var(--td-surface); border: 1px solid var(--td-border);
    border-radius: 8px; cursor: pointer;
    transition: border-color .18s cubic-bezier(.16,1,.3,1), transform .18s cubic-bezier(.16,1,.3,1);
  }
  .pa-row:hover { border-color: var(--td-text2); transform: translateY(-1px); }
  .pa-row-img {
    width: 44px; height: 44px; flex-shrink: 0;
    border-radius: 10px; overflow: hidden;
    background: var(--td-raised); border: 1px solid var(--td-border);
    display: flex; align-items: center; justify-content: center; color: var(--td-text2);
  }
  .pa-row-img img { width: 100%; height: 100%; object-fit: cover; display: block; }
  .pa-row-img svg { width: 20px; height: 20px; opacity: .5; }
  .pa-row-main { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
  .pa-row-name { font-size: 15px; font-weight: 500; color: var(--td-text); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
  .pa-row-sub  { font-size: 12.5px; color: var(--td-text2); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
  .pa-row-sub .pa-row-meta { font-family: var(--f-sans); font-size: 12.5px; color: var(--td-text2); letter-spacing: 0; }
  .pa-row-pills { display: flex; gap: 4px; flex-shrink: 0; align-items: center; }

  /* Slim two-line row (docked list): 30px photo · name on top · muted SKU line
     underneath. One language only; stays narrow so the editor pane gets the room,
     while the SKU is readable without a hover. */
  .pa-row-1l { padding: 7px 12px; gap: var(--s2); }
  .pa-row-1l .pa-row-img { width: 30px; height: 30px; border-radius: 7px; }
  .pa-row-1l .pa-row-img svg { width: 15px; height: 15px; }
  .pa-row-1l .pa-row-name { font-size: 13.5px; line-height: 1.3; }
  .pa-row-1l .pa-row-sub { font-size: 11px; line-height: 1.25; }
  .pa-row-1l .pa-row-meta { font-size: 11px; font-variant-numeric: tabular-nums; }

  /* "Available on the shop" switch — sage when live, quiet when not. */
  .pa-shop-toggle {
    position: relative; display: inline-flex; align-items: center;
    width: 38px; height: 22px; flex-shrink: 0; cursor: pointer;
  }
  .pa-shop-toggle input {
    position: absolute; inset: 0; width: 100%; height: 100%;
    margin: 0; opacity: 0; cursor: pointer;
  }
  .pa-shop-knob {
    /* Off = the page's own hairline colour, so it reads "not on" in both themes
       rather than glowing pale against the dark ground. */
    width: 38px; height: 22px; border-radius: 999px;
    background: var(--td-border); transition: background .18s cubic-bezier(.16,1,.3,1);
  }
  .pa-shop-knob::after {
    content: ''; position: absolute; top: 2px; left: 2px;
    width: 18px; height: 18px; border-radius: 50%;
    background: #fff; box-shadow: 0 1px 2px rgba(17,26,43,.20);
    transition: transform .18s cubic-bezier(.16,1,.3,1);
  }
  .pa-shop-toggle.is-on .pa-shop-knob { background: var(--td-sage); }
  .pa-shop-toggle.is-on .pa-shop-knob::after { transform: translateX(16px); }
  /* Mixed family (some sizes live, some not) — amber: needs a look, not an error. */
  .pa-shop-toggle.is-mixed .pa-shop-knob { background: var(--td-amber); }
  .pa-card-shop { display: flex; align-items: center; gap: 7px; margin-top: 8px; }
  .pa-card-shop-lbl { font-size: 12px; color: var(--td-text2); }

  /* ── Overview table (.pa-list-rich) — the landing list. Photo · name ·
        category · SKU · stock · price · shop. Real columns, quiet hairlines. ── */
  .pa-list-rich {
    gap: 0;
    background: var(--td-surface);
    border: 1px solid var(--td-border);
    /* Top corners square, matching .pa-rowhead's now-square top (see below) —
       with no overflow:hidden a rounded top here would poke out past the
       header's straight edge. Bottom stays rounded via .pa-row:last-child. */
    border-radius: 0 0 14px 14px;
    /* No overflow:hidden — .pa-rowhead is position:sticky and needs the
       viewport (not this box) as its containing block, else it would be
       trapped inside the list instead of locking under the topbar. Corners
       are rounded on the header/last row instead. */
    margin-top: 16px;
  }
  .pa-list-rich .pa-row, .pa-rowhead {
    display: grid;
    /* Leading 34px is the always-on select checkbox column. Rest of the track
       list is set inline per render (paRender) — img · name · category ·
       SKU · one column per store · price. Fallback keeps the old single-stock
       shape for safety. */
    grid-template-columns: 34px var(--pa-grid, 46px minmax(0,1fr) minmax(110px,0.8fr) minmax(90px,0.7fr) 104px 116px);
    align-items: center; gap: var(--s3);
  }
  .pa-list-rich .pa-row {
    background: transparent;
    border: none; border-bottom: 1px solid var(--td-border); border-radius: 0;
    padding: 12px 18px; transform: none;
  }
  .pa-list-rich .pa-row:hover { transform: none; background: var(--td-raised); border-color: var(--td-border); }
  .pa-list-rich .pa-row:last-child { border-bottom: none; border-bottom-left-radius: 14px; border-bottom-right-radius: 14px; }
  .pa-list-rich .pa-row.is-selected { background: var(--td-raised); }
  .pa-rowhead {
    padding: 14px 18px; background: var(--td-raised);
    border-bottom: 1px solid var(--td-border);
    /* Square top, not rounded: once stuck it's pinned flush under the topbar
       (top:56px = exact topbar height, no gap) — a rounded top edge there
       reads as a floating pill instead of an attached bar. */
    font-size: 13.5px; letter-spacing: .02em; text-transform: none;
    color: var(--td-text); font-weight: 700;
    position: sticky; top: 56px; z-index: 5;
  }
  .pa-list-rich .pa-row-img { width: 44px; height: 44px; border-radius: 10px; }
  .pa-list-rich .pa-row-name { font-size: 15px; font-weight: 500; }
  .pa-col-cat {
    font-size: 14px; color: var(--td-text);
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  }
  .pa-col-sku {
    font-family: var(--f-sans); font-size: 13px; color: var(--td-text2); letter-spacing: 0;
    font-variant-numeric: tabular-nums;
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  }
  .pa-col-muted { color: var(--td-text2); }
  .pa-col-price {
    font-variant-numeric: tabular-nums; font-size: 14px; font-weight: 500; color: var(--td-text);
    justify-self: end; white-space: nowrap;
  }
  /* Header-only: alignment matches the body's price column, but weight/size
     inherit from .pa-rowhead (700/13.5px) instead of the body's 500/14px —
     "Price" was previously reusing .pa-col-price and picking up its font. */
  .pa-col-price-h { justify-self: end; }
  .pa-col-stock { font-size: 14px; }

  /* Stock — sage healthy · amber low · brick sold out. */
  .pa-stock {
    display: inline-flex; align-items: center; gap: 7px;
    font-variant-numeric: tabular-nums; color: var(--td-text);
  }
  .pa-stock-dot { width: 7px; height: 7px; border-radius: 50%; background: var(--td-sage); flex: none; }
  .pa-stock.is-low .pa-stock-dot { background: var(--td-amber); }
  .pa-stock.is-out { color: var(--td-brick); font-weight: 600; }
  .pa-stock.is-out .pa-stock-dot { background: var(--td-brick); }
  /* Editable per-store stock — replaces the pill when one store is picked. */
  .pa-stock-input {
    width: 72px; padding: 5px 8px; text-align: right;
    font: 400 13px var(--f-sans); font-variant-numeric: tabular-nums;
    border: 1px solid var(--td-border); border-radius: 7px;
    background: var(--td-surface); color: var(--td-text);
  }
  .pa-stock-input:focus { outline: none; border-color: var(--td-dusk); }
  .pa-stock-input.is-saved { border-color: var(--td-sage); background: var(--td-sage-tint); }
  .pa-stock-input.is-err   { border-color: var(--td-brick); background: var(--td-brick-tint); }
  .pa-stock-input::-webkit-outer-spin-button,
  .pa-stock-input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
  /* Variant family in store mode: the total isn't inline-editable (each size
     has its own count) — show it quiet. */
  .pa-stock-locked { color: var(--td-text2); cursor: default; }

  /* ── Two-location stock: one column per store, both always visible ──────────
     The Shop toggle column is gone; every store gets its own headed, editable
     column (Tianhe · Yuexiu …). Centre the pair under their headers, and edge
     the boxes amber when low / brick when sold out (the save-confirm sage flash
     still wins while it shows). */
  .pa-multistock .pa-col-stock,
  .pa-multistock .pa-col-stock-h { justify-self: center; text-align: center; }
  .pa-multistock .pa-stock-input { text-align: center; }
  .pa-stock-input.is-low:not(.is-saved):not(.is-err) { border-color: var(--td-amber); color: var(--td-amber); }
  .pa-stock-input.is-out:not(.is-saved):not(.is-err) { border-color: var(--td-brick); color: var(--td-brick); }

  /* Shop column: centre the toggle under its header. */
  .pa-list-rich .pa-row-pills { justify-content: center; }
  .pa-rowhead .pa-col-shop { text-align: center; }

  /* ── Drag-to-reorder (single category, list overview) ──────────────────────
     A grip column slides in at the front; dragging a row's grip rewrites the
     store-page order (store_products.sort_order, which the shop grid sorts by).
     Only the grip starts a drag — the rest of the row still clicks to edit. */
  .pa-list-rich.pa-reorder .pa-row,
  .pa-list-rich.pa-reorder .pa-rowhead {
    grid-template-columns: 34px 22px var(--pa-grid, 46px minmax(0,1fr) minmax(110px,0.8fr) minmax(90px,0.7fr) 104px 116px);
  }
  .pa-grip {
    display: flex; align-items: center; justify-content: center;
    cursor: grab; color: var(--td-text2); user-select: none; align-self: center;
  }
  .pa-grip svg { width: 13px; height: 14px; display: block; }
  .pa-grip:hover { color: var(--td-text); }
  .pa-grip:active { cursor: grabbing; }
  .pa-row.pa-row-dragging { opacity: .45; }
  /* Drop line = ink. The row you're about to land on, not an alarm. */
  .pa-row.pa-row-drop { box-shadow: inset 0 2px 0 0 var(--td-text); }

  /* ── Multi-select (bulk edit) ──────────────────────────────────────────────
     The leading checkbox column is always on screen (ticking one is itself
     what arms select mode). Once armed, clicking anywhere on a row toggles
     its tick and the bulk bar applies one change to every ticked product.
     Reorder wins over select mode's row-click takeover (mutually exclusive —
     we don't arm reorder while selecting). */
  .pa-selbox { display: flex; align-items: center; justify-content: center; }
  /* Selection is an ordinary act, so it wears Ink — never Dusk. */
  .pa-selbox input { width: 16px; height: 16px; accent-color: var(--td-text); cursor: pointer; margin: 0; }
  /* Once select mode is armed the whole row handles the toggle, so the box
     becomes display-only (its own click would double-fire the row's). Before
     that the box is the only thing that ticks, so it stays clickable. */
  .pa-list-rich.pa-selecting .pa-row .pa-selbox input { pointer-events: none; }
  .pa-list-rich.pa-selecting .pa-row { cursor: pointer; }
  .pa-list-rich .pa-row.is-sel { background: var(--td-raised); }
  .pa-list-rich .pa-row.is-sel:hover { background: var(--td-raised); box-shadow: inset 2px 0 0 0 var(--td-text); }
  /* ── Add-product split button (toolbar) ─────────────────────────────────
     One control: the main face starts the step-by-step wizard; the caret
     drops a small menu with both ways to add. */
  .pa-addsplit { position: relative; display: inline-flex; }
  .pa-addsplit-main { border-top-right-radius: 0; border-bottom-right-radius: 0; }
  .pa-addsplit-caret {
    border-top-left-radius: 0; border-bottom-left-radius: 0;
    padding-left: 8px; padding-right: 8px; margin-left: 1px;
    display: inline-flex; align-items: center; justify-content: center;
  }
  .pa-addsplit-caret svg { transition: transform .18s var(--ease-out, ease); }
  .pa-addsplit.is-open .pa-addsplit-caret svg { transform: rotate(180deg); }
  .pa-addsplit-menu {
    position: absolute; top: calc(100% + 6px); right: 0; z-index: 60;
    min-width: 248px; padding: 6px; background: var(--td-surface);
    border: 1px solid var(--td-border); border-radius: 12px;
    box-shadow: 0 8px 30px rgba(0, 0, 0, .28), 0 1px 2px rgba(0, 0, 0, .18);
  }
  .pa-addsplit-menu[hidden] { display: none; }
  .pa-addsplit-menu button {
    display: flex; align-items: flex-start; gap: 11px; width: 100%;
    padding: 10px 12px; border: 0; background: none; border-radius: 8px;
    font-family: var(--f-sans); font-size: 13px; color: var(--td-text);
    text-align: left; cursor: pointer; transition: background .14s ease;
  }
  .pa-addsplit-menu button:hover { background: var(--td-raised); }
  .pa-addsplit-menu button svg { flex: none; margin-top: 2px; color: var(--td-text2); }
  .pa-addsplit-menu button:hover svg { color: var(--td-text); }
  .pa-addsplit-menu span { display: flex; flex-direction: column; gap: 1px; line-height: 1.35; }
  .pa-addsplit-menu small { color: var(--td-text2); font-size: 11.5px; }

  /* ── Bulk selection bar (#pa-bulkbar in select mode) ────────────────────
     A distinct raised bar that reads as a "mode": one short menu per field
     instead of a wall of chips, with Delete set apart from the safe edits. */
  .pa-bulkbar-sel {
    display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
    margin: 0 0 var(--s3); padding: 9px 12px;
    background: var(--td-raised); border: 1px solid var(--td-border);
    border-radius: 12px; box-shadow: 0 6px 22px rgba(0, 0, 0, .22);
    font-family: var(--f-sans); font-size: 12px;
    animation: paBulkRise .28s var(--ease-out, ease) both;
  }
  @keyframes paBulkRise { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: translateY(0); } }
  .pa-bulk-count { font-weight: 600; color: var(--td-text); display: inline-flex; align-items: center; gap: 7px; }
  .pa-bulk-count svg { width: 15px; height: 15px; color: var(--td-sage); }
  .pa-bulk-actions { margin-left: auto; display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
  .pa-bulk-vr { width: 1px; align-self: stretch; background: var(--td-border); margin: 2px 2px; }

  /* Field menu — trigger button + dropdown. */
  .pa-bm { position: relative; display: inline-block; }
  .pa-bm-trig {
    height: 32px; display: inline-flex; align-items: center; gap: 6px;
    padding: 0 10px; border: 1px solid var(--td-border); border-radius: 8px;
    background: var(--td-surface); color: var(--td-text);
    font-family: var(--f-sans); font-size: 12px; cursor: pointer; transition: border-color .14s ease, background .14s ease;
  }
  .pa-bm-trig:hover:not(:disabled) { border-color: var(--td-text2); }
  .pa-bm-trig:disabled { opacity: .5; cursor: default; }
  .pa-bm-trig svg { width: 12px; height: 12px; color: var(--td-text2); transition: transform .16s ease; }
  .pa-bm.open .pa-bm-trig svg { transform: rotate(180deg); }
  .pa-bm.open .pa-bm-trig { border-color: var(--td-text2); }
  .pa-bm-menu {
    position: absolute; top: calc(100% + 6px); left: 0; z-index: 60;
    min-width: 190px; padding: 6px; background: var(--td-surface);
    border: 1px solid var(--td-border); border-radius: 11px;
    box-shadow: 0 8px 30px rgba(0, 0, 0, .28);
    display: none;
  }
  .pa-bm.open .pa-bm-menu { display: block; animation: paBulkRise .16s var(--ease-out, ease) both; }
  .pa-bm-menu.right { left: auto; right: 0; }
  .pa-bm-menu button {
    display: block; width: 100%; text-align: left; padding: 8px 11px;
    border: 0; background: none; border-radius: 7px;
    font-family: var(--f-sans); font-size: 12.5px; color: var(--td-text);
    cursor: pointer; transition: background .12s ease;
  }
  .pa-bm-menu button:hover { background: var(--td-raised); }
  .pa-bm-h { font-size: 10px; letter-spacing: .06em; text-transform: uppercase; color: var(--td-text2); font-weight: 600; padding: 8px 11px 3px; }
  .pa-bm-sep { height: 1px; background: var(--td-border); margin: 5px 4px; }
  .pa-bulk-del { color: var(--danger); }
  .pa-bulk-del:hover:not(:disabled) { border-color: var(--danger); background: var(--danger-soft); }

  /* Bulk action groups inside #pa-bulkbar when selecting. */
  .pa-bulk-grp { display: inline-flex; align-items: center; gap: 6px; }
  .pa-bulk-grp .pa-bulk-lbl { font-size: 10px; letter-spacing: .05em; text-transform: uppercase; color: var(--td-text2); font-weight: 600; }
  .pa-bulk-sep { width: 1px; align-self: stretch; background: var(--td-border); margin: 0 2px; }

  .pa-reorder-hint {
    display: flex; align-items: center; gap: 7px;
    padding: 9px 4px 2px; font-size: 12px; color: var(--td-text2);
  }
  .pa-reorder-hint svg { width: 14px; height: 14px; opacity: .6; flex: none; }

  /* Variants ("sizes") — sits in the main column between name and description,
     so the most-edited control is reachable without scrolling past the long
     description. Spacing comes from the .pd-main flex gap. */
  .pd-variants {}
  /* Product editor (#pd-editor) — docked in the right column of the Products
     split view. The sizing below is the fallback shape (kept so the editor
     still has reasonable bounds if anything ever renders it outside the split
     container); .pa-split-editor #pd-editor overrides it for the live page. */
  #pd-editor { width: min(1180px, 94vw); height: 90vh; max-width: 94vw; }
  /* Two-pane editor: a main column (photos → name → sizes → description) and a
     narrow commercial sidebar (visibility, pricing, inventory, organization).
     Inputs fill their cell so short fields no longer float in empty space.
     (id+class beats .pu-editor-body) */
  /* The body is the scroll window: fill the height left between the pinned
     head and foot (flex:1 + min-height:0 lets it shrink below content so the
     base .pu-editor-body overflow-y:auto actually engages) instead of growing
     to content height and pushing the foot off-screen. */
  #pd-editor .pu-editor-body { grid-template-columns: minmax(0, 1fr) 320px; gap: 24px; align-items: start; flex: 1 1 auto; min-height: 0; }
  #pd-editor > .pu-editor-head, #pd-editor > .pu-editor-foot { flex: none; }
  @media (max-width: 880px) { #pd-editor .pu-editor-body { grid-template-columns: 1fr; } }
  #pd-editor .pd-main { display: flex; flex-direction: column; gap: 22px; min-width: 0; }
  #pd-editor .pd-side { display: flex; flex-direction: column; gap: 14px; min-width: 0; }
  /* ── Essentials-first editor layout (pe-*) ─────────────────────────────────
     The body is one column: an ESSENTIALS block (the daily handful) over a
     stack of quiet disclosure ROWS (everything occasional). Replaces the old
     two-column pd-main/pd-side grid. All classes are #pd-editor-scoped so they
     never collide with the base sheet's generic names. */
  #pd-editor .pu-editor-body.pe-body { display: block; }
  #pd-editor .pe-ess { display: grid; grid-template-columns: minmax(0, 1fr) 320px; gap: 24px; align-items: start; margin-bottom: 20px; }
  @media (max-width: 880px) { #pd-editor .pe-ess { grid-template-columns: 1fr; } }
  #pd-editor .pe-ess-main { display: flex; flex-direction: column; gap: 18px; min-width: 0; }
  #pd-editor .pe-ess-side { display: flex; flex-direction: column; gap: 14px; min-width: 0; }
  #pd-editor .pe-rows { display: flex; flex-direction: column; gap: 10px; }
  /* A quiet row = native <details> styled to match the .pd-var-toggle cards the
     variants/bundle/options sections render, so the whole stack reads as one. */
  #pd-editor .pe-dis { border: 1px solid var(--line-soft); border-radius: 10px; background: var(--bg-app); overflow: hidden; transition: border-color .12s ease; }
  #pd-editor .pe-dis[open] { background: var(--bg-elev-1); }
  #pd-editor .pe-dis > summary { display: flex; align-items: center; gap: 8px; padding: 11px 12px; cursor: pointer; list-style: none; user-select: none; }
  #pd-editor .pe-dis > summary::-webkit-details-marker { display: none; }
  #pd-editor .pe-dis > summary::marker { content: ""; }
  #pd-editor .pe-dis > summary:hover { color: var(--text-1); }
  #pd-editor .pe-dis:hover { border-color: var(--brass); }
  #pd-editor .pe-dis-t { font-family: var(--f-sans); font-size: 10px; letter-spacing: .1em; text-transform: uppercase; color: var(--td-text2); }
  #pd-editor .pe-dis-s { font-size: 12px; color: var(--text-2); font-family: var(--f-sans); letter-spacing: 0; text-transform: none; margin-left: auto; }
  #pd-editor .pe-dis .pe-caret { color: var(--text-4); width: 12px; flex: none; transition: transform .12s ease; }
  #pd-editor .pe-dis[open] > summary .pe-caret { transform: rotate(90deg); }
  #pd-editor .pe-dis-body { padding: 12px 14px 16px; border-top: 1px solid var(--line-soft); }
  #pd-editor .pe-dis-body > .pd-sec + .pd-sec { margin-top: 16px; }
  #pd-editor .pe-dis-body .field + .field { margin-top: 12px; }
  #pd-editor .pd-row2 { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
  #pd-editor .field { margin-bottom: 0; }
  #pd-editor .field input[type=text],
  #pd-editor .field input[type=number],
  #pd-editor .field input[type=url],
  #pd-editor .field select { width: 100%; box-sizing: border-box; }
  #pd-editor .pd-card { background: var(--bg-app); border: 1px solid var(--line-soft); border-radius: 10px; padding: 14px; display: flex; flex-direction: column; gap: 12px; }
  /* Override the global `section { display:none }` (used for page-level views) —
     these <section> wrappers are content groups inside the editor. */
  #pd-editor .pd-sec { display: block; }
  #pd-facts.pd-facts-collapsed .pd-facts-body { display: none; }
  #pd-facts.pd-facts-collapsed .pd-sec-h { margin-bottom: 0; }
  #pd-facts:not(.pd-facts-collapsed) .pd-facts-caret { transform: rotate(90deg); }
  #pd-editor .pd-sec-h, #pd-editor .pd-card-h { font-family: var(--f-sans); font-size: 10px; letter-spacing: .1em; text-transform: uppercase; color: var(--td-text2); margin: 0; }
  #pd-editor .pd-pub { flex-direction: row; align-items: center; gap: 10px; cursor: pointer; font-weight: 600; }
  #pd-editor .pd-pub input { width: 16px; height: 16px; }
  /* Autosave status pill in the editor foot. Replaces the old manual Save
     button — edits persist on their own; this just reports state. */
  .pd-save-status { font: 500 12px var(--f-sans); color: var(--text-3); display: inline-flex; align-items: center; gap: 6px; white-space: nowrap; transition: color .2s; }
  .pd-save-status::before { content: ""; width: 7px; height: 7px; border-radius: 50%; background: currentColor; opacity: .55; }
  .pd-save-status.is-saving { color: var(--brass); }
  .pd-save-status.is-saved  { color: var(--ok, #5a8a5e); }
  .pd-save-status.is-dirty  { color: var(--text-3); }
  .pd-save-status.is-hint   { color: var(--brass); }
  .pd-save-status.is-error  { color: var(--danger, #b65656); }

  /* Products page split view: list on the left, editor docked on the right.
     The editor used to open in a centered .pu-editor-bg overlay; now it lives
     inline as a second column. Container collapses to one column on narrow
     widths, and to a single column (list-only or editor-only) when the user
     hits Expand on the editor or switches to grid/compact catalogue scan. */
  .pa-split { display: grid; grid-template-columns: minmax(0, 288px) minmax(0, 1fr); gap: 16px; align-items: start; }
  .pa-split-list { min-width: 0; }
  /* The editor flows at full height and the whole page scrolls (one scroll, not
     a pane-within-a-page). While the editor is docked the list is capped to 18
     rows (see PA_DOCK_PAGE_SIZE in paRender) so it ends near the editor's bottom
     edge — no dead gap, no separate scroll. The overview list (editor closed)
     uses the operator's "Per page" choice instead. */
  .pa-split-editor { min-width: 0; }
  @media (max-width: 1024px) { .pa-split { grid-template-columns: 1fr; } .pa-split-editor { position: static; } }
  /* Catalogue-scan mode: grid/compact view hides the editor so the list takes
     the full page width — same flow as before. List view → split. */
  .pa-split.pa-split-listonly { grid-template-columns: 1fr; }
  .pa-split.pa-split-listonly .pa-split-editor { display: none; }
  /* Expand-editor mode: hide the list so the editor takes the full page. */
  .pa-split.pa-split-editonly { grid-template-columns: 1fr; }
  .pa-split.pa-split-editonly .pa-split-list { display: none; }
  /* Selected row highlight — picks out the product currently loaded in the
     docked editor so the operator never loses their place after a save. */
  .pa-row.is-selected { border-color: var(--brass); background: var(--bg-elev-1); }
  /* Docked editor: drop the modal sizing (centered 1180×90vh) and let it grow
     to its content in the right column. No max-height → no internal scroll; the
     whole page scrolls through it instead, so the edit form isn't squeezed into
     a short box. */
  .pa-split-editor #pd-editor { width: auto; height: auto; max-width: 100%; max-height: none; }
  /* Docked editor scrolls WITH the page, not as its own pane. The base
     .pu-editor-body is overflow-y:auto + overscroll-behavior:contain (for the
     modal shell); inside the split that turns the white form area into a
     scroll trap — wheeling over it does nothing because it's a (non-scrolling)
     scroll container that swallows the chain. Make it overflow:visible so the
     wheel falls through to the page. */
  .pa-split-editor #pd-editor .pu-editor-body { overflow: visible; overscroll-behavior: auto; }
  /* Empty state shown in the editor pane when no product is selected. */
  .pd-empty { background: var(--td-surface); border: 1px dashed var(--td-border); border-radius: 14px; padding: 56px 24px; text-align: center; color: var(--td-text2); font-family: var(--f-sans); }
  .pd-empty .pd-empty-h { font-size: 14px; color: var(--td-text); margin: 0 0 6px; font-weight: 500; }
  .pd-empty .pd-empty-sub { font-size: 12.5px; color: var(--td-text2); margin: 0; }

  /* Product photo gallery (editor) — thumbnails, drag-to-reorder, first = main. */
  .pd-gallery { display: flex; flex-wrap: wrap; gap: 10px; min-height: 8px; }
  .pd-gallery .pd-thumb {
    position: relative; width: 84px; height: 84px; border-radius: 8px;
    border: 1px solid var(--line); overflow: hidden; background: var(--bg-app);
    cursor: grab; flex-shrink: 0;
  }
  .pd-gallery .pd-thumb.dragging { opacity: .4; }
  .pd-gallery .pd-thumb.drop-target { border-color: var(--brand, #A3B49F); box-shadow: 0 0 0 2px var(--brand, #A3B49F); }
  .pd-gallery .pd-thumb-img { width: 100%; height: 100%; background-size: cover; background-position: center; cursor: zoom-in; }
  /* Full-size photo lightbox (click a gallery thumb to open). */
  #pd-lightbox {
    position: fixed; inset: 0; z-index: 4000; display: flex; align-items: center;
    justify-content: center; background: rgba(0,0,0,.82); cursor: zoom-out;
    animation: pd-lb-fade .18s ease;
  }
  @keyframes pd-lb-fade { from { opacity: 0; } to { opacity: 1; } }
  #pd-lightbox .pd-lb-img {
    max-width: 92vw; max-height: 92vh; object-fit: contain; cursor: default;
    border-radius: 6px; box-shadow: 0 16px 60px rgba(0,0,0,.5);
  }
  #pd-lightbox .pd-lb-x {
    position: absolute; top: 18px; right: 22px; width: 40px; height: 40px;
    border: none; border-radius: 50%; background: rgba(255,255,255,.14); color: #fff;
    font-size: 26px; line-height: 1; cursor: pointer; display: flex;
    align-items: center; justify-content: center; padding: 0;
  }
  #pd-lightbox .pd-lb-x:hover { background: rgba(255,255,255,.26); }
  #pd-lightbox .pd-lb-nav {
    position: absolute; top: 50%; transform: translateY(-50%); width: 48px; height: 48px;
    border: none; border-radius: 50%; background: rgba(255,255,255,.14); color: #fff;
    font-size: 32px; line-height: 1; cursor: pointer; display: flex;
    align-items: center; justify-content: center; padding: 0;
  }
  #pd-lightbox .pd-lb-nav:hover { background: rgba(255,255,255,.26); }
  #pd-lightbox .pd-lb-prev { left: 22px; }
  #pd-lightbox .pd-lb-next { right: 22px; }
  #pd-lightbox .pd-lb-count {
    position: absolute; bottom: 20px; left: 0; right: 0; text-align: center;
    color: rgba(255,255,255,.8); font-family: var(--f-mono); font-size: 12px; letter-spacing: .08em;
  }
  .pd-gallery .pd-thumb-x {
    position: absolute; top: 3px; right: 3px; width: 20px; height: 20px;
    border: none; border-radius: 50%; background: rgba(0,0,0,.62); color: #fff;
    font-size: 14px; line-height: 1; cursor: pointer; display: flex;
    align-items: center; justify-content: center; padding: 0;
  }
  .pd-gallery .pd-thumb-main {
    position: absolute; left: 0; right: 0; bottom: 0; background: rgba(0,0,0,.62);
    color: #fff; font-size: 9px; letter-spacing: .04em; text-align: center; padding: 2px 0;
  }
  .pd-var-head { font-family: var(--f-sans); font-size: 10px; letter-spacing: .1em; text-transform: uppercase; color: var(--td-text2); margin-bottom: 6px; }
  /* Variants is collapsed by default (a single-option product needs no table);
     this header is the open/close toggle. Caret + label + a summary chip so the
     operator sees the state ("Single option" / "N variants") without expanding. */
  .pd-var-toggle { display: flex; align-items: center; gap: 8px; width: 100%; background: var(--bg-app); border: 1px solid var(--line-soft); border-radius: 10px; padding: 10px 12px; cursor: pointer; text-align: left; transition: border-color .12s ease, background .12s ease; }
  .pd-var-toggle .pd-var-caret { color: var(--text-4); font-size: 10px; width: 10px; transition: transform .12s ease; }
  .pd-var-toggle .pd-var-summary { font-size: 12px; color: var(--text-2); font-family: var(--f-sans); letter-spacing: 0; text-transform: none; margin-left: auto; }
  .pd-var-toggle:hover { border-color: var(--brass); background: var(--bg-elev-1); } .pd-var-toggle:hover .pd-var-summary { color: var(--text-1); }
  /* Sizes table — every size of a product on one inline-editable row
     (name · sku · price · stock · show), so the operator never has to open a
     size just to set its stock. The size you opened is read-only here (edit it
     in the form); sibling sizes are editable inline; ✕ deletes the size. */
  /* The table lives in the narrow 1fr left column (the 360px stock rail sits
     beside it). overflow-x:auto is a safety net, but the row is sized to FIT the
     pane so the trailing ✕ delete button is always visible without scrolling. */
  .va-table { display: flex; flex-direction: column; gap: 5px; margin-top: 10px; overflow-x: auto; }
  /* grip · photo · NAME(flex) · sku · ¥ · was · stock · show · ✕ — the Name
     column is the flexible track (minmax 64px→1fr): it shrinks first so every
     fixed column, including the ✕ delete button at the end, always stays on
     screen instead of overflowing off the right edge. */
  .va-row { display: grid; grid-template-columns: 20px 42px minmax(64px,1fr) 80px 64px 58px 54px 48px 34px 26px; gap: 8px; align-items: center; min-width: 0; }
  /* Size/weight column input — narrower text field; feeds the size picker's grey subline. */
  .va-wt { font-size: 13px; }
  /* Was-price (compare-at) column input: dimmed so it reads as the secondary,
     optional "original price" next to the live ¥ price. */
  .va-num.va-was { color: var(--text-3); }
  .va-num.va-was::placeholder { color: var(--text-4); }
  .va-row-head span { font-size: 10px; letter-spacing: .05em; text-transform: uppercase; color: var(--text-4); font-family: var(--f-mono); }
  .va-row-head .va-num { text-align: right; }
  .va-drag { cursor: grab; color: var(--text-4); text-align: center; user-select: none; font-size: 13px; line-height: 1; }
  .va-drag:active { cursor: grabbing; }
  .va-row.va-drop-before { box-shadow: inset 0 2px 0 var(--brass); }
  .va-row.va-drop-after  { box-shadow: inset 0 -2px 0 var(--brass); }
  .va-label { width: 100%; box-sizing: border-box; }
  /* Variant option name = two stacked inputs (English over 中文), so the EN and
     CN options are entered & stored separately rather than glued into one tag. */
  .va-name { display: flex; flex-direction: column; gap: 4px; min-width: 0; }
  .va-name .va-label-cn { font-size: 13px; }
  .va-name .va-label-cn::placeholder { color: var(--text-4); }
  .va-num { width: 100%; box-sizing: border-box; text-align: right; font-variant-numeric: tabular-nums; }
  .va-ro { font-size: 12px; color: var(--text-3); font-variant-numeric: tabular-nums; text-align: right; }
  .va-show { display: flex; justify-content: center; }
  .va-self { color: var(--brass); font-size: 12px; text-align: center; cursor: help; }
  /* Per-variant photo: a small square thumbnail that doubles as the upload
     trigger. Empty rows show a dashed "＋" placeholder; clicking either opens
     the file picker (siblings) or jumps to the gallery above (the self row). */
  .va-photo { width: 40px; height: 40px; border-radius: 6px; padding: 0; cursor: pointer; overflow: hidden;
              border: 1px solid var(--line); background: var(--surface-2); background-size: cover; background-position: center;
              display: flex; align-items: center; justify-content: center; position: relative; flex: none; }
  .va-photo:hover { border-color: var(--brass); }
  .va-photo.va-photo-empty { border-style: dashed; color: var(--text-4); font-size: 17px; line-height: 1; }
  .va-photo.va-photo-busy { opacity: .5; pointer-events: none; }
  /* Inherited (not its own) photo: dimmed + a brass corner dot so the operator
     can see at a glance which shades still share the group photo. */
  .va-photo.va-photo-shared { opacity: .6; }
  .va-photo.va-photo-shared::after { content: ''; position: absolute; top: 2px; right: 2px; width: 6px; height: 6px;
                                     border-radius: 50%; background: var(--brass); box-shadow: 0 0 0 1.5px var(--surface-2); }
  /* Out-of-stock size — faint row tint + red stock cell so it stands out. */
  .va-row.va-oos { background: rgba(182,86,86,.06); border-radius: 6px; }
  .va-row.va-oos .va-stk { color: var(--danger); border-color: rgba(182,86,86,.5); }
  /* Stock-by-location grid: variant rows × store columns + a live row total.
     One place to set every variant's stock in every store. Wrapped so many
     stores scroll horizontally instead of bursting out of the narrow card. */
  #pd-store-stocks { overflow-x: auto; }
  .pd-stock-matrix { width: 100%; border-collapse: collapse; font-size: 13px; }
  .pd-stock-matrix th, .pd-stock-matrix td { padding: 4px 5px; text-align: center; }
  .pd-stock-matrix th:first-child, .pd-stock-matrix td:first-child { padding-left: 0; }
  .pd-stock-matrix tr:first-child th { font-size: 10px; letter-spacing: .04em; text-transform: uppercase; color: var(--text-4); font-family: var(--f-mono); font-weight: 500; }
  /* Variant-name cell — left-aligned, normal case, and capped so a long product
     name truncates with an ellipsis rather than widening the whole table. */
  .pd-stock-matrix th.vh { text-align: left; color: var(--text-1); font-weight: 600; text-transform: none; letter-spacing: 0; font-family: var(--f-sans); white-space: nowrap; max-width: 140px; overflow: hidden; text-overflow: ellipsis; }
  .pd-stock-matrix tr:first-child th.vh { color: var(--text-4); font-weight: 500; }
  .pd-stock-matrix input { width: 56px; box-sizing: border-box; padding: 6px 7px; border: 1px solid var(--line,#d8d2c4); border-radius: 8px; font-size: 13px; text-align: right; font-variant-numeric: tabular-nums; }
  .pd-stock-matrix td.mtot, .pd-stock-matrix th.mtot { color: var(--text-2); font-weight: 600; min-width: 30px; font-variant-numeric: tabular-nums; }
  .pd-stock-matrix .va-self { font-size: 11px; }
  /* Description editor — Classic (rich) editor + "Product editor" launch, EN|CN side-by-side. */
  .pd-desc-modebar { display: inline-flex; gap: 4px; margin-left: 10px; vertical-align: middle; }
  .pd-mode-btn { font-size: 11px; padding: 3px 10px; border: 1px solid var(--line); background: var(--bg-pane); color: var(--text-2); border-radius: 6px; cursor: pointer; font-weight: 500; }
  .pd-mode-btn.active { background: var(--brass); color: #fff; border-color: var(--brass); }
  /* The "🎨 Design" switch reads as a toggle — green when on, not brass, so it's
     not confused with the always-on Classic tab. */
  .pd-blockdesign-toggle.active { background: var(--brand, #A3B49F); border-color: var(--brand, #A3B49F); color: #fff; }
  .pd-desc-cols { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; }
  @media (max-width: 760px) { .pd-desc-cols { grid-template-columns: 1fr; } }
  .pd-desc-lang { font-size: 10px; letter-spacing: .08em; text-transform: uppercase; color: var(--text-3); margin-bottom: 4px; font-family: var(--f-mono); }
  /* Advanced rich-text toolbar — grouped icon buttons, format dropdown,
     colour swatches and a fullscreen toggle. Commands run on whichever
     EN/CN box was last focused (selection tracked in JS). */
  .pd-rte-toolbar { display: flex; flex-wrap: wrap; align-items: center; gap: 2px; margin-bottom: 8px; padding: 5px 6px; border: 1px solid var(--line); border-radius: 8px; background: var(--bg-pane); position: sticky; top: 0; z-index: 2; }
  .pd-rte-toolbar button { min-width: 30px; height: 30px; padding: 0 7px; border: 1px solid transparent; background: transparent; color: var(--text-1); border-radius: 6px; cursor: pointer; font-size: 13px; line-height: 1; display: inline-flex; align-items: center; justify-content: center; }
  .pd-rte-toolbar button:hover { background: var(--bg-elev-1, rgba(255,255,255,.06)); border-color: var(--line); }
  .pd-rte-toolbar button:active { transform: translateY(1px); }
  .pd-rte-toolbar button svg { width: 16px; height: 16px; display: block; }
  .pd-rte-toolbar .pd-rte-clr { font-size: 13px; font-weight: 600; }
  .pd-rte-toolbar .pd-rte-clr sub { font-size: 9px; }
  .pd-rte-sep { width: 1px; align-self: stretch; margin: 3px 4px; background: var(--line); }
  /* Keep the global <select> chevron (set via background-image on `select`) —
     only override colour/size, never the `background` shorthand (which would
     wipe the arrow) and leave room on the right for it. */
  .pd-rte-sel { height: 30px; border: 1px solid var(--line); background-color: var(--bg-app); color: var(--text-1); border-radius: 6px; font-size: 12px; padding: 0 26px 0 8px; cursor: pointer; }
  .pd-rte-sel:hover { border-color: var(--brass); }
  /* Colour controls — native picker behind an "A"/highlight glyph; the bar
     under the glyph reflects the chosen colour. */
  .pd-rte-color { position: relative; display: inline-flex; flex-direction: column; align-items: center; justify-content: center; width: 30px; height: 30px; border: 1px solid transparent; border-radius: 6px; cursor: pointer; }
  .pd-rte-color:hover { background: var(--bg-elev-1, rgba(255,255,255,.06)); border-color: var(--line); }
  .pd-rte-color > span { font-size: 13px; font-weight: 700; line-height: 1; pointer-events: none; }
  .pd-rte-color > i { display: block; width: 16px; height: 3px; border-radius: 2px; margin-top: 1px; background: #e23b3b; pointer-events: none; }
  .pd-rte-color.pd-rte-hl > i { background: #ffe680; }
  .pd-rte-color input[type=color] { position: absolute; inset: 0; width: 100%; height: 100%; opacity: 0; border: none; padding: 0; margin: 0; cursor: pointer; }
  /* Each editor is a fixed-height window: it scrolls its OWN content
     (overflow:auto) instead of growing to fit a long description and pushing
     the rest of the form down — so the office edits the text without scrolling
     the whole modal. resize:vertical still lets them drag it, capped by
     max-height. Fullscreen overrides this cap below. */
  .pd-rte { min-height: 140px; max-height: 42vh; overflow: auto; resize: vertical; border: 1px solid var(--line); border-radius: 8px; padding: 10px 12px; font-size: 13px; line-height: 1.5; color: var(--text-1); background: var(--bg-app); overflow-wrap: break-word; }
  .pd-rte:focus { outline: none; border-color: var(--brass); }
  .pd-rte h2 { font-size: 17px; margin: 8px 0 4px; font-weight: 700; }
  .pd-rte h3 { font-size: 15px; margin: 6px 0; }
  .pd-rte ul, .pd-rte ol { padding-left: 20px; margin: 6px 0; }
  .pd-rte blockquote { margin: 6px 0; padding: 2px 0 2px 12px; border-left: 3px solid var(--line); color: var(--text-2); }
  .pd-rte hr { border: none; border-top: 1px solid var(--line); margin: 10px 0; }
  .pd-rte a { color: var(--brass); }
  /* Inserted/pasted images stay inside the editor column (never bleed past it
     into the right rail) and preview exactly how the mini-app renders them —
     full column width, proportional height. Overrides any fixed pixel width an
     image carried in. */
  .pd-rte img { max-width: 100% !important; height: auto !important; display: block; margin: 8px auto; }
  /* Fullscreen — lift the whole formatted block over the page so the office
     can write a long description without the cramped modal. */
  #pd-desc-rich.pd-rte-fs { position: fixed; inset: 0; z-index: 9999; margin: 0; padding: 16px 20px 20px; background: var(--bg-app); overflow-y: auto; }
  #pd-desc-rich.pd-rte-fs .pd-rte { max-height: calc(100vh - 200px); min-height: 50vh; }
  body.pd-rte-fs-lock { overflow: hidden; }
  .va-x { flex: none; border: none; background: none; cursor: pointer; color: var(--text-3); font-size: 14px; padding: 2px 6px; line-height: 1; }
  .va-x:hover { color: var(--brass); }
  .va-actions { display: flex; gap: 8px; margin-top: 12px; }
  /* Variant-axis noun ("color"/"颜色") — drives the storefront picker heading. */
  .va-noun { display: grid; grid-template-columns: auto 1fr 1fr; align-items: center; gap: 8px; margin: 10px 0 4px; padding: 8px 10px; background: var(--bg-app); border: 1px solid var(--line-soft); border-radius: 10px; }
  .va-noun .va-noun-lbl { font-size: 12px; font-weight: 700; color: var(--text-2); white-space: nowrap; }
  .va-noun .va-noun-hint { grid-column: 1 / -1; font-size: 11px; color: var(--text-4); }
  @media (max-width: 560px) { .va-noun { grid-template-columns: 1fr 1fr; } .va-noun .va-noun-lbl { grid-column: 1 / -1; } }
  /* "Buy together" bundle editor (#pd-bundle) — chips of chosen products + a
     search picker. No bundle SKU; items keep their own price/stock. */
  .bu-row2 { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; margin-top: 8px; }
  .bu-chips { display: flex; flex-wrap: wrap; gap: 6px; margin: 10px 0 2px; }
  .bu-chip { display: inline-flex; align-items: center; gap: 6px; background: var(--bg-app); border: 1px solid var(--line-soft); border-radius: 14px; padding: 3px 6px 3px 10px; font-size: 12px; color: var(--text-1); max-width: 220px; }
  .bu-chip span { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
  .bu-chip-x { border: none; background: none; cursor: pointer; color: var(--text-3); font-size: 13px; line-height: 1; padding: 0 2px; flex: none; }
  .bu-chip-x:hover { color: var(--brass); }
  .bu-search { width: 100%; margin-top: 8px; box-sizing: border-box; }
  .bu-picklist { margin-top: 6px; max-height: 220px; overflow-y: auto; border: 1px solid var(--line-soft); border-radius: var(--r-md); }
  .bu-pick { padding: 8px 12px; font-size: 12px; cursor: pointer; border-bottom: 1px solid var(--line-soft); }
  .bu-pick:last-child { border-bottom: none; }
  .bu-pick:hover { background: var(--bg-elev-1); color: var(--text-1); }
  .bu-pick-tag { color: var(--text-3, #8a8f80); font-size: 11px; margin-left: 6px; }
  /* Bundle-product editor (#pd-bundle-product) — its own chip so the long
     product name truncates while the qty stepper + remove stay fixed-width and
     never get clipped (the bu-chip span ellipsis rule was squeezing the qty). */
  .bp-chip { display: inline-flex; align-items: center; gap: 8px; background: var(--bg-app); border: 1px solid var(--line-soft); border-radius: 14px; padding: 3px 8px 3px 12px; font-size: 12px; color: var(--text-1); }
  .bp-chip-name { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 160px; }
  .bp-chip-qty { display: inline-flex; align-items: center; gap: 3px; color: var(--text-3); flex: none; }
  /* Scope under the container #id so this beats the base input[type=number] rule
     (equal specificity, but that one is later in the file → it would otherwise
     win and re-apply 8px 10px padding, squeezing the digit to a sliver). */
  #pd-bundle-product .bp-chip-qty input {
    width: 46px; height: 28px; box-sizing: border-box;
    padding: 0 4px; font: 13px var(--f-sans); line-height: 26px; text-align: center;
    border: 1px solid var(--line-soft); border-radius: 6px;
    background: var(--bg-pane); color: var(--text-1);
    appearance: textfield; -moz-appearance: textfield;
  }
  #pd-bundle-product .bp-chip-qty input::-webkit-outer-spin-button,
  #pd-bundle-product .bp-chip-qty input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
  .bp-chip-x { border: none; background: none; cursor: pointer; color: var(--text-3); font-size: 13px; line-height: 1; padding: 0 2px; flex: none; }
  .bp-chip-x:hover { color: var(--brass); }
  /* Bundle stock-mode chooser (#pd-bundle-product) — two selectable cards:
     "assembled from components" vs "pre-packed". */
  #pd-bundle-product .bp-modes { display: flex; flex-direction: column; gap: 8px; margin: 10px 0 2px; }
  #pd-bundle-product .bp-mode { display: flex; align-items: flex-start; gap: 10px; border: 1px solid var(--line-soft); border-radius: var(--r-md); padding: 10px 12px; background: var(--bg-app); cursor: pointer; transition: border-color .12s ease, background .12s ease; }
  #pd-bundle-product .bp-mode:hover { border-color: var(--brass); }
  #pd-bundle-product .bp-mode.is-on { border-color: var(--brass); background: var(--bg-elev-1); }
  #pd-bundle-product .bp-mode input { margin: 2px 0 0; flex: none; accent-color: var(--brass); }
  #pd-bundle-product .bp-mode-txt { display: flex; flex-direction: column; gap: 2px; }
  #pd-bundle-product .bp-mode-title { font-size: 13px; color: var(--text-1); font-family: var(--f-sans); }
  #pd-bundle-product .bp-mode-desc { font-size: 11px; color: var(--text-3); line-height: 1.4; }
  /* Customer-choice options editor (#pd-options) — one .opt-block per option
     (question + type + required), each holding a list of .opt-choice rows. */
  .opt-list { display: flex; flex-direction: column; gap: 10px; margin: 10px 0 2px; }
  .opt-block { border: 1px solid var(--line-soft); border-radius: var(--r-md); padding: 10px 12px; background: var(--bg-app); }
  .opt-qrow { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
  .opt-q { flex: 1 1 160px; min-width: 120px; }
  .opt-type { flex: none; }
  .opt-req { display: inline-flex; align-items: center; gap: 5px; font-size: 12px; color: var(--text-2); white-space: nowrap; cursor: pointer; }
  .opt-req input { margin: 0; }
  .opt-choices { display: flex; flex-direction: column; gap: 6px; margin-top: 8px; padding-top: 8px; border-top: 1px dashed var(--line-soft); }
  .opt-choice { display: flex; align-items: center; gap: 8px; }
  /* Drag-to-reorder grip — choices within an option, and whole option blocks. */
  .opt-drag { flex: none; display: inline-flex; align-items: center; justify-content: center;
    width: 18px; height: 24px; color: var(--text-3); cursor: grab; border-radius: 4px;
    -webkit-user-select: none; user-select: none; touch-action: none; }
  .opt-drag:hover { color: var(--text-1); background: color-mix(in srgb, var(--text-3) 14%, transparent); }
  .opt-drag:active { cursor: grabbing; }
  .opt-drag-block { align-self: center; }
  .opt-dragging { opacity: 0.45; }
  .opt-choice.opt-dragover { box-shadow: inset 0 2px 0 0 var(--brass); }
  .opt-block.opt-dragover { box-shadow: 0 0 0 2px color-mix(in srgb, var(--brass) 55%, transparent); }
  .opt-choice > input[type="radio"], .opt-choice > input[type="checkbox"] { margin: 0; flex: none; }
  .opt-clab { flex: 1 1 120px; min-width: 90px; }
  .opt-cdelta { display: inline-flex; align-items: center; gap: 2px; color: var(--text-3); font-size: 12px; flex: none; }
  #pd-options .opt-cdelta input {
    width: 64px; height: 28px; box-sizing: border-box; padding: 0 6px;
    font: 13px var(--f-sans); border: 1px solid var(--line-soft); border-radius: 6px;
    background: var(--bg-pane); color: var(--text-1);
  }
  .opt-addc { align-self: flex-start; margin-top: 2px; }
  /* Stock-product link on a paid add-on choice. */
  .opt-link-btn { flex: none; white-space: nowrap; }
  .opt-link { display: inline-flex; align-items: center; gap: 6px; flex: none; max-width: 220px;
    font-size: 12px; color: var(--text-1); background: color-mix(in srgb, var(--brass) 12%, transparent);
    border: 1px solid color-mix(in srgb, var(--brass) 40%, transparent); border-radius: 14px; padding: 3px 6px 3px 10px;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .opt-unlink { border: none; background: none; cursor: pointer; color: var(--text-3); font-size: 12px; line-height: 1; padding: 0 2px; flex: none; }
  .opt-unlink:hover { color: var(--brass); }
  .opt-picker { flex-basis: 100%; margin-top: 6px; }
  /* "Bundle" card pill — flags a product that is sold as one but made of others.
     A fact about how it's built, so it stays quiet cream rather than a colour. */
  .pa-pill-bundle { background: rgba(246,243,236,.88); color: #58606E; border: 1px solid rgba(17,26,43,.12); }
  /* Order-line bundle "what's inside" pack list (admin order drawer). */
  /* Chosen customer options (dressing / paid extras) on an order line — the
     distinguishing info when two lines share a SKU. Brass + medium weight so a
     packer's eye lands on it. */
  .oi-opts {
    margin-top: 8px; padding: 5px 0 4px 10px;
    border-left: 2px solid color-mix(in srgb, var(--brass) 35%, transparent);
    font-size: 12px; font-weight: 500; color: var(--brass);
    line-height: 1.5; word-break: break-word;
  }
  .oi-bndl { margin-top: 6px; padding: 6px 8px; border-left: 2px solid color-mix(in srgb, var(--brass) 50%, transparent); background: var(--bg-app); border-radius: 0 6px 6px 0; }
  .oi-bndl-h { font-size: 10px; text-transform: uppercase; letter-spacing: .04em; color: var(--text-3); margin-bottom: 3px; }
  .oi-bndl-row { display: flex; align-items: center; gap: 6px; font-size: 12px; color: var(--text-1); line-height: 1.5; }
  .oi-bndl-sku { font-size: 10px; padding: 0 5px; flex: none; }
  .oi-bndl-qty { color: var(--brass); font-variant-numeric: tabular-nums; min-width: 24px; }
  .oi-bndl-zh { color: var(--text-3); }

  .pa-card {
    background: var(--td-surface);
    border: 1px solid var(--td-border);
    border-radius: 14px;
    overflow: hidden;
    cursor: pointer;
    transition: border-color .18s cubic-bezier(.16,1,.3,1), box-shadow .18s cubic-bezier(.16,1,.3,1);
    display: flex; flex-direction: column;
  }
  .pa-card:hover { box-shadow: 0 1px 2px rgba(17,26,43,.05), 0 4px 16px rgba(17,26,43,.04); }
  .pa-card.is-selected { border-color: var(--td-text); box-shadow: 0 0 0 1px var(--td-text); }
  /* Quick-duplicate button — hover-revealed top-right on cards, trailing on rows. */
  .pa-card { position: relative; }
  .pa-dup {
    position: absolute; top: 6px; right: 6px; z-index: 3;
    width: 26px; height: 26px; padding: 0; display: none;
    align-items: center; justify-content: center;
    border: 1px solid var(--td-border); border-radius: 6px;
    background: var(--td-surface); color: var(--td-text2); cursor: pointer;
    font-size: 13px; line-height: 1;
  }
  .pa-card:hover .pa-dup { display: flex; }
  .pa-dup:hover { border-color: var(--td-text2); color: var(--td-text); }
  /* Upstream catalog mode (e.g. Roots): products + stock are owned by the
     kitchen app, so the mini-app backend never creates/duplicates products.
     Hide the catalog-write controls; clicking a card opens the page designer. */
  body.catalog-upstream #pa-add-btn,
  body.catalog-upstream .pa-dup { display: none !important; }
  /* Upstream mode never docks the in-page editor either — clicking a product
     opens the standalone Product Page Editor in a new tab, so the right-hand
     editor pane is permanently empty dead weight (and its placeholder even
     points at the now-hidden "+ Add product" button). Force the catalogue
     full-width in every view and drop the editor pane + its placeholder, so the
     List view is a real full-width row list instead of a narrow column beside a
     useless panel. */
  body.catalog-upstream .pa-split { grid-template-columns: 1fr !important; }
  body.catalog-upstream .pa-split-editor { display: none !important; }
  /* Per-tenant feature gating — Option B: "off" never hides a feature, it dims it.
     Two body flags per feature: ent-<key> = platform-entitled (hard ceiling) and
     feat-<key> = entitled AND operator-enabled (set in applyFeatureGates).
       • not entitled  → element removed entirely (no dead switches)
       • entitled, off → nav entry stays but dims + shows an "Off" pill; the
                         feature's own page hides its config and shows an enable
                         card; the on/off switch (.feat-bar) stays live
       • entitled, on  → everything normal
     The switch lives on each feature's own page (.feat-bar, injected by
     mountFeatureControls()) so a feature is managed in ONE place. */
  /* 1 — not entitled: hide every tagged element. */
  body:not(.ent-membership)  [data-feat="membership"],
  body:not(.ent-affiliate)   [data-feat="affiliate"],
  body:not(.ent-reviews)     [data-feat="reviews"],
  body:not(.ent-feedback)    [data-feat="feedback"],
  body:not(.ent-socialProof) [data-feat="socialProof"],
  body:not(.ent-quickStock)  [data-feat="quickStock"],
  body:not(.ent-kitchenAdd)  [data-feat="kitchenAdd"],
  body:not(.ent-faq)         [data-feat="faq"] { display: none !important; }
  /* Inbox group holds ONLY Reviews + Feedback — hide it only when NEITHER is
     entitled (off items just dim inside it, so the group still has something). */
  body:not(.ent-reviews):not(.ent-feedback) .tabs-group[data-group="inbox"] { display: none !important; }
  /* 2 — entitled but off: dim the nav entry + append an "Off" pill. */
  body.ent-membership:not(.feat-membership)   .tabs [data-feat="membership"],
  body.ent-affiliate:not(.feat-affiliate)     .tabs [data-feat="affiliate"],
  body.ent-reviews:not(.feat-reviews)         .tabs [data-feat="reviews"],
  body.ent-feedback:not(.feat-feedback)       .tabs [data-feat="feedback"],
  body.ent-socialProof:not(.feat-socialProof) .tabs [data-feat="socialProof"],
  body.ent-quickStock:not(.feat-quickStock)   .tabs [data-feat="quickStock"],
  body.ent-faq:not(.feat-faq)                 .tabs [data-feat="faq"] { opacity: .5; }
  body.ent-membership:not(.feat-membership) .tabs [data-feat="membership"]::after,
  body.ent-affiliate:not(.feat-affiliate)   .tabs [data-feat="affiliate"]::after,
  body.ent-reviews:not(.feat-reviews)       .tabs [data-feat="reviews"]::after,
  body.ent-feedback:not(.feat-feedback)     .tabs [data-feat="feedback"]::after,
  body.ent-socialProof:not(.feat-socialProof) .tabs [data-feat="socialProof"]::after,
  body.ent-quickStock:not(.feat-quickStock)   .tabs [data-feat="quickStock"]::after,
  body.ent-faq:not(.feat-faq)                 .tabs [data-feat="faq"]::after {
    content: "OFF"; margin-left: 6px; font: 700 9px/1 var(--f-mono, monospace);
    letter-spacing: .06em; padding: 3px 5px; border-radius: 6px;
    display: inline-flex; align-items: center; align-self: center;
    background: var(--bg-elev-2); color: var(--text-3); border: 1px solid var(--line);
  }
  /* In a dropdown popover the row is full-width, so the pill is pinned to the
     row's right edge and vertically centred the same way the active brass-bar
     ::after is (absolute + top:50% + translateY) — flex centring wasn't holding
     here, so mirror the proven approach. Kept clear of the menu border. */
  body.ent-membership:not(.feat-membership) .tabs-popover [data-feat="membership"]::after,
  body.ent-affiliate:not(.feat-affiliate)   .tabs-popover [data-feat="affiliate"]::after,
  body.ent-reviews:not(.feat-reviews)       .tabs-popover [data-feat="reviews"]::after,
  body.ent-feedback:not(.feat-feedback)     .tabs-popover [data-feat="feedback"]::after,
  body.ent-socialProof:not(.feat-socialProof) .tabs-popover [data-feat="socialProof"]::after,
  body.ent-faq:not(.feat-faq)               .tabs-popover [data-feat="faq"]::after {
    position: absolute; right: 12px; top: 50%; transform: translateY(-50%);
    margin-left: 0;
    /* Reset the active brass-bar's width/height (.tabs-popover button.active::after)
       which otherwise leaks in on the current tab's OFF row, squashing the pill to
       3px and shoving its text left out of line with the other rows. Mirrors the
       leaderboard/voting/flash status pills below. */
    width: auto; height: auto;
  }
  /* Pill is out of flow, so reserve right padding on the off row to keep a long
     label from sliding under it. */
  body.ent-membership:not(.feat-membership) .tabs-popover [data-feat="membership"],
  body.ent-affiliate:not(.feat-affiliate)   .tabs-popover [data-feat="affiliate"],
  body.ent-reviews:not(.feat-reviews)       .tabs-popover [data-feat="reviews"],
  body.ent-feedback:not(.feat-feedback)     .tabs-popover [data-feat="feedback"],
  body.ent-socialProof:not(.feat-socialProof) .tabs-popover [data-feat="socialProof"],
  body.ent-faq:not(.feat-faq)               .tabs-popover [data-feat="faq"],
  body.lb-off .tabs-popover button[data-tab="leaderboard"],
  body.vot-open .tabs-popover button[data-tab="voting"] { padding-right: 56px; }
  /* Leaderboard isn't entitlement-gated (it has its own enable toggle on its
     page), but its nav row gets the same OFF pill when disabled — driven by
     body.lb-off. Dim only when it's NOT the current tab, since you sit on this
     row to turn it on. Fully self-contained so it also wins over the active
     brass-bar ::after when you're viewing the row while it's off. */
  body.lb-off .tabs-popover button[data-tab="leaderboard"]:not(.active) { opacity: .5; }
  body.lb-off .tabs-popover [data-tab="leaderboard"]::after {
    content: "Off"; width: auto; height: auto;
    position: absolute; right: 12px; top: 50%; transform: translateY(-50%);
    font: 700 9px/1 var(--f-mono, monospace); letter-spacing: .06em;
    padding: 3px 5px; border-radius: 6px;
    display: inline-flex; align-items: center;
    background: var(--bg-elev-2); color: var(--text-3); border: 1px solid var(--line);
  }
  /* Voting nav row gets an OPEN pill (same mechanic as Leaderboard's OFF, but a
     live/positive tint) whenever at least one poll is currently open — driven by
     body.vot-open. Just a status badge, so no dimming. */
  body.vot-open .tabs-popover [data-tab="voting"]::after {
    content: "Live"; width: auto; height: auto;
    position: absolute; right: 12px; top: 50%; transform: translateY(-50%);
    font: 700 9px/1 var(--f-mono, monospace); letter-spacing: .06em;
    padding: 3px 5px; border-radius: 6px;
    display: inline-flex; align-items: center;
    background: var(--brass-soft, var(--bg-elev-2)); color: var(--brass, var(--text-2));
    border: 1px solid var(--brass, var(--line));
  }
  /* Flash-sales nav row gets a LIVE pill (same mechanic as the Voting OPEN pill)
     whenever a flash sale is currently active — driven by body.fs-live. */
  body.fs-live .tabs-popover [data-tab="flash"]::after {
    content: "Live"; width: auto; height: auto;
    position: absolute; right: 12px; top: 50%; transform: translateY(-50%);
    font: 700 9px/1 var(--f-mono, monospace); letter-spacing: .06em;
    padding: 3px 5px; border-radius: 6px;
    display: inline-flex; align-items: center;
    background: var(--brass-soft, var(--bg-elev-2)); color: var(--brass, var(--text-2));
    border: 1px solid var(--brass, var(--line));
  }
  /* 3 — entitled but off: inside the feature's own page, hide the real config and
     show the enable card. .feat-bar (the switch) + .feat-off are spared. */
  .feat-off { display: none; }
  body.ent-membership:not(.feat-membership)   #tab-membership > :not(.feat-bar):not(.feat-off),
  body.ent-affiliate:not(.feat-affiliate)     #tab-affiliate  > :not(.feat-bar):not(.feat-off),
  body.ent-reviews:not(.feat-reviews)         #tab-reviews    > :not(.feat-bar):not(.feat-off),
  body.ent-feedback:not(.feat-feedback)       #tab-feedback   > :not(.feat-bar):not(.feat-off),
  body.ent-socialProof:not(.feat-socialProof) #tab-social     > :not(.feat-bar):not(.feat-off),
  body.ent-quickStock:not(.feat-quickStock)   #qs-home > :not(.feat-bar):not(.feat-off),
  body.ent-kitchenAdd:not(.feat-kitchenAdd)   #ka-home > :not(.feat-bar):not(.feat-off),
  body.ent-faq:not(.feat-faq)                 #tab-faq        > :not(.feat-bar):not(.feat-off) { display: none !important; }
  body.ent-membership:not(.feat-membership)   #tab-membership .feat-off,
  body.ent-affiliate:not(.feat-affiliate)     #tab-affiliate  .feat-off,
  body.ent-reviews:not(.feat-reviews)         #tab-reviews    .feat-off,
  body.ent-feedback:not(.feat-feedback)       #tab-feedback   .feat-off,
  body.ent-socialProof:not(.feat-socialProof) #tab-social     .feat-off,
  body.ent-quickStock:not(.feat-quickStock)   #qs-home .feat-off,
  body.ent-kitchenAdd:not(.feat-kitchenAdd)   #ka-home .feat-off,
  body.ent-faq:not(.feat-faq)                 #tab-faq        .feat-off { display: block; }
  /* feature on/off bar + enable card (mounted at the top of each feature page).
     Tacet-native: quiet card, ink primary action — amber/brass stay reserved
     for status, not a plain "turn this on" control. */
  .feat-bar { display: flex; align-items: center; justify-content: space-between;
    gap: 16px; padding: 14px 18px; margin-bottom: var(--s4);
    background: var(--td-surface); border: 1px solid var(--td-border); border-radius: 14px; }
  .feat-bar-t { font-weight: 500; font-size: 13.5px; color: var(--td-text); }
  .feat-bar-d { font-size: 11.5px; color: var(--td-text2); margin-top: 3px; max-width: 60ch; line-height: 1.5; }
  .feat-bar-state { font-size: 12px; color: var(--td-text2); min-width: 30px; }
  .feat-off { padding: 48px 24px; text-align: center;
    background: var(--td-raised); border: 1px solid var(--td-border); border-radius: 14px; }
  .feat-off h3 { font-size: 15px; font-weight: 500; color: var(--td-text); margin: 0 0 6px; }
  .feat-off p { font-size: 12.5px; color: var(--td-text2); margin: 0 auto 18px; max-width: 46ch; line-height: 1.6; }
  .feat-off .btn-primary { background: var(--td-text); border-color: var(--td-text); color: var(--td-paper); }
  .feat-off .btn-primary:hover { background: var(--td-dusk); border-color: var(--td-dusk); }
  .pa-card-img {
    aspect-ratio: 1 / 1;                 /* square crop is the default; the View popover offers 4:3 + full */
    background: var(--td-raised);
    overflow: hidden;
    position: relative;
    display: flex; align-items: center; justify-content: center;
    color: var(--td-text2);
  }
  .pa-card-img img { width: 100%; height: 100%; object-fit: cover; display: block; }
  /* Undesigned products have no photography yet — keep the empty state minimal
     and honest: a faint broken-image glyph on the neutral placeholder ground. */
  .pa-card-img-empty { display: flex; color: var(--td-text2); opacity: .5; }
  .pa-card-img-empty svg { display: block; }
  .pa-card-pills {
    position: absolute; top: 8px; left: 8px;
    display: flex; gap: 4px; flex-wrap: wrap;
  }
  .pa-card-body {
    padding: var(--s3) var(--s4) var(--s4);
    flex: 1;
    display: flex; flex-direction: column; gap: var(--s1);
    min-height: 64px;
  }
  /* Name clamps to two lines so long bilingual product names ("12pc Whole
     Wheat Wrap (XL - 10 inch)") stay readable without blowing out the card. */
  .pa-card-name {
    font-size: 14.5px; font-weight: 500; color: var(--td-text); line-height: 1.35;
    display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
  }
  .pa-card-sub  {
    font-size: 11px; font-weight: 600; letter-spacing: .06em; text-transform: uppercase;
    color: var(--td-text2); line-height: 1.3;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  }
  .pa-card-meta { font-family: var(--f-sans); font-size: 12px; color: var(--td-text2); letter-spacing: 0;
    font-variant-numeric: tabular-nums; text-transform: none; }

  /* Status badges on cards. They sit ON product photography, so each carries an
     opaque-enough ground to stay legible over any image in either theme — but
     the hues are Tacet roles, not the old Full Circle brand colours:
     DESIGNED = sage (healthy, has designer sections) · PLAIN = paper (neutral,
     no sections yet) · DRAFT = amber (waiting on you) · VARIANTS = ink (a
     fact, not a state) · SOLD OUT = brick (the one thing that's wrong). */
  .pa-pill {
    display: inline-flex; align-items: center; gap: 5px;
    box-sizing: border-box;
    height: 22px; padding: 0 9px; border-radius: 999px;
    font-family: var(--f-sans); font-size: 10px; font-weight: 500;
    letter-spacing: .1em; text-transform: uppercase; line-height: 1;
    backdrop-filter: blur(6px);
  }
  .pa-pill svg { display: block; flex-shrink: 0; }
  .pa-pill-designed { background: var(--td-sage); color: #16210F; }
  .pa-pill-plain    { background: rgba(251,249,244,.82); color: #58606E;
                      border: 1px solid rgba(17,26,43,.12); }
  .pa-pill-draft    { background: rgba(247,238,222,.92); color: #8A6425;
                      border: 1px solid rgba(201,149,74,.32); }
  .pa-pill-variants { background: rgba(17,26,43,.72); color: #FBF9F4; }
  .pa-pill-oos      { background: var(--td-brick); color: #fff; }

  /* List-row "designed?" mark — a bare sparkle glyph, no pill ground: lit sage
     when the product has designer sections, faint when it doesn't. Replaces
     the wider DESIGNED/PLAIN text pill on the compact single-line row only
     (the grid card keeps the opaque pill — it sits on top of photography). */
  .pa-status-icon { display: inline-flex; flex-shrink: 0; }
  .pa-status-icon svg { display: block; width: 15px; height: 15px; }
  .pa-status-icon.is-designed { color: var(--td-sage-ink); }
  .pa-status-icon.is-plain    { color: var(--td-border); }

  /* Empty state */
  .pa-empty {
    text-align: center; padding: var(--s7) var(--s4);
    color: var(--td-text2); font-size: 13.5px;
    background: var(--td-surface); border: 1px dashed var(--td-border);
    border-radius: 14px;
  }

  /* ── Products View tweaks (per-operator) — photo crop, status display,
     empty-state. Defaults (square / badge / icon) reproduce the base look. ── */
  #tab-products.pv-crop-portrait .pa-card-img { aspect-ratio: 3 / 4; }
  #tab-products.pv-crop-full     .pa-card-img { aspect-ratio: 5 / 7; }
  /* Status · Border — hide badges, tint the card edge by status (draft wins). */
  #tab-products.pv-status-border .pa-card-pills,
  #tab-products.pv-status-minimal .pa-card-pills { display: none; }
  #tab-products.pv-status-border .pa-card.is-designed { border-color: rgba(163,180,159,.55); }
  #tab-products.pv-status-border .pa-card.is-draft    { border-color: rgba(201,149,74,.55); }
  /* Status · Minimal — a small status dot in the image corner. */
  #tab-products.pv-status-minimal .pa-card-img::after {
    content: ''; position: absolute; top: 8px; left: 8px;
    width: 8px; height: 8px; border-radius: 50%;
    background: rgba(232,229,220,.7); box-shadow: 0 0 0 2px rgba(18,22,26,.25);
  }
  #tab-products.pv-status-minimal .pa-card.is-designed .pa-card-img::after { background: var(--success); }
  #tab-products.pv-status-minimal .pa-card.is-draft    .pa-card-img::after { background: var(--warn); }
  /* Empty-state shapes — hatched ground or plain (vs the default broken-image icon). */
  #tab-products.pv-empty-hatched .pa-card-img:has(.pa-card-img-empty) {
    background-image: repeating-linear-gradient(45deg, transparent, transparent 6px, var(--line) 6px, var(--line) 7px);
  }
  #tab-products.pv-empty-hatched .pa-card-img-empty,
  #tab-products.pv-empty-plain   .pa-card-img-empty { display: none; }

  /* ── Designer Sections editor — 3-pane: palette · canvas · inspector ── */
  /* Stretch the modal to near-fullscreen like the CMS editor */
  .pa-modal {
    width: min(calc(100vw - 32px), 1480px);
    max-width: calc(100vw - 32px);
    max-height: 94vh;
  }
  .pa-edit-body {
    display: grid;
    grid-template-columns: 220px 1fr 400px;
    /* Definite row height (not content-auto) so the three panes — and the
       phone canvas inside — fill the modal instead of growing to content and
       overflowing. Lets the phone stretch to fit at any zoom (no canvas scroll). */
    grid-template-rows: minmax(0, 1fr);
    gap: 0;
    padding: 0;
    overflow: hidden;
    min-height: 0;
    flex: 1;
  }
  @media (max-width: 1100px) {
    .pa-edit-body { grid-template-columns: 200px 1fr; }
    .pa-edit-inspector { display: none !important; }
  }
  @media (max-width: 640px) {
    .pa-edit-body { grid-template-columns: 1fr; }
    .pa-edit-palette { display: none !important; }
  }

  /* ── Left panel: block list (vertical palette) ── */
  .pa-edit-palette {
    background: var(--bg-pane);
    border-right: 1px solid var(--line-soft);
    display: flex; flex-direction: column;
    min-height: 0; overflow: hidden;
  }
  .pa-edit-palette-head {
    padding: var(--s3);
    border-bottom: 1px solid var(--line-soft);
    display: flex; flex-direction: column; gap: var(--s2);
    flex-shrink: 0;
  }
  .pa-edit-palette-title {
    font-size: 10px; font-weight: 600;
    letter-spacing: .12em; text-transform: uppercase;
    color: var(--text-3);
  }
  .pa-edit-palette-scroll {
    flex: 1; overflow-y: auto;
    padding: var(--s2) var(--s3) var(--s4);
    display: flex; flex-direction: column; gap: 4px;
  }

  /* ── Center: phone canvas (the big focal point like CMS) ── */
  .pa-edit-canvas {
    background: var(--bg-app);
    background-image:
      radial-gradient(ellipse at top, rgba(163, 180, 159, .04), transparent 55%),
      radial-gradient(ellipse at bottom, rgba(54, 83, 41, .05), transparent 55%);
    overflow-y: auto;
    overflow-x: auto;
    display: flex;
    /* stretch (cross axis) lets the phone fill the canvas height; capped by
       .pa-prev-frame max-height so it never grows past 580px on tall screens. */
    align-items: stretch;
    justify-content: center;
    padding: var(--s6) var(--s5);
    min-width: 0;
  }

  /* ── Right: inspector panel (chrome + form) ── */
  .pa-edit-inspector {
    background: var(--bg-pane);
    border-left: 1px solid var(--line-soft);
    display: flex; flex-direction: column;
    overflow: hidden;
    min-width: 0;
  }
  .pa-edit-inspector-scroll {
    /* overflow-x:hidden is explicit: a lone overflow-y:auto makes the browser
       compute overflow-x to `auto` too, so a few px of vertical overflow spawns
       a phantom horizontal scrollbar (which then eats height and keeps the
       vertical one alive). Pinning x hidden kills the loop. */
    flex: 1; overflow-y: auto; overflow-x: hidden;
  }
  /* Chrome fields inside inspector */
  .pa-inspector-chrome {
    padding: var(--s4) var(--s5);
    border-bottom: 1px solid var(--line-soft);
  }
  /* Colophon as compact collapsible inside inspector */
  .pa-colophon-details {
    margin-top: var(--s3);
  }
  .pa-colophon-details summary {
    font-size: 10px; font-weight: 600;
    letter-spacing: .12em; text-transform: uppercase;
    color: var(--text-3); cursor: pointer;
    padding: 6px 0 4px; user-select: none;
    list-style: none; display: flex; align-items: center; gap: 6px;
  }
  .pa-colophon-details summary::-webkit-details-marker { display: none; }
  .pa-colophon-details summary::before {
    content: '▶'; font-size: 8px; color: var(--text-4);
    transition: transform var(--t-fast);
  }
  .pa-colophon-details[open] summary::before { transform: rotate(90deg); }
  .pa-colophon-details .pa-chrome { margin-top: var(--s2); }
  /* Presets inside inspector */
  .pa-inspector-presets {
    padding: var(--s3) var(--s5) var(--s4);
    border-bottom: 1px solid var(--line-soft);
  }
  .pa-inspector-presets .pa-presets {
    margin: 0;
    padding: var(--s3) var(--s4);
  }
  .pa-inspector-presets .pa-presets-row {
    grid-template-columns: 1fr;
  }

  /* Card chrome (keywords + tagline) — compact strip, not a big card */
  .pa-chrome {
    background: var(--bg-elev-1);
    border: 1px solid var(--line-soft);
    border-radius: var(--r-lg);
    padding: var(--s3) var(--s4);
    margin-bottom: var(--s4);
  }
  .pa-chrome-title {
    font-size: 10px; font-weight: 600;
    letter-spacing: .12em; text-transform: uppercase;
    color: var(--text-3); margin-bottom: var(--s2);
  }
  .pa-chrome-grid {
    display: grid; grid-template-columns: 1fr 1fr; gap: var(--s2) var(--s3);
  }
  @media (max-width: 540px) { .pa-chrome-grid { grid-template-columns: 1fr; } }
  .pa-chrome-grid .field { margin: 0; }
  .pa-chrome-grid .field input { padding: 6px 9px; font-size: 12px; }
  .pa-chrome-grid label.stacked { font-size: 10px; margin-bottom: 4px; }

  /* Colophon IP character picker — small selectable thumbnails */
  .pa-colophon-grid {
    display: grid;
    grid-template-columns: repeat(5, 1fr);
    gap: var(--s2);
  }
  @media (max-width: 540px) { .pa-colophon-grid { grid-template-columns: repeat(4, 1fr); } }
  .pa-colophon-tile {
    aspect-ratio: 1;
    background: var(--bg-pane);
    border: 2px solid var(--line);
    border-radius: var(--r-md);
    cursor: pointer;
    overflow: hidden;
    position: relative;
    transition: border-color var(--t-fast), transform var(--t-fast);
    display: flex; align-items: center; justify-content: center;
  }
  .pa-colophon-tile:hover { border-color: var(--line-strong); transform: translateY(-1px); }
  .pa-colophon-tile.active {
    border-color: var(--accent);
    box-shadow: 0 0 0 2px var(--accent) inset;
  }
  .pa-colophon-tile img {
    width: 100%; height: 100%; object-fit: contain;
    /* IP files are b&w line-art on white. Blend the white onto our card
       background so they read on the dark theme without a glaring square. */
    mix-blend-mode: multiply;
    background: #fff;
  }
  :root[data-theme="light"] .pa-colophon-tile img { background: #fff; mix-blend-mode: normal; }
  .pa-colophon-tile-label {
    position: absolute; bottom: 0; left: 0; right: 0;
    background: rgba(0,0,0,0.55);
    color: #fff;
    font-size: 9px;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    padding: 2px 4px;
    text-align: center;
    line-height: 1.2;
  }

  /* Page presets — coherent block-recipe starters from the v1 design */
  .pa-presets {
    margin-bottom: var(--s4);
    padding: var(--s3) var(--s4);
    background: var(--bg-elev-1);
    border: 1px solid var(--line-soft);
    border-radius: var(--r-lg);
  }
  .pa-presets-head {
    display: flex; align-items: baseline; justify-content: space-between;
    gap: var(--s3); flex-wrap: wrap;
    margin-bottom: var(--s3);
  }
  .pa-presets-title {
    font-size: 11px; font-weight: 600;
    letter-spacing: .12em; text-transform: uppercase;
    color: var(--text-3);
  }
  .pa-presets-hint { margin: 0; }
  .pa-presets-row {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: var(--s3);
  }
  @media (max-width: 620px) { .pa-presets-row { grid-template-columns: 1fr; } }
  .pa-preset-card {
    background: var(--bg-pane);
    border: 1px solid var(--line);
    border-radius: var(--r-md);
    padding: var(--s3) var(--s4);
    cursor: pointer;
    transition: border-color var(--t-fast), background var(--t-fast);
    display: flex; flex-direction: column; gap: 6px;
    text-align: left;
    min-width: 0;
  }
  .pa-preset-card:hover {
    border-color: var(--accent);
    background: var(--bg-elev-2);
  }
  .pa-preset-card-label {
    font-size: 13px; font-weight: 600; color: var(--text-1); line-height: 1.3;
  }
  .pa-preset-card-desc {
    font-size: 11px; color: var(--text-3); line-height: 1.5;
  }

  /* Block rows — vertical palette tiles (replaces horizontal tab chips) */
  .pa-block-row {
    display: flex; align-items: center; gap: 8px;
    padding: 8px 10px;
    background: var(--bg-pane);
    border: 1px solid var(--line-soft);
    border-radius: var(--r-md);
    cursor: pointer;
    transition: border-color var(--t-fast), background var(--t-fast), transform var(--t-fast);
    user-select: none;
  }
  .pa-block-row:hover {
    background: var(--bg-elev-2);
    border-color: var(--line-strong);
    transform: translateY(-1px);
  }
  .pa-block-row.active {
    background: var(--bg-elev-2);
    border-color: var(--accent);
    box-shadow: 0 0 0 1px var(--accent) inset;
  }
  .pa-block-row .pbr-icon {
    width: 28px; height: 28px;
    background: rgba(163, 180, 159, .08);
    border-radius: var(--r-md);
    display: flex; align-items: center; justify-content: center;
    font-size: 14px; flex-shrink: 0;
  }
  .pa-block-row.active .pbr-icon { background: rgba(163, 180, 159, .16); }
  .pa-block-row .pbr-info { flex: 1; min-width: 0; }
  .pa-block-row .pbr-name {
    font-size: 12px; font-weight: 500; color: var(--text-1);
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  }
  .pa-block-row .pbr-sub {
    font-size: 10px; color: var(--text-3);
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
    margin-top: 1px;
  }
  .pa-block-row .pbr-num {
    width: 16px; height: 16px; border-radius: 50%;
    background: var(--bg-elev-2); color: var(--text-3);
    font-size: 9px; font-weight: 700;
    display: flex; align-items: center; justify-content: center;
    flex-shrink: 0;
  }
  .pa-block-row.active .pbr-num { background: var(--accent); color: var(--accent-ink); }

  /* Inspector-style block editor head — matches cms.html .inspector-head */
  .pa-inspector-head {
    padding: var(--s4) var(--s5) var(--s3);
    border-bottom: 1px solid var(--line-soft);
    display: flex; align-items: center; justify-content: space-between;
    gap: var(--s3); flex-shrink: 0;
  }
  .pa-inspector-head-left {
    display: flex; align-items: center; gap: 10px; min-width: 0;
  }
  .pa-inspector-icon {
    width: 26px; height: 26px;
    background: rgba(163, 180, 159, .12);
    border-radius: var(--r-sm);
    display: flex; align-items: center; justify-content: center;
    font-size: 14px; flex-shrink: 0;
  }
  .pa-inspector-title {
    font-size: 13px; font-weight: 600; color: var(--text-1);
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  }
  .pa-inspector-sub { font-size: 11px; color: var(--text-3); white-space: nowrap; }
  .pa-inspector-actions { display: flex; gap: 4px; flex-shrink: 0; }
  .pa-inspector-actions .btn { padding: 5px 9px; font-size: 11px; }
  .pa-inspector-actions .btn.danger {
    color: var(--danger); border-color: rgba(232, 85, 106, .35);
  }
  .pa-inspector-actions .btn.danger:hover { background: var(--danger-soft); }
  .pa-inspector-body { padding: var(--s4) var(--s5); }
  /* Compact the block-editor form so the common case (e.g. a 3-cell Spec
     Strip = 12 inputs) fits the inspector without a scrollbar. Scoped to the
     designer inspector, so the rest of the admin's forms keep their spacing. */
  .pa-inspector-body label.stacked { margin-bottom: 3px; }
  .pa-inspector-body .field input[type=text],
  .pa-inspector-body .field input[type=number],
  .pa-inspector-body .field input[type=url],
  .pa-inspector-body .field select,
  .pa-inspector-body .field textarea { padding: 6px 9px; }

  .pa-edit-grid-2 { display: grid; grid-template-columns: 1fr 1fr; gap: 8px 12px; }
  @media (max-width: 620px) { .pa-edit-grid-2 { grid-template-columns: 1fr; } }
  .pa-edit-grid-2 .field { margin-bottom: 0; }

  /* Empty state when no blocks exist */
  .pa-blocks-empty {
    text-align: center; padding: var(--s7) var(--s4);
    color: var(--text-3); font-size: 13px; line-height: 1.6;
  }

  /* ─── Live phone preview — centered in the canvas column ── */
  .pa-prev-frame {
    width: 320px;
    background: #2a2a30;
    border-radius: 38px;
    padding: 10px;
    box-shadow: 0 12px 40px rgba(0, 0, 0, 0.55), 0 2px 8px rgba(0,0,0,.3);
    flex-shrink: 0;
    /* Fill the canvas height (via align-items:stretch) but never exceed 580px,
       so the device always fits without scrolling. Column flex lets the screen
       child flex-fill the frame. */
    display: flex;
    flex-direction: column;
    max-height: 580px;
  }
  .pa-prev-screen {
    background: #F2E7DF;      /* oat */
    width: 100%;
    /* Flex-fill the frame instead of a fixed height: the frame is sized by the
       canvas (stretch, capped at 580px), so the screen fits at every zoom and
       the product page scrolls inside it — never the canvas. */
    flex: 1;
    min-height: 0;
    border-radius: 30px;
    overflow-y: auto;
    overflow-x: hidden;
    position: relative;
    color: #1a1a17;            /* ink */
    font-family: 'Aleo', 'PingFang SC', Georgia, serif;
  }
  .pa-prev-screen::-webkit-scrollbar { width: 4px; }
  .pa-prev-screen::-webkit-scrollbar-thumb { background: rgba(0,0,0,0.15); border-radius: 4px; }

  /* Hero image area */
  .pa-prev-hero {
    width: 100%; aspect-ratio: 1;
    background: #c4c4c4;
    background-size: cover; background-position: center;
    display: flex; align-items: center; justify-content: center;
    color: rgba(0, 0, 0, 0.35);
    font-size: 24px;
  }

  /* Header block — name / subtitle / tagline */
  .pa-prev-body { padding: 22px 22px 60px; }
  .pa-prev-name {
    font-family: 'Bear Serif', 'DF Yuan', serif;
    font-weight: 500;
    font-size: 30px;
    line-height: 1.05;
    color: #1a1a17;
  }
  .pa-prev-subtitle {
    margin-top: 6px;
    font-family: 'DF Yuan', 'PingFang SC', sans-serif;
    font-size: 12px;
    color: #6A6E2D;      /* olive */
  }
  .pa-prev-tagline {
    margin-top: 10px;
    font-family: 'Toni Grotesk', sans-serif;
    font-size: 13px;
    font-weight: 700;
    line-height: 1.25;
    color: #1a1a17;
  }

  /* Block: hero_intro */
  .pa-prev-block { margin-top: 22px; }
  .pa-prev-block:first-of-type { margin-top: 8px; }
  .pa-prev-hero-lead {
    font-family: 'Toni Grotesk', sans-serif;
    font-size: 10px;
    font-weight: 700;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    color: #6A6E2D;
    margin-bottom: 8px;
  }
  .pa-prev-hero-body {
    font-family: 'Aleo', serif;
    font-size: 14px;
    line-height: 1.55;
    color: #1a1a17;
    white-space: pre-line;
  }
  .pa-prev-hero-hl {
    color: #365329;
    font-weight: 700;
    background: linear-gradient(transparent 60%, rgba(163, 180, 159, 0.55) 60%);
    padding: 0 2px;
  }

  /* Block: stat_callout (bleeds to screen edges) */
  .pa-prev-stat {
    margin-left: -22px;
    margin-right: -22px;
    padding: 26px 22px;
    background: #CCD900;
    color: #1a1a17;
    text-align: center;
  }
  .pa-prev-stat-num {
    font-family: 'Bear Serif', serif;
    font-size: 56px;
    line-height: 1;
    font-weight: 500;
    letter-spacing: -0.01em;
  }
  .pa-prev-stat-claim {
    margin-top: 8px;
    font-family: 'Toni Grotesk', sans-serif;
    font-size: 13px;
    font-weight: 700;
    letter-spacing: 0.16em;
    text-transform: uppercase;
  }
  .pa-prev-stat-sub {
    margin-top: 8px;
    font-family: 'Aleo', serif;
    font-style: italic;
    font-size: 12px;
    line-height: 1.45;
  }

  /* Block: allergen_card */
  .pa-prev-allergen {
    border: 2px solid #1a1a17;
    background: #FFFCF7;
    padding: 14px 14px 16px;
    box-shadow: 3px 3px 0 #1a1a17;
  }
  .pa-prev-allergen-head {
    display: flex; align-items: center; gap: 6px;
    font-family: 'Toni Grotesk', sans-serif;
    font-size: 12px;
    font-weight: 700;
    letter-spacing: 0.14em;
    color: #1a1a17;
    text-transform: uppercase;
    margin-bottom: 10px;
    padding-bottom: 10px;
    border-bottom: 1px dashed rgba(26, 26, 23, 0.25);
  }
  .pa-prev-allergen-emoji { font-size: 16px; letter-spacing: 0; }
  .pa-prev-allergen-section { margin-top: 10px; }
  .pa-prev-allergen-section:first-of-type { margin-top: 0; }
  .pa-prev-allergen-label {
    font-family: 'Toni Grotesk', sans-serif;
    font-size: 10px;
    font-weight: 700;
    letter-spacing: 0.16em;
    color: #6A6E2D;
    margin-bottom: 6px;
    text-transform: uppercase;
  }
  .pa-prev-allergen-chips {
    display: flex; flex-wrap: wrap; gap: 4px;
  }
  .pa-prev-allergen-chip {
    display: inline-block;
    padding: 4px 10px;
    font-size: 11px;
    font-weight: 600;
    border-radius: 3px;
    line-height: 1.3;
  }
  .pa-prev-allergen-chip-strong { background: #E96174; color: #fff; }
  .pa-prev-allergen-chip-soft   { background: #F2E7DF; color: #1a1a17; border: 1px solid rgba(26,26,23,0.18); }

  /* Preview empty state */
  .pa-prev-empty {
    margin-top: 32px;
    padding: 20px;
    text-align: center;
    font-family: 'Aleo', serif;
    font-size: 12px;
    color: rgba(0, 0, 0, 0.45);
    border: 1.5px dashed rgba(0, 0, 0, 0.18);
    border-radius: 8px;
    line-height: 1.5;
  }

  /* ───────── v1 preview blocks · NEW types ───────── */
  /* Block container — 22px page margin matches mini-app .body padding. */
  .pa-prev-block { padding: 0; border-bottom: 1px solid #1a1a17; }
  .pa-prev-block:last-child { border-bottom: 0; }

  /* Shared section head used by Ingredients / How-to / Storage. */
  .pa-prev-section-head {
    display: flex; justify-content: space-between; align-items: flex-end;
    margin-bottom: 14px;
  }
  .pa-prev-section-h3 {
    font-family: 'Bear Serif', serif;
    font-size: 24px; line-height: 0.95;
    letter-spacing: -0.005em; color: #1a1a17;
  }
  .pa-prev-section-tag {
    font-family: 'Toni Grotesk', sans-serif;
    font-size: 10px; letter-spacing: 0.22em; text-transform: uppercase;
    color: rgba(26, 26, 23, 0.6);
  }

  /* Spec Strip */
  .pa-prev-spec {
    display: grid; grid-template-columns: repeat(3, 1fr);
  }
  .pa-prev-spec-cell {
    padding: 14px 14px 16px;
    border-right: 1px solid rgba(26, 26, 23, 0.12);
    display: flex; flex-direction: column; gap: 6px;
  }
  .pa-prev-spec-cell:last-child { border-right: 0; }
  .pa-prev-spec-k {
    font-family: 'Toni Grotesk', sans-serif;
    font-size: 9.5px; letter-spacing: 0.22em; text-transform: uppercase;
    color: rgba(26, 26, 23, 0.6);
  }
  .pa-prev-spec-v {
    font-family: 'Bear Serif', serif;
    font-size: 14px; line-height: 1.1;
  }

  /* Hero Intro override — the existing .pa-prev-hero rule is for the
     hero image placeholder; the Hero Intro block uses .pa-prev-hero too.
     Override only when it has block children. */
  .pa-prev-block.pa-prev-hero {
    padding: 30px 22px 28px;
    aspect-ratio: auto;
    background: transparent;
    display: block;
    color: #1a1a17;
    font-size: inherit;
  }

  /* Why */
  .pa-prev-why { padding: 4px 22px 18px; }
  .pa-prev-why-head {
    display: flex; justify-content: space-between; align-items: baseline;
    padding: 18px 0 12px;
  }
  .pa-prev-why-l {
    font-family: 'Bear Serif', serif;
    font-size: 11px; letter-spacing: 0.22em; text-transform: uppercase;
    color: #6A6E2D;
  }
  .pa-prev-why-r {
    font-family: 'Toni Grotesk', sans-serif;
    font-size: 11px; letter-spacing: 0.16em; text-transform: uppercase;
    color: rgba(26, 26, 23, 0.6);
  }
  .pa-prev-why-row {
    display: grid; grid-template-columns: 32px 1fr; gap: 12px;
    align-items: baseline;
    padding: 12px 0; border-top: 1px solid rgba(26, 26, 23, 0.12);
  }
  .pa-prev-why-row.first { border-top: 1px solid #1a1a17; }
  .pa-prev-why-n {
    font-family: 'League Gothic', sans-serif;
    font-size: 28px; line-height: 0.85; color: #1a1a17;
  }
  .pa-prev-why-claim {
    font-family: 'Toni Grotesk', sans-serif;
    font-size: 13px; line-height: 1.35;
  }

  /* Ingredients */
  .pa-prev-ing { padding: 22px 22px 22px; }
  .pa-prev-ing-chips { display: flex; flex-wrap: wrap; gap: 6px; }
  .pa-prev-chip {
    display: inline-flex; align-items: baseline; gap: 5px;
    padding: 7px 11px 6px;
    border: 1px solid #1a1a17; border-radius: 999px;
    background: transparent;
    font-family: 'Toni Grotesk', sans-serif;
    font-size: 11px; line-height: 1; color: #1a1a17;
  }
  .pa-prev-chip.lead {
    background: #1a1a17; color: #F2E7DF;
    font-family: 'Bear Serif', serif;
    font-size: 14px; padding: 9px 13px 8px;
  }
  .pa-prev-chip-pct {
    font-family: 'League Gothic', sans-serif;
    font-size: 12px; color: #E96174;
  }
  .pa-prev-chip.lead .pa-prev-chip-pct { color: #CCD900; }
  .pa-prev-ing-line {
    font-family: 'Bear Serif', serif;
    font-size: 18px; line-height: 1.25;
    letter-spacing: -0.005em; color: #1a1a17;
  }
  .pa-prev-ing-line-pct {
    font-family: 'League Gothic', sans-serif;
    color: #E96174; font-size: 0.85em;
  }
  .pa-prev-ing-line-sep {
    display: inline-block; width: 6px; height: 6px;
    background: #CCD900; border-radius: 50%;
    margin: 0 0.35em 0.12em; vertical-align: middle;
  }

  /* Allergen — extends the existing .pa-prev-allergen with v1 internals. */
  .pa-prev-allergen {
    margin: 22px 22px; padding: 14px 14px 12px;
    border: 1px solid #1a1a17; background: #FAF1E8;
    border-radius: 0; box-shadow: none;
  }
  .pa-prev-allergen-top {
    display: flex; justify-content: space-between; align-items: center;
    padding-bottom: 10px; margin-bottom: 10px;
    border-bottom: 1px solid #1a1a17;
  }
  .pa-prev-allergen-ttl {
    display: inline-flex; align-items: center; gap: 6px;
    font-family: 'Bear Serif', serif; font-size: 15px;
  }
  .pa-prev-allergen-warn {
    display: inline-flex; align-items: center; justify-content: center;
    width: 17px; height: 17px;
    background: #E96174; color: #F2E7DF; border-radius: 3px;
    font-family: 'Toni Grotesk', sans-serif; font-size: 11px; font-weight: 700;
  }
  .pa-prev-allergen-meta {
    font-family: 'Toni Grotesk', sans-serif;
    font-size: 8.5px; letter-spacing: 0.22em; text-transform: uppercase;
    color: rgba(26, 26, 23, 0.6);
  }
  .pa-prev-allergen-row {
    display: flex; align-items: baseline; gap: 8px;
    padding: 6px 0;
  }
  .pa-prev-allergen-row + .pa-prev-allergen-row {
    border-top: 1px dashed rgba(26, 26, 23, 0.3);
  }
  .pa-prev-allergen-lbl {
    font-family: 'Toni Grotesk', sans-serif;
    font-size: 9px; letter-spacing: 0.18em; text-transform: uppercase;
    color: rgba(26, 26, 23, 0.6);
    flex: 0 0 76px; line-height: 1.4;
  }
  .pa-prev-allergen-chips {
    display: flex; flex-wrap: wrap; gap: 5px; flex: 1;
  }
  .pa-prev-allergen-chip {
    display: inline-block;
    padding: 4px 9px 3px; border-radius: 999px;
    font-family: 'Toni Grotesk', sans-serif;
    font-size: 10px; line-height: 1;
  }
  .pa-prev-allergen-chip.strong { background: #E96174; color: #fff; font-weight: 700; }
  .pa-prev-allergen-chip.soft   { background: #F2CA6C; color: #1a1a17; }
  .pa-prev-allergen-empty {
    font-family: 'Aleo', serif; font-style: italic;
    font-size: 11px; color: rgba(26, 26, 23, 0.6);
  }

  /* How To Use */
  .pa-prev-how { padding: 22px 22px 22px; }
  .pa-prev-how-grid {
    display: grid;
    gap: 1px;
    background: rgba(26, 26, 23, 0.12);
    border: 1px solid rgba(26, 26, 23, 0.12);
  }
  .pa-prev-how-tile {
    background: #F2E7DF; padding: 14px 6px 12px;
    text-align: center;
    display: flex; flex-direction: column; gap: 5px; align-items: center;
    min-height: 64px; justify-content: center;
  }
  .pa-prev-how-ic { font-size: 20px; line-height: 1; }
  .pa-prev-how-lbl {
    font-family: 'Toni Grotesk', sans-serif;
    font-size: 10px; letter-spacing: 0.14em; text-transform: uppercase;
    color: #1a1a17;
  }
  .pa-prev-how-sub {
    font-family: 'Aleo', serif; font-style: italic;
    font-size: 10px; color: rgba(26, 26, 23, 0.6);
  }

  /* Storage */
  .pa-prev-storage { padding: 22px 22px 22px; }
  .pa-prev-storage-pair {
    display: grid; grid-template-columns: 1fr 1fr; gap: 9px;
  }
  .pa-prev-storage-card {
    border: 1px solid #1a1a17; padding: 16px 14px 15px;
    position: relative;
    display: flex; flex-direction: column; gap: 8px;
    min-height: 128px;
  }
  .pa-prev-storage-card.alt { background: #1a1a17; color: #F2E7DF; }
  .pa-prev-storage-ic {
    position: absolute; top: 11px; right: 11px;
    font-size: 16px; line-height: 1;
  }
  .pa-prev-storage-head {
    font-family: 'Toni Grotesk', sans-serif;
    font-size: 9.5px; letter-spacing: 0.22em; text-transform: uppercase;
  }
  .pa-prev-storage-num {
    font-family: 'League Gothic', sans-serif;
    font-size: 44px; line-height: 0.95; margin-top: auto;
  }
  .pa-prev-storage-unit {
    font-family: 'Toni Grotesk', sans-serif;
    font-size: 10px; letter-spacing: 0.16em; line-height: 1.5; text-transform: uppercase;
  }
  .pa-prev-storage-note {
    margin-top: 11px;
    font-family: 'Aleo', serif; font-style: italic;
    font-size: 11px; color: rgba(26, 26, 23, 0.6);
    text-align: right;
  }

  /* Notes Callout */
  .pa-prev-notes { padding: 16px 22px 18px; }
  .pa-prev-notes-p {
    font-family: 'Aleo', serif; font-style: italic;
    font-size: 11px; line-height: 1.55;
    color: rgba(26, 26, 23, 0.6);
    position: relative; padding-left: 14px;
  }
  .pa-prev-notes-p::before {
    content: "✱"; position: absolute; left: 0; top: 0;
    color: #6A6E2D; font-style: normal;
  }

  /* Pull Quote */
  .pa-prev-pullquote {
    padding: 18px 22px 20px;
    background: #E8D9CB;
  }
  .pa-prev-pullquote-p {
    font-family: 'Aleo', serif; font-style: italic;
    font-size: 13px; line-height: 1.5;
  }
  .pa-prev-pullquote-who {
    margin-top: 8px;
    font-family: 'Toni Grotesk', sans-serif;
    font-size: 9px; letter-spacing: 0.22em; text-transform: uppercase;
    color: rgba(26, 26, 23, 0.6);
  }

  /* Colophon (preview) — page sign-off mirroring product.wxss .colophon */
  .pa-prev-colophon {
    padding: 22px 22px 18px;
    background: #F2E7DF;
    text-align: center;
    display: flex; flex-direction: column; align-items: center;
    gap: 10px;
    position: relative;
    border-top: 1px solid #1a1a17;
  }
  .pa-prev-colophon-img {
    width: 72px; height: 72px;
    object-fit: contain;
    mix-blend-mode: multiply;
  }
  .pa-prev-colophon-tagline {
    font-family: 'Bear Serif', serif;
    font-size: 16px; line-height: 1;
    letter-spacing: -0.005em;
    color: #1a1a17;
  }
  .pa-prev-colophon-meta {
    display: flex; align-items: center; gap: 8px;
    font-family: 'Toni Grotesk', sans-serif;
    font-size: 8px;
    letter-spacing: 0.32em; text-transform: uppercase;
    color: rgba(26, 26, 23, 0.6);
  }
  .pa-prev-colophon-rule {
    width: 14px; height: 1px;
    background: rgba(26, 26, 23, 0.3);
  }
  .pa-prev-colophon-auto {
    position: absolute; top: 6px; right: 8px;
    font-family: 'Toni Grotesk', sans-serif;
    font-size: 8px; letter-spacing: 0.18em; text-transform: uppercase;
    color: rgba(26, 26, 23, 0.4);
    background: rgba(26, 26, 23, 0.06);
    padding: 2px 6px; border-radius: 3px;
  }

  /* ═══════════════════════════════════════════════════════════════════
     BUTTONS
     ═══════════════════════════════════════════════════════════════════ */
  .btn {
    display: inline-flex; align-items: center; gap: 6px;
    font-size: 13px; font-weight: 500;
    padding: 7px 12px;
    border: 1px solid var(--line);
    background: var(--bg-elev-1);
    color: var(--text-1);
    border-radius: var(--r-md);
    cursor: pointer;
    transition: background var(--t-fast) ease, border-color var(--t-fast) ease, color var(--t-fast) ease, transform var(--t-fast) ease;
    user-select: none;
  }

  /* Bulk attach tracking is an opt-in tool — hidden from the Orders toolbar
     until the operator switches it on in Settings → Delivery. The body gains
     .ord-bulk-tracking from loadConfig() / the delivery toggle. */
  .ord-bulk-trk-btn { display: none; }
  body.ord-bulk-tracking .ord-bulk-trk-btn { display: inline-flex; }
  .btn:hover { background: var(--bg-elev-2); border-color: var(--line-strong); }
  .btn:active { transform: translateY(1px); }
  .btn:disabled { opacity: .4; cursor: not-allowed; }
  .btn svg { width: 14px; height: 14px; flex-shrink: 0; }
  /* Primary action = Aged Brass (the single brass CTA per surface —
     Attach tracking, Save, Apply…). Tea is reserved for active/live. */
  .btn-primary {
    background: var(--brass); color: var(--accent-ink);
    border-color: var(--brass); font-weight: 600;
  }
  .btn-primary:hover { background: var(--brass-hi); border-color: var(--brass-hi); }
  .btn-ghost {
    background: transparent; border-color: transparent; color: var(--text-2);
  }
  .btn-ghost:hover { background: var(--bg-elev-1); color: var(--text-1); border-color: transparent; }
  .btn-danger {
    background: transparent; color: var(--danger); border-color: rgba(232, 85, 106, .25);
  }
  .btn-danger:hover { background: var(--danger-soft); border-color: var(--danger); }
  .btn-sm { padding: 4px 10px; font-size: 12px; }
  .btn-icon { padding: 7px 8px; }

  /* "Paid how?" off-app tender picker (manual Mark-paid). In-app modal so it
     reads as part of the backend, not a browser prompt. */
  .pm-pick-bg {
    position: fixed; inset: 0; z-index: 1200;
    background: var(--bg-overlay); backdrop-filter: blur(2px);
    display: flex; align-items: center; justify-content: center; padding: 24px;
    animation: pmFade var(--t-fast) ease;
  }
  @keyframes pmFade { from { opacity: 0; } to { opacity: 1; } }
  .pm-pick {
    width: 420px; max-width: 100%;
    background: var(--bg-pane); border: 1px solid var(--line-strong);
    border-radius: var(--r-lg); box-shadow: 0 24px 60px rgba(0,0,0,.45);
    overflow: hidden;
  }
  .pm-pick-head { padding: 18px 20px 14px; border-bottom: 1px solid var(--line-soft); }
  .pm-pick-title { font-size: 14px; font-weight: 600; color: var(--text-1); }
  .pm-pick-sub { font-size: 11px; color: var(--text-3); margin-top: 4px; }
  .pm-pick-list { padding: 12px; display: flex; flex-direction: column; gap: 6px; }
  .pm-pick-opt {
    display: flex; align-items: center; gap: 12px; width: 100%; text-align: left;
    padding: 11px 12px; font-size: 13px; font-weight: 500; color: var(--text-1);
    background: var(--bg-elev-1); border: 1px solid var(--line);
    border-radius: var(--r-md); cursor: pointer;
    transition: background var(--t-fast) ease, border-color var(--t-fast) ease;
  }
  .pm-pick-opt:hover, .pm-pick-opt:focus-visible {
    background: var(--bg-elev-2); border-color: var(--brass); outline: none;
  }
  .pm-pick-num {
    flex-shrink: 0; width: 22px; height: 22px; border-radius: var(--r-sm);
    display: inline-flex; align-items: center; justify-content: center;
    font-family: var(--f-mono, monospace); font-size: 11px; color: var(--brass);
    background: rgba(201,149,74,.12); border: 1px solid rgba(201,149,74,.30);
  }
  .pm-pick-foot {
    padding: 12px 20px 18px; display: flex; justify-content: flex-end;
    border-top: 1px solid var(--line-soft);
  }

  /* ═══════════════════════════════════════════════════════════════════
     INPUTS
     ═══════════════════════════════════════════════════════════════════ */
  input[type=text], input[type=number], input[type=url], input[type=date], select, textarea {
    font: 13px var(--f-sans);
    padding: 8px 10px;
    background: var(--bg-input);
    border: 1px solid var(--line);
    color: var(--text-1);
    border-radius: var(--r-md);
    outline: none;
    min-width: 0;
    transition: border-color var(--t-fast) ease, box-shadow var(--t-fast) ease;
  }
  input[type=date], input[type=datetime-local], input[type=time] {
    color-scheme: dark; cursor: pointer;
    font-family: var(--f-mono);
    font-variant-numeric: tabular-nums;
    letter-spacing: 0.02em;
  }
  /* Native datetime inputs render an `mm/dd/yyyy, --:-- --` mask when
     empty — Chrome paints this in the foreground colour which clashes with
     the dark theme. Dim the empty fields + separators so the mask reads
     like a placeholder; filled values stay full-contrast. */
  input[type=date]::-webkit-datetime-edit-text,
  input[type=datetime-local]::-webkit-datetime-edit-text,
  input[type=time]::-webkit-datetime-edit-text {
    color: var(--text-3);
    padding: 0 3px;
  }
  input[type=date]:invalid::-webkit-datetime-edit,
  input[type=datetime-local]:invalid::-webkit-datetime-edit,
  input[type=time]:invalid::-webkit-datetime-edit,
  input[type=date]:not(:focus):placeholder-shown::-webkit-datetime-edit,
  input[type=datetime-local]:not(:focus):placeholder-shown::-webkit-datetime-edit,
  input[type=time]:not(:focus):placeholder-shown::-webkit-datetime-edit {
    color: var(--text-3);
    opacity: 0.65;
  }
  input[type=date]::-webkit-datetime-edit-month-field:focus,
  input[type=date]::-webkit-datetime-edit-day-field:focus,
  input[type=date]::-webkit-datetime-edit-year-field:focus,
  input[type=datetime-local]::-webkit-datetime-edit-month-field:focus,
  input[type=datetime-local]::-webkit-datetime-edit-day-field:focus,
  input[type=datetime-local]::-webkit-datetime-edit-year-field:focus,
  input[type=datetime-local]::-webkit-datetime-edit-hour-field:focus,
  input[type=datetime-local]::-webkit-datetime-edit-minute-field:focus,
  input[type=datetime-local]::-webkit-datetime-edit-ampm-field:focus {
    background: rgba(163, 180, 159, 0.20);
    color: var(--text-1);
    border-radius: 3px;
    outline: none;
  }
  input[type=date]::-webkit-calendar-picker-indicator,
  input[type=datetime-local]::-webkit-calendar-picker-indicator,
  input[type=time]::-webkit-calendar-picker-indicator {
    filter: invert(0.7);
    cursor: pointer;
    padding: 2px;
    opacity: 0.55;
    transition: opacity var(--t-fast) ease;
  }
  input[type=date]:hover::-webkit-calendar-picker-indicator,
  input[type=datetime-local]:hover::-webkit-calendar-picker-indicator,
  input[type=time]:hover::-webkit-calendar-picker-indicator {
    opacity: 1;
  }
  /* Focus for every field in the admin. Tacet: "focus = border turns Dusk, no
     glow rings" (design-system, Inputs) and focus rings are Dusk (rulebook
     §1.8 rule 3). This used to paint a tea-green border plus a 3px --ring glow,
     both leftovers from Full Circle. Because it is the rule everything
     inherits, a component that re-pointed only its border-color to Dusk still
     wore the green halo — it looked migrated and was not.
     box-shadow:none is stated, not omitted, so the reset survives if a legacy
     rule above ever puts a glow back. --ring itself is untouched: switches,
     ::selection and the order-row outline still use it, and those are not
     input focus rings. */
  input:focus, select:focus, textarea:focus {
    border-color: var(--td-dusk);
    box-shadow: none;
  }
  input:hover:not(:focus), select:hover:not(:focus), textarea:hover:not(:focus) {
    border-color: var(--line-strong);
  }
  textarea { min-height: 64px; resize: vertical; font-family: inherit; width: 100%; }
  select {
    appearance: none;
    background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%237d7a72' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'><polyline points='6 9 12 15 18 9'></polyline></svg>");
    background-repeat: no-repeat;
    background-position: right 10px center;
    padding-right: 32px;
    cursor: pointer;
    color-scheme: dark;
  }
  /* Open-dropdown popup. Chrome/Safari honour these; Firefox uses OS chrome. */
  option {
    background: var(--bg-input);
    color: var(--text-1);
  }
  label.inline {
    font-size: 11px; font-weight: 600;
    letter-spacing: .06em; text-transform: uppercase;
    color: var(--text-3);
  }
  label.stacked {
    display: block;
    font-size: 11px; font-weight: 600;
    letter-spacing: .06em; text-transform: uppercase;
    color: var(--text-3);
    margin-bottom: 6px;
  }
  .field { margin-bottom: var(--s4); }
  .helper { font-size: 11px; color: var(--text-4); margin-top: 6px; line-height: 1.5; }

  /* ═══════════════════════════════════════════════════════════════════
     TABLES
     ═══════════════════════════════════════════════════════════════════ */
  .table-wrap {
    background: var(--bg-pane);
    border: 1px solid var(--line);
    border-radius: var(--r-lg);
    overflow: hidden;
    /* Same two-layer lift as .fc-tile / .fc-kpi so tables read as elevated
       surfaces too, not flat rectangles — one elevation across the backend. */
    box-shadow: 0 1px 2px rgba(18,22,26,.05), 0 8px 22px rgba(18,22,26,.07);
  }
  table { width: 100%; border-collapse: collapse; font-size: 13px; }
  th, td {
    padding: 10px 14px;
    text-align: left;
    border-bottom: 1px solid var(--line-soft);
    vertical-align: middle;
  }
  th {
    font-size: 11px; font-weight: 600;
    letter-spacing: .08em; text-transform: uppercase;
    color: var(--text-3);
    background: var(--bg-app);
    border-bottom-color: var(--line);
  }
  tbody tr { transition: background var(--t-fast) ease; }
  tbody tr:hover { background: var(--bg-elev-1); }
  /* Clickable order rows in the Orders tab — whole row opens the drawer. */
  tbody tr.order-row { cursor: pointer; }
  tbody tr.order-row:focus-visible {
    outline: 2px solid var(--ring, rgba(163, 180, 159, .55));
    outline-offset: -2px;
  }
  /* New-order arrival flash — fires once per row when an unseen order id
     appears via the auto-refresh poller. Soft lime wash that fades out. */
  @keyframes order-row-flash {
    0%   { background: rgba(163, 180, 159, .28); box-shadow: inset 3px 0 0 var(--accent); }
    60%  { background: rgba(163, 180, 159, .14); box-shadow: inset 3px 0 0 var(--accent); }
    100% { background: transparent;              box-shadow: inset 0   0 0 transparent; }
  }
  tbody tr.order-row.is-fresh { animation: order-row-flash 6s ease-out 1; }
  /* One-shot highlight for a row a deep-link jumped to (e.g. an activity-feed
     event opening its Feedback report). Same lime wash, shorter + self-clearing. */
  @keyframes row-flash {
    0%   { background: rgba(163, 180, 159, .28); box-shadow: inset 3px 0 0 var(--accent); }
    100% { background: transparent;              box-shadow: inset 0   0 0 transparent; }
  }
  tbody tr.row-flash { animation: row-flash 1.6s ease-out 1; }
  /* Unread order — a Tea "NEW" chip + bold order#/customer + a soft Tea wash
     over the whole row, persisting from arrival until any operator opens the
     detail (server seen_at). The wash is an inset box-shadow so it layers above
     whatever background the row-rhythm tweak (striped / cards) paints, without a
     specificity fight. Complements the one-shot 6s arrival flash above. */
  .ord-no { font-family: var(--f-mono); font-weight: 400; font-size: 12px; }
  tr.order-row.is-unread td { box-shadow: inset 0 0 0 999px rgba(163, 180, 159, .12); }
  tr.order-row.is-unread .ord-no,
  tr.order-row.is-unread .ord-cust { font-weight: 700; color: var(--text-1); }
  .new-chip {
    display: inline-block; vertical-align: middle; margin-right: 8px;
    padding: 2px 7px; border-radius: 3px;
    background: var(--success); color: #E8E5DC;   /* Tacet sage — the "live/new" chip */
    font-family: var(--f-mono); font-size: 9px; font-weight: 700;
    letter-spacing: .16em; text-transform: uppercase; line-height: 1.5; white-space: nowrap;
  }
  /* Status cell wraps status + refund pills as a small flex column so
     a narrow column shows the refund pill directly below the status pill
     (no awkward floating indent), while a wide column keeps them inline. */
  td.status-cell { vertical-align: middle; }
  td.status-cell .stack {
    display: inline-flex; flex-wrap: wrap; gap: 4px; align-items: center;
  }
  /* Quiet sub-line under the status pill — shared by the fapiao line and the
     stock-pending flag. Brass = still owed/pending, muted = resolved. Text
     rather than a second pill so it never competes with the order's own
     status. Used both inside td.status-cell (list rows) and inline in the
     drawer's .title-row (stock-pending there), so it stays unscoped. */
  .ord-note-line {
    display: flex; align-items: center; gap: 5px; margin-top: 5px;
    font-size: 11px; letter-spacing: .02em; color: var(--text-3); white-space: nowrap;
  }
  .ord-note-line svg { width: 11px; height: 11px; flex: none; }
  .ord-note-line.is-owed { color: var(--brass); font-weight: 600; }
  /* Inline among a row of pills — no top offset needed there, the row's
     align-items:center already handles vertical placement. The order drawer
     shows these on its .od-marks line now (the pinned header carries the ONE
     status pill and nothing else); the list rows still stack them in
     td.status-cell, and the readonly mirror still has a pill title-row. */
  .title-row .ord-note-line { margin-top: 0; }
  /* Refund-pending badge — shown alongside the status pill on any row
     where refund_status is in the open pipeline. Makes the queue visible
     from every filter view, not just the dedicated chip. */
  .pill.refund-pending {
    display: inline-flex; align-items: center; gap: 4px;
    padding: 2px 8px; border-radius: 999px;
    font-size: 10px; font-weight: 700; letter-spacing: .03em;
    background: rgba(201, 149, 74, .12);
    color: var(--brass);
    border: 1px solid rgba(201, 149, 74, .30);
    white-space: nowrap;
  }
  /* Drawer-prominent refund banner — sits between the order's status pill
     and the kicker grid so processing staff can't miss it. Color shifts
     with state: red for open / attention-needed (pending, reviewing),
     green for approved (good outcome, just needs payout recorded).
     Earlier the banner stayed red even after approval, which read as
     alarming for a state that's actually positive. */
  .drawer-refund-banner {
    display: flex; align-items: center; gap: 10px;
    padding: 10px 14px;
    margin: 10px 0 4px;
    border-radius: 10px;
    font-size: 13px; font-weight: 600;
    /* default = pending/reviewing (red) */
    background: rgba(232, 85, 106, .10);
    border: 1px solid rgba(232, 85, 106, .35);
    color: var(--st-refunded);
  }
  .drawer-refund-banner[data-state="approved"] {
    background: rgba(95, 194, 142, .12);
    border-color: rgba(95, 194, 142, .40);
    color: #2f8a5a;
  }
  :root[data-theme="light"] .drawer-refund-banner[data-state="approved"] {
    color: #1f6c44;   /* a touch darker on cream for legibility */
  }
  .drawer-refund-banner .arrow { font-size: 16px; line-height: 1; }
  .drawer-refund-banner .label { display: flex; flex-direction: column; gap: 2px; }
  .drawer-refund-banner .sub { font-size: 11px; font-weight: 500; color: var(--text-2); }
  /* (Old orders-live-pill removed — replaced by the dot next to Settings.) */

  /* Payment-brand chip — small SVG logo + label, used in the order
     drawer Payment tile and top-up history rows. Two flavors:
     default = inline-tight, `.lg` = drawer hero with a bit more
     breathing room. Keeps the same visual language as status pills. */
  /* An unrecognised payment method, from paymentBrandHTML's fallback. Quiet
     and unweighted ON PURPOSE — every real method above it is bold, so an
     unhandled key must not borrow that authority and pass for a brand. It
     should read as "not handled yet", not as a payment type. */
  .pay-unknown {
    font-weight: 400;
    color: var(--text-2);
  }
  .pay-brand {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    font-weight: 700;
    line-height: 1.2;
    vertical-align: middle;
  }
  .pay-brand svg { display: block; flex-shrink: 0; border-radius: 4px; }
  .pay-brand .lbl { white-space: nowrap; }
  .pay-brand.lg { font-size: 14px; gap: 8px; }
  /* Monochrome: label inherits the surrounding text colour (the mark is drawn
     in currentColor too) — payment brand is machine data, not a status colour. */
  .pay-brand-wechat .lbl,
  .pay-brand-alipay .lbl { color: inherit; }
  tbody tr:last-child td { border-bottom: 0; }
  td.muted { color: var(--text-3); }
  td.numeric { font-variant-numeric: tabular-nums; }

  /* Delivery zones — unified table, sub-tab pills, edit drawer. */
  .dz-toolbar { align-items: center; gap: 12px; flex-wrap: wrap; margin-bottom: var(--s3); }
  .dz-subtabs {
    display: inline-flex; padding: 3px; gap: 2px;
    background: var(--bg-elev-1); border: 1px solid var(--line);
    border-radius: 999px;
  }
  .dz-subtab {
    border: 0; background: transparent;
    padding: 6px 14px; border-radius: 999px;
    font-size: 12px; font-weight: 600; letter-spacing: .02em;
    color: var(--text-3); cursor: pointer;
    transition: background var(--t-fast) ease, color var(--t-fast) ease;
  }
  .dz-subtab:hover { color: var(--text-2); }
  .dz-subtab.active {
    background: var(--bg-elev-2); color: var(--text-1);
    box-shadow: 0 1px 0 var(--line-strong) inset;
  }
  #dz-search { min-width: 220px; flex: 1; }
  #dz-table { width: 100%; }
  #dz-table th {
    white-space: nowrap; padding: 10px 12px;
    font-size: 11px; text-transform: uppercase; letter-spacing: .04em;
    color: var(--text-3); font-weight: 600;
  }
  #dz-table td {
    padding: 12px; vertical-align: middle;
    border-top: 1px solid var(--line);
    font-size: 13px; font-variant-numeric: tabular-nums;
  }
  #dz-table tbody tr.dz-row { cursor: pointer; transition: background var(--t-fast) ease; }
  #dz-table tbody tr.dz-row:hover { background: var(--bg-elev-1); }
  .dz-zone-cell { display: flex; align-items: center; gap: 10px; }
  .dz-zone-text { display: flex; flex-direction: column; line-height: 1.25; }
  .dz-zone-text .sub { font-size: 11px; color: var(--text-3); }
  .dz-type-chip {
    display: inline-flex; align-items: center;
    padding: 2px 8px; border-radius: 999px;
    font-size: 10px; font-weight: 700; letter-spacing: .06em;
    border: 1px solid var(--line);
    background: var(--bg-elev-1); color: var(--text-3);
  }
  .dz-type-chip.gz   { background: rgba(163, 180, 159, .12); color: var(--accent-hi);  border-color: rgba(163, 180, 159, .35); }
  /* CITY = neutral ink chip (was an off-palette light-blue). GZ stays Tea so
     the two zone types still read apart, on-palette. */
  .dz-type-chip.city { background: var(--bg-elev-1); color: var(--text-2); border-color: var(--line); }
  /* Province-wide (全省/全市) — earthy tan, distinct from GZ green + CITY ink */
  .dz-type-chip.prov { background: rgba(176, 137, 104, .14); color: #8a6a44; border-color: rgba(176, 137, 104, .4); }
  .dz-extras { color: var(--text-3); font-size: 11px; }
  .dz-wprice { color: var(--brass); font-variant-numeric: tabular-nums; white-space: nowrap; }
  .dz-sd-yes { color: var(--st-delivered); font-weight: 600; }
  .dz-sd-no  { color: var(--text-4); }

  /* Province grouping header — a sticky-feeling section divider, not a clickable row */
  .dz-group-row > td {
    background: var(--bg-elev-1);
    border-top: 1px solid var(--line);
    padding: 8px 12px;
  }
  .dz-group-name  { font-weight: 700; color: var(--text-1); }
  .dz-group-count { color: var(--text-4); font-size: 11px; margin-left: 8px; }
  .dz-group-count::before { content: '· '; }

  /* ── Delivery Zones View tweaks (per-operator) — row density + zone-badge
     style. Defaults (comfortable / pill) reproduce the base look. ── */
  #dz-table.dz-compact th { padding-top: 6px; padding-bottom: 6px; }
  #dz-table.dz-compact td { padding-top: 7px; padding-bottom: 7px; }
  #dz-table.dzb-square .dz-type-chip { border-radius: var(--r-sm); }
  #dz-table.dzb-underline .dz-type-chip {
    background: transparent !important; border-color: transparent !important;
    padding-left: 0; padding-right: 0; border-radius: 0;
    text-decoration: underline; text-underline-offset: 3px;
  }
  /* Actions sit at the far right, on one line. width:1% shrinks the column to
     its content and nowrap stops the two 28px buttons from wrapping onto
     stacked lines (the old fixed 36px was narrower than the pair). */
  .dz-rowact { white-space: nowrap; width: 1%; text-align: right; }
  .dz-rowact .btn {
    width: 28px; height: 28px; padding: 0;
    display: inline-flex; align-items: center; justify-content: center;
    vertical-align: middle;
    font-size: 14px; line-height: 1; color: var(--text-3);
  }
  /* Delete = Aged Brass (destructive but on-palette); duplicate stays neutral. */
  .dz-rowact-del { border-color: rgba(201, 149, 74, .25); color: var(--brass); }
  .dz-rowact-del:hover { background: rgba(201, 149, 74, .08); border-color: rgba(201, 149, 74, .45); color: var(--brass-hi); }
  /* ON toggle — Tea Green checked fill with a white check (was browser blue). */
  #dz-table input[type="checkbox"] { width: 18px; height: 18px; accent-color: var(--accent); cursor: pointer; }

  /* Edit drawer form */
  .dz-edit-form { display: flex; flex-direction: column; gap: 14px; }
  .dz-edit-form h4 {
    margin: 10px 0 -2px;
    padding-bottom: 4px;
    font-size: 11px; text-transform: uppercase; letter-spacing: .06em;
    color: var(--text-3); font-weight: 700;
    border-bottom: 1px solid var(--line);
  }
  .dz-grid {
    display: grid; gap: 10px;
    grid-template-columns: repeat(2, 1fr);
  }
  .dz-grid .field { margin: 0; }
  .dz-grid input, .dz-grid select { width: 100%; box-sizing: border-box; }
  .dz-seg {
    display: inline-flex; padding: 3px; gap: 2px;
    background: var(--bg-elev-1); border: 1px solid var(--line);
    border-radius: 8px; width: fit-content;
  }
  .dz-seg button {
    border: 0; background: transparent;
    padding: 6px 14px; border-radius: 6px;
    font-size: 12px; font-weight: 600;
    color: var(--text-3); cursor: pointer;
  }
  .dz-seg button.active { background: var(--bg-pane); color: var(--text-1); }
  .dz-hours-pair { display: inline-flex; align-items: center; gap: 8px; white-space: nowrap; }
  .dz-hours-pair input { width: 64px; text-align: center; }
  .dz-hours-pair .dash { color: var(--text-3); }
  .dz-check-row {
    display: flex; align-items: center; gap: 8px;
    font-size: 12px; color: var(--text-2);
  }
  .dz-rowact-btn + .dz-rowact-btn { margin-left: 4px; }

  /* Multi-select chip picker — used by Add zone to pick one OR many
     districts (GZ) / cities (other) in one shot. Save creates one
     zone per selected chip with the same form values. */
  .dz-chip-grid {
    display: flex; flex-wrap: wrap; gap: 6px;
    margin-top: 4px;
  }
  .dz-chip {
    display: inline-flex; align-items: center; gap: 4px;
    padding: 6px 12px;
    border: 1px solid var(--line);
    border-radius: 999px;
    background: var(--bg-elev-1);
    font-size: 12px;
    color: var(--text-1);
    cursor: pointer;
    user-select: none;
    transition: background var(--t-fast) ease, border-color var(--t-fast) ease, opacity var(--t-fast) ease;
  }
  .dz-chip:hover { background: var(--bg-elev-2); }
  .dz-chip.on {
    background: rgba(163, 180, 159, .18);
    border-color: var(--accent);
    color: var(--accent-hi);
  }
  .dz-chip.taken {
    opacity: .45;
    cursor: not-allowed;
    text-decoration: line-through;
  }
  .dz-chip .dz-chip-x {
    display: inline-flex; align-items: center; justify-content: center;
    width: 14px; height: 14px;
    border-radius: 999px;
    background: rgba(0, 0, 0, .18);
    color: inherit;
    font-size: 11px; line-height: 1;
    margin-left: 2px;
  }
  .dz-chip .dz-chip-x:hover { background: rgba(0, 0, 0, .32); }
  .dz-multi-count {
    font-size: 11px; color: var(--text-3); margin-top: 6px;
  }
  .dz-multi-count b { color: var(--accent-hi); }

  /* Apply-to-others panel — opens on top of the edit drawer. */
  .label-row {
    display: flex; align-items: center; gap: 12px;
    margin-bottom: 8px;
  }
  .dz-apply-tools {
    display: inline-flex; gap: 12px; margin-left: auto;
  }
  .btn-link {
    background: transparent; border: 0; padding: 0;
    font-size: 11px; color: var(--text-3);
    text-decoration: underline; cursor: pointer;
  }
  .btn-link:hover { color: var(--text-1); }
  .dz-apply-bundle {
    display: flex; align-items: flex-start; gap: 10px;
    padding: 10px 12px;
    background: var(--bg-elev-1);
    border: 1px solid var(--line);
    border-radius: var(--r-md);
    margin-bottom: 8px;
    cursor: pointer;
    transition: border-color var(--t-fast) ease, background var(--t-fast) ease;
  }
  .dz-apply-bundle:hover { border-color: var(--line-strong); }
  .dz-apply-bundle input[type=checkbox] { margin-top: 2px; flex-shrink: 0; }
  .dz-bundle-title { display: block; font-size: 13px; font-weight: 600; color: var(--text-1); }
  .dz-bundle-sub   { display: block; font-size: 11px; margin-top: 2px; line-height: 1.4; }
  .dz-apply-list {
    max-height: 240px; overflow-y: auto;
    border: 1px solid var(--line);
    border-radius: var(--r-md);
    background: var(--bg-input);
  }
  .dz-apply-list .dz-apply-row {
    display: flex; align-items: center; gap: 10px;
    padding: 8px 12px;
    border-bottom: 1px solid var(--line-soft);
    cursor: pointer;
    font-size: 12px;
  }
  .dz-apply-list .dz-apply-row:last-child { border-bottom: 0; }
  .dz-apply-list .dz-apply-row:hover { background: var(--bg-elev-1); }
  .dz-apply-list .dz-apply-row input { flex-shrink: 0; }
  .dz-apply-list .dz-type-chip { font-size: 10px; }
  .dz-apply-empty { padding: 16px; text-align: center; color: var(--text-4); font-size: 12px; }
  .dz-apply-row .dz-apply-name { flex: 1; min-width: 0; }
  .dz-apply-row .dz-apply-name .sub { color: var(--text-4); font-size: 11px; margin-left: 6px; }

  /* ═══════════════════════════════════════════════════════════════════
     PILLS (status)
     ═══════════════════════════════════════════════════════════════════ */
  .pill {
    display: inline-flex; align-items: center; gap: 5px;
    padding: 2px 10px 2px 8px;
    border-radius: 999px;
    font-size: 11px; font-weight: 600;
    letter-spacing: .04em;
    border: 1px solid;
  }
  .pill .dot { width: 6px; height: 6px; border-radius: 50%; background: var(--text-4); }
  .pill.pub { background: rgba(163, 180, 159, .12); color: var(--accent); border-color: rgba(163, 180, 159, .30); }
  .pill.pub .dot { background: var(--accent); }
  .pill.draft { background: var(--bg-elev-1); color: var(--text-3); border-color: var(--line); }
  .pill.draft .dot { background: var(--text-4); }
  /* Meituan sync status — Tea = live/synced, Brass = needs action, neutral = not on store */
  .pill.mt-ok   { background: rgba(163,180,159,.12); color: var(--accent); border-color: rgba(163,180,159,.30); }
  .pill.mt-ok .dot   { background: var(--accent); }
  .pill.mt-warn { background: rgba(201,149,74,.14); color: var(--warn); border-color: rgba(201,149,74,.35); }
  .pill.mt-warn .dot { background: var(--warn); }
  .pill.mt-off  { background: var(--bg-elev-1); color: var(--text-3); border-color: var(--line); }
  .pill.mt-off .dot  { background: var(--text-4); }
  /* Meituan toolbar — segmented filter + overflow menu */
  .mt-seg { display: inline-flex; border: 1px solid var(--line); border-radius: 8px; overflow: hidden; }
  .mt-seg button { border: 0; background: transparent; color: var(--text-2); font-size: 12px; padding: 5px 12px; cursor: pointer; }
  .mt-seg button + button { border-left: 1px solid var(--line); }
  .mt-seg button.active { background: var(--accent); color: var(--accent-ink); font-weight: 600; }
  .mt-cell-foot { display: flex; align-items: center; gap: 8px; margin-top: 5px; }
  .mt-sub { font-size: 11px; line-height: 1.35; margin-top: 3px; }
  /* Product identity cell — photo + names + SKU chip */
  .mt-pcell { display: flex; align-items: center; gap: 11px; min-width: 0; }
  .mt-thumb { width: 40px; height: 40px; border-radius: 8px; flex: none; object-fit: cover;
              background: var(--bg-elev-2); border: 1px solid var(--line-soft); }
  .mt-thumb-ph { display: flex; align-items: center; justify-content: center; color: var(--text-4); }
  .mt-thumb-ph svg { width: 18px; height: 18px; }
  .mt-pmeta { min-width: 0; }
  .mt-psku { display: inline-block; font-family: var(--f-mono, ui-monospace, monospace); font-size: 11px;
             line-height: 1; color: var(--text-3); background: var(--bg-elev-1);
             border: 1px solid var(--line-soft); border-radius: 5px; padding: 2.5px 6px;
             margin-top: 4px; letter-spacing: .02em; }
  /* Live on neither store — brass edge stripe so problem rows pop while scrolling */
  tr.mt-unmapped td:first-child { box-shadow: inset 3px 0 0 var(--brass); }

  /* ── Meituan tab redesign (2026-07): status strip + sub-views ─────────────── */
  .mt-how { margin: 0 0 var(--s4); font-size: 12.5px; color: var(--text-3); }
  .mt-how summary { cursor: pointer; }
  .mt-how > div { margin-top: 8px; padding: 10px 14px; background: var(--bg-elev-1); border: 1px solid var(--line-soft); border-radius: 8px; max-width: 70ch; color: var(--text-2); }

  /* status strip — one compact row instead of the four fc-kpi tiles */
  .mt-strip { display: flex; align-items: center; flex-wrap: wrap; gap: 6px 0; background: var(--bg-pane); border: 1px solid var(--line); border-radius: 10px; padding: 12px 6px; margin: 0 0 18px; }
  .mt-cell { display: flex; flex-direction: column; gap: 2px; padding: 2px 18px; border-right: 1px solid var(--line-soft); min-width: 0; }
  #mt-kpi-order-tile { border-right: none; }
  .mt-cell .k { font-family: var(--f-mono); font-size: 10px; font-weight: 700; letter-spacing: .08em; text-transform: uppercase; color: var(--text-3); white-space: nowrap; }
  .mt-cell .v { font-size: 14px; font-weight: 600; color: var(--text-1); display: flex; align-items: center; gap: 6px; white-space: nowrap; font-variant-numeric: tabular-nums; }
  .mt-cell .v small { font-weight: 400; color: var(--text-3); font-size: 11.5px; }
  .mt-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--accent); box-shadow: 0 0 0 3px rgba(163,180,159,.18); flex: none; }
  .mt-dot.off { background: var(--danger); box-shadow: 0 0 0 3px rgba(232,85,106,.18); }
  .mt-cell.alert .k, .mt-cell.alert .v { color: var(--warn); }
  .mt-strip-grow { flex: 1 1 auto; }
  .mt-strip-act { display: flex; align-items: center; gap: 10px; padding: 0 12px; }
  .mt-upd { font-size: 11px; color: var(--text-4); white-space: nowrap; }

  /* sub-tabs: Products / Orders / Bundles */
  .mt-tabs { display: flex; gap: 2px; border-bottom: 1px solid var(--line); margin: 0 0 16px; }
  .mt-tab { appearance: none; background: none; border: none; border-bottom: 2px solid transparent; margin-bottom: -1px; padding: 8px 14px 9px; font: 600 13.5px var(--f-sans); color: var(--text-3); cursor: pointer; display: flex; align-items: center; gap: 7px; }
  .mt-tab:hover { color: var(--text-1); }
  .mt-tab.active { color: var(--text-1); border-bottom-color: var(--brass); }
  .mt-tab .n { font: 600 11px var(--f-mono); color: var(--text-3); background: var(--bg-elev-2); border-radius: 99px; padding: 1px 7px; }
  .mt-tab .n:empty { display: none; }
  .mt-tab.active .n { background: rgba(201,149,74,.16); color: var(--brass); }

  /* attention banner — only home of the Match-names flow */
  .mt-attn { display: flex; align-items: center; flex-wrap: wrap; gap: 12px; background: rgba(201,149,74,.12); border: 1px solid rgba(201,149,74,.35); border-radius: 10px; padding: 11px 16px; margin: 0 0 12px; color: var(--brass); }
  .mt-attn .atxt { flex: 1; min-width: 200px; color: var(--text-2); font-size: 13px; }
  .mt-attn .atxt b { color: var(--text-1); }

  /* orders / bundles cards */
  .mt-card { background: var(--bg-pane); border: 1px solid var(--line); border-radius: 10px; overflow: hidden; }
  .mt-note { display: flex; align-items: center; gap: 8px; color: var(--text-3); font-size: 12px; padding: 10px 14px; background: var(--bg-elev-1); border-bottom: 1px solid var(--line-soft); }
  .mt-dayhd { font-family: var(--f-mono); font-size: 10px; font-weight: 700; letter-spacing: .08em; text-transform: uppercase; color: var(--text-4); padding: 11px 14px 5px; }
  .mt-orow { display: flex; align-items: center; gap: 12px; padding: 8px 14px; border-bottom: 1px solid var(--line-soft); cursor: pointer; }
  .mt-orow:hover { background: var(--bg-elev-1); }
  .mt-oago { color: var(--text-4); font-size: 11.5px; flex: none; width: 58px; }
  .mt-ostore { font-size: 10.5px; font-weight: 600; color: var(--text-3); background: var(--bg-elev-2); border-radius: 99px; padding: 2px 8px; flex: none; min-width: 44px; text-align: center; }
  .mt-ono { font-family: var(--f-mono); font-size: 11.5px; color: var(--text-3); flex: none; }
  .mt-oitems { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--text-2); font-size: 12.5px; }
  .mt-oamt { font-family: var(--f-mono); font-weight: 600; font-size: 13px; font-variant-numeric: tabular-nums; flex: none; min-width: 64px; text-align: right; color: var(--text-1); }
  .mt-odetail { padding: 4px 14px 12px 84px; border-bottom: 1px solid var(--line-soft); font-size: 12.5px; color: var(--text-2); }

  /* mapping-table sync toggle: hidden until the row is hovered so rows read as
     name + status; stays visible when OFF (a paused product is never hidden)
     or when reached by keyboard */
  #mt-products-table .toggle-label { opacity: 0; transition: opacity .12s; }
  #mt-products-table tr:hover .toggle-label,
  #mt-products-table .toggle-label:focus-within,
  #mt-products-table .toggle-label:has(input:not(:checked)) { opacity: 1; }

  /* Match-names dialog */
  .mt-ovl { position: fixed; inset: 0; background: var(--bg-overlay); z-index: 9999; display: flex; align-items: center; justify-content: center; padding: 20px; }
  .mt-dlg { background: var(--bg-pane); color: var(--text-1); width: min(540px, 100%); max-height: 86vh; overflow: auto; border-radius: 12px; border: 1px solid var(--line-strong); box-shadow: 0 18px 50px rgba(0,0,0,.4); }
  .mt-dlg-hd { display: flex; justify-content: space-between; align-items: center; gap: 10px; padding: 14px 16px; border-bottom: 1px solid var(--line); }
  .mt-dlg-hd b { font-size: 15px; }
  .mt-dlg-bd { padding: 16px; }
  .mt-dlg-bd p { margin: 0 0 14px; color: var(--text-2); font-size: 13px; max-width: 52ch; }
  .mt-okline { display: flex; align-items: center; gap: 9px; background: rgba(163,180,159,.14); color: var(--accent); border-radius: 8px; padding: 10px 13px; font-weight: 600; margin-bottom: 6px; font-size: 13px; }
  .mt-pickhd { font-size: 12px; font-weight: 600; color: var(--text-2); margin: 12px 0 8px; }
  .mt-pick { border: 1px solid var(--line-soft); border-radius: 8px; padding: 10px 12px; margin-bottom: 8px; }
  .mt-pick .pn { font-weight: 600; margin-bottom: 6px; font-size: 13px; }
  .mt-pick .chips { display: flex; gap: 6px; flex-wrap: wrap; }
  .mt-chip { border: 1px solid var(--line); background: var(--bg-pane); color: var(--text-1); border-radius: 99px; padding: 4px 12px; font: 12.5px var(--f-sans); cursor: pointer; }
  .mt-chip:hover { border-color: var(--accent); color: var(--accent); background: rgba(163,180,159,.12); }
  .mt-dlg-ft { display: flex; align-items: center; gap: 10px; padding: 12px 16px; border-top: 1px solid var(--line); }
  .mt-tinylink { background: none; border: none; padding: 0; font: 12px var(--f-sans); color: var(--text-4); text-decoration: underline; text-underline-offset: 3px; cursor: pointer; }
  .mt-tinylink:hover { color: var(--text-2); }

  @media (max-width: 720px) {
    .mt-cell { padding: 2px 12px; border-right: none; }
    .mt-strip-act { width: 100%; justify-content: flex-end; padding-top: 6px; }
    .mt-odetail { padding-left: 14px; }
    .mt-oago { width: auto; }
  }
  @media (prefers-reduced-motion: reduce) {
    #mt-products-table .toggle-label { transition: none; }
  }
  /* Brass = needs-action / in-flight; Tea = money-in / done; neutral = closed. */
  .pill.st-pending_payment { background: rgba(201, 149, 74, .12); color: var(--st-pending_payment); border-color: rgba(201, 149, 74, .30); }
  .pill.st-pending_payment .dot { background: var(--st-pending_payment); }
  .pill.st-paid            { background: rgba(163, 180, 159, .12); color: var(--st-paid); border-color: rgba(163, 180, 159, .30); }
  .pill.st-paid .dot       { background: var(--st-paid); }
  .pill.st-preparing       { background: rgba(201, 149, 74, .12); color: var(--st-preparing); border-color: rgba(201, 149, 74, .30); }
  .pill.st-preparing .dot  { background: var(--st-preparing); }
  .pill.st-dispatched      { background: rgba(201, 149, 74, .12); color: var(--st-dispatched); border-color: rgba(201, 149, 74, .30); }
  .pill.st-dispatched .dot { background: var(--st-dispatched); }
  .pill.st-rider_pickup      { background: rgba(74, 114, 176, .12); color: var(--st-rider_pickup); border-color: rgba(74, 114, 176, .30); }
  .pill.st-rider_pickup .dot { background: var(--st-rider_pickup); }
  .pill.st-delivered       { background: rgba(163, 180, 159, .14); color: var(--st-delivered); border-color: rgba(163, 180, 159, .35); }
  .pill.st-delivered .dot  { background: var(--st-delivered); }
  .pill.st-cancelled       { background: var(--bg-elev-1); color: var(--text-3); border-color: var(--line); }
  .pill.st-cancelled .dot  { background: var(--text-4); }
  .pill.st-refunded        { background: var(--bg-elev-1); color: var(--st-refunded); border-color: var(--line); }
  .pill.st-refunded .dot   { background: var(--st-refunded); }
  .pill.st-awaiting_restock      { background: rgba(201, 149, 74, .14); color: var(--st-awaiting_restock); border-color: rgba(201, 149, 74, .35); }
  .pill.st-awaiting_restock .dot { background: var(--st-awaiting_restock); }

  /* Semantic pill variants — use these instead of ad-hoc inline styles so
     every tab (Products, Vouchers, Flash, Reviews, etc.) shares the same
     visual vocabulary. Pick the meaning, not the colour:
       .pub      → published / live / active (lime) — legacy alias
       .draft    → unpublished / inactive / ended (grey) — legacy alias
       .success  → completed, ok, paid, granted-success (green)
       .info     → upcoming, public, granted, neutral-informational (blue)
       .warn     → pending, attention needed, partial (warm orange)
       .danger   → failed, rejected, expired-bad (red)
       .paused   → on hold, awaiting, standby (lilac)
       .neutral  → archived, generic (muted grey) */
  .pill.success      { background: rgba(163, 180, 159, .14); color: var(--success); border-color: rgba(163, 180, 159, .35); }
  .pill.success .dot { background: var(--success); }
  .pill.info         { background: rgba(163, 180, 159, .12); color: var(--accent); border-color: rgba(163, 180, 159, .30); }
  .pill.info .dot    { background: var(--accent); }
  .pill.warn         { background: rgba(201, 149, 74, .14); color: var(--brass); border-color: rgba(201, 149, 74, .30); }
  .pill.warn .dot    { background: var(--brass); }
  .pill.danger       { background: rgba(232, 85, 106, .12); color: #e8556a; border-color: rgba(232, 85, 106, .3); }
  .pill.danger .dot  { background: #e8556a; }
  .pill.paused       { background: rgba(201, 149, 74, .14); color: var(--brass); border-color: rgba(201, 149, 74, .35); }
  .pill.paused .dot  { background: var(--brass); }
  .pill.neutral      { background: var(--bg-elev-1); color: var(--text-3); border-color: var(--line); }
  .pill.neutral .dot { background: var(--text-4); }

  /* ─── Status-filter chip strip (Orders page) ─── */
  .status-chips {
    display: flex; flex-wrap: wrap; gap: 6px;
    align-items: center;
  }
  /* Customer-scope chip — the removable "showing one customer" banner above the
     orders list (the "View all N orders" path). ocf- prefixed to stay clear of
     the base .k/.v/.dot component classes. Sage accent matches the All chip. */
  .ord-cust-chip {
    display: flex; align-items: center; gap: 8px;
    margin-bottom: var(--s3);
    padding: 7px 8px 7px 14px;
    width: fit-content; max-width: 100%;
    border: 1px solid rgba(163, 180, 159, .40);
    background: rgba(163, 180, 159, .12);
    border-radius: 999px;
    font-size: 13px;
  }
  .ord-cust-chip[hidden] { display: none; }
  .ord-cust-chip .ocf-lbl { color: var(--text-3); }
  .ord-cust-chip .ocf-name { color: var(--text-1); font-weight: 600; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .ord-cust-chip .ocf-x {
    display: inline-flex; align-items: center; justify-content: center;
    width: 22px; height: 22px; flex: none;
    border: 0; border-radius: 50%; background: transparent; cursor: pointer;
    color: var(--text-3); transition: background var(--t-fast) ease, color var(--t-fast) ease;
  }
  .ord-cust-chip .ocf-x svg { width: 15px; height: 15px; }
  .ord-cust-chip .ocf-x:hover { background: rgba(163, 180, 159, .22); color: var(--text-1); }

  /* "View all N orders →" link at the foot of a customer drawer's Recent-orders
     fold. Quiet accent text button. */
  .cux-viewall { margin-top: 10px; text-align: right; }
  .cux-viewall-btn {
    border: 0; background: transparent; cursor: pointer;
    font-size: 13px; font-weight: 600; color: var(--accent);
    padding: 4px 2px; transition: opacity var(--t-fast) ease;
  }
  .cux-viewall-btn:hover { opacity: .72; text-decoration: underline; }

  .status-chip {
    display: inline-flex; align-items: center; gap: 6px;
    padding: 6px 12px;
    border-radius: 999px;
    font-size: 12px; font-weight: 600;
    letter-spacing: .03em;
    border: 1px solid var(--line);
    background: var(--bg-elev-1);
    color: var(--text-3);
    cursor: pointer;
    transition: background var(--t-fast) ease, color var(--t-fast) ease,
                border-color var(--t-fast) ease, transform var(--t-fast) ease;
    user-select: none;
  }
  .status-chip:hover { background: var(--bg-elev-2); color: var(--text-2); }
  .status-chip:active { transform: translateY(1px); }
  .status-chip .dot { width: 6px; height: 6px; border-radius: 50%; background: var(--text-4); }
  /* Open-work count badge. Empty by default (hidden) — JS only fills it when
     the count is > 0, so cleared queues carry no "0". Neutral for the normal
     flow; .alert tints the stuck states (awaiting restock / refund queue). */
  .status-chip .chip-ct { margin-left: 5px; font-size: 11px; font-weight: 700; font-variant-numeric: tabular-nums; opacity: .85; }
  .status-chip .chip-ct:empty { display: none; }
  .status-chip .chip-ct.alert { color: var(--danger); opacity: 1; }
  .status-chip.active                     { background: var(--bg-elev-2); color: var(--text-1); border-color: var(--line-strong); }
  .status-chip.active.st-all              { background: rgba(163, 180, 159, .14); color: var(--accent); border-color: rgba(163, 180, 159, .40); }
  .status-chip.active.st-all .dot         { background: var(--accent); }
  .status-chip.active.st-pending_payment  { background: rgba(201, 149, 74, .14); color: var(--st-pending_payment); border-color: rgba(201, 149, 74, .45); }
  .status-chip.active.st-paid             { background: rgba(163, 180, 159, .14); color: var(--st-paid); border-color: rgba(163, 180, 159, .45); }
  .status-chip.active.st-preparing        { background: rgba(201, 149, 74, .16); color: var(--st-preparing); border-color: rgba(201, 149, 74, .45); }
  .status-chip.active.st-dispatched       { background: rgba(201, 149, 74, .16);  color: var(--st-dispatched); border-color: rgba(201, 149, 74, .45); }
  .status-chip.active.st-rider_pickup     { background: rgba(74, 114, 176, .16);  color: var(--st-rider_pickup); border-color: rgba(74, 114, 176, .45); }
  .status-chip.active.st-delivered        { background: rgba(163, 180, 159, .16);  color: var(--st-delivered); border-color: rgba(163, 180, 159, .45); }
  .status-chip.active.st-awaiting_restock { background: rgba(201, 149, 74, .16); color: var(--st-awaiting_restock); border-color: rgba(201, 149, 74, .45); }
  .status-chip.active.st-cancelled        { background: var(--bg-elev-2); color: var(--text-2); border-color: var(--line-strong); }
  .status-chip.active.st-refunded         { background: var(--bg-elev-2);  color: var(--st-refunded); border-color: var(--line-strong); }
  .status-chip.active .dot                { background: currentColor; }

  /* ═══════════════════════════════════════════════════════════════════
     PRODUCT ROW (inline-edit inputs in the table)
     ═══════════════════════════════════════════════════════════════════ */
  .row-actions { display: flex; gap: 4px; }
  .product-meta { display: flex; align-items: center; gap: 10px; }
  .product-thumb {
    width: 36px; height: 36px;
    background: var(--bg-elev-2);
    border-radius: var(--r-sm);
    overflow: hidden; flex-shrink: 0;
    display: flex; align-items: center; justify-content: center;
    color: var(--text-4);
  }
  .product-thumb img { width: 100%; height: 100%; object-fit: cover; }
  .product-thumb svg { width: 16px; height: 16px; }
  .product-name { font-size: 13px; color: var(--text-1); }
  .product-sub { font-size: 11px; color: var(--text-3); margin-top: 1px; }
  td input[type=number] { padding: 5px 8px; font-size: 12px; }

  details.copy-edit {
    grid-column: 1 / -1;
    margin-top: 8px;
    background: var(--bg-app);
    border: 1px solid var(--line-soft);
    border-radius: var(--r-md);
    padding: 10px;
  }
  details.copy-edit summary {
    cursor: pointer; user-select: none;
    font-size: 11px; color: var(--text-3); letter-spacing: .06em; text-transform: uppercase;
    padding: 2px 0;
  }
  details.copy-edit summary:hover { color: var(--text-1); }
  details.copy-edit textarea, details.copy-edit input { margin-top: 6px; }

  /* ═══════════════════════════════════════════════════════════════════
     SIDE DRAWER — slide-in right-side panel used by Orders, Customers,
     Logistics zones, Media items, and Pop-ups. The underlying list
     stays anchored and clickable so users can jump between records
     rapidly. ESC + the × button close.

     Width can be tuned per consumer via --drawer-vw / --drawer-px on
     the aside; defaults give the same min(60vw, 760px) the Orders
     drawer shipped with. Add the .dw-wide class for editors that need
     extra room (e.g. the Pop-up editor with its phone preview).
     ═══════════════════════════════════════════════════════════════════ */
  .side-drawer {
    position: fixed;
    top: 0; right: 0; bottom: 0;
    width: min(var(--drawer-vw, 60vw), var(--drawer-px, 760px));
    min-width: 480px;
    background: var(--bg-app);
    border-left: 1px solid var(--line);
    box-shadow: -12px 0 32px rgba(0, 0, 0, .35);
    transform: translateX(100%);
    /* Symmetric slide both ways: on close, hold `visibility: visible`
       for the full slide-out (the 253ms visibility delay) so the panel
       glides off-screen instead of vanishing instantly. On open the
       delay is 0 so it appears immediately and slides in. Close is the
       gentle 253ms beat shared by every drawer; open is the slow zen
       glide set on .open below. */
    transition: transform 253ms cubic-bezier(.22, .61, .36, 1),
                visibility 0s linear 253ms;
    z-index: 200;
    display: flex;
    flex-direction: column;
    overflow: hidden;
    visibility: hidden;
  }
  /* ─── Unified "zen" open for EVERY side-drawer ───
     One motion language across the whole backend: every drawer glides in on
     the Mini App Editor's signature soft deceleration (cubic-bezier .16,1,.3,1)
     over a long beat so it settles rather than snaps. Only the OPEN is slowed —
     the base .side-drawer rule governs the gentle 253ms slide-out. The inner
     content (.drawer-body children) drifts up + fades a beat behind the panel,
     so Orders, Customers, Zones, Media, Pop-ups, Activity all feel identical. */
  .side-drawer.open {
    transform: translateX(0);
    visibility: visible;
    transition: transform 1100ms cubic-bezier(.16, 1, .3, 1),
                visibility 0s linear 0s;
  }
  /* ─── Order-drawer focus scrim ───────────────────────────────────────
     Blur + dim behind the order detail so the operator focuses on the one
     order rather than the list behind it. z-index 199 — under the drawer
     (200), over the topbar (12) + rail (11) — so everything but the drawer
     de-focuses. Fades with the drawer; a click closes the order (same as the
     dw-dismiss outside-click). Scoped to Orders. On phones the full-screen
     drawer covers it, so it's a no-op there. */
  .ord-scrim {
    position: fixed; inset: 0; z-index: 199;
    background: rgba(17, 26, 43, .30);
    backdrop-filter: blur(4px); -webkit-backdrop-filter: blur(4px);
    opacity: 0; pointer-events: none;
    transition: opacity .3s var(--ease-out);
  }
  .ord-scrim.show { opacity: 1; pointer-events: auto; }
  @media (prefers-reduced-motion: reduce) { .ord-scrim { transition: none; } }
  .side-drawer.dw-wide { --drawer-vw: 80vw; --drawer-px: 980px; }
  /* Compact peek — the activity drawer holds only a few lines, so it doesn't
     need the full editor width. Narrower panel sized to its content. */
  .side-drawer.dw-compact { --drawer-vw: 32vw; --drawer-px: 420px; min-width: 360px; }
  .side-drawer .drawer-body > * { opacity: 0; transform: translateY(10px); }
  .side-drawer.open .drawer-body > * {
    opacity: 1;
    transform: none;
    transition: opacity 900ms cubic-bezier(.16, 1, .3, 1) 260ms,
                transform 900ms cubic-bezier(.16, 1, .3, 1) 260ms;
  }
  /* Orders renders its detail as DIRECT children of .drawer-body (#order-detail-mount
     IS the body), so the zen fade-up above animates the real content. The customer
     drawer nests its content one level deeper, inside the persistent #cu-drawer-actions
     / #cu-detail-body wrappers — so the rule above only ever faded those wrappers (once,
     on open) while the detail injected after the fetch popped in with no fade. Mirror the
     same entrance on that injected content so the customer window fades in identically. */
  #cu-drawer-actions > *, #cu-detail-body > * { opacity: 0; transform: translateY(10px); }
  .side-drawer.open #cu-drawer-actions > *,
  .side-drawer.open #cu-detail-body > * {
    opacity: 1;
    transform: none;
    transition: opacity 900ms cubic-bezier(.16, 1, .3, 1) 260ms,
                transform 900ms cubic-bezier(.16, 1, .3, 1) 260ms;
  }
  /* Switching records while a drawer is already open (Orders / Customers):
     cross-fade the whole mount — old record fades out, new one fades back in on
     the same zen ease as the slide. JS toggles .od-swapping / .cu-swapping
     around the data swap. (#order-detail-mount IS the order drawer's body.) */
  #order-detail-mount,
  #cu-drawer .drawer-body { transition: opacity 792ms cubic-bezier(.16, 1, .3, 1); }
  #order-detail-mount.od-swapping,
  #cu-drawer .drawer-body.cu-swapping { opacity: 0; transition: opacity 317ms cubic-bezier(.16, 1, .3, 1); }
  .side-drawer .drawer-head {
    padding: 14px 18px;
    display: flex; align-items: center; justify-content: space-between;
    gap: 12px;
    border-bottom: 1px solid var(--line);
    flex-shrink: 0;
  }
  .side-drawer .drawer-body {
    flex: 1;
    overflow-y: auto;
    padding: var(--s5);
    /* Stop scroll chaining: when the drawer's scroll hits the top or
       bottom edge, the wheel event used to bubble through the
       semi-transparent backdrop and start scrolling the underlying
       Customers/Orders list. `contain` (with `none` for old browsers)
       caps the scroll at the drawer's own bounds. */
    overscroll-behavior: contain;
    overscroll-behavior-y: contain;
  }
  .side-drawer .drawer-foot {
    padding: 14px 18px;
    display: flex; align-items: center; gap: 8px; justify-content: flex-end;
    border-top: 1px solid var(--line);
    flex-shrink: 0;
  }
  /* Orders' print bar is retired (2026-07-30) — Print receipt rides the pinned
     header instead, so this slot is always empty and always collapsed. The node
     stays because the shared drawer shell owns it; only the layout rules for
     its old contents (spacer + "Prints at <store>" hint) are gone. */
  #order-drawer .drawer-foot { justify-content: flex-start; flex-wrap: wrap; }
  #order-drawer .drawer-foot:empty { display: none; }

  /* ── Orders · in-flow PAGE (desktop ≥721px, body.ord-viewing) ───────────
     Rebuilt from the floating drawer into a real page. #order-drawer now lives
     inside <main>; when an order is open we hide the tab sections and drop the
     drawer to position:static, so the order renders in the content-area FLOW —
     no fixed overlay, no slide-in, no shadow. The nav rail + topbar (both fixed)
     stay put. Phones (<721px) are untouched and keep the full-screen drawer. */
  .ord-page-bar { display: none; }
  @media (min-width: 721px) {
    /* Hide the list; give the content area over to the order. main is pinned to
       the viewport (below the 56px topbar) and clips, so the order owns a fixed
       content height and scrolls INSIDE its body — reliable, unlike relying on
       window scroll of a static block. Rail + topbar (both fixed) stay put. */
    body.ord-viewing main > section { display: none; }
    body.ord-viewing main { padding: 56px 0 0; overflow: hidden; }
    body.ord-viewing #order-drawer {
      position: static;
      width: auto; min-width: 0; max-width: none;
      height: calc(100vh - 56px);
      transform: none; visibility: visible;
      box-shadow: none; border: 0;
      /* White content panel — matches the mockup's .c-main (var(--surface)),
         distinct from the cream page/rail behind it. */
      background: var(--bg-pane);
      display: flex; flex-direction: column; overflow: hidden;
    }
    body.ord-viewing #order-drawer .drawer-close { display: none; }
    /* Back + breadcrumb bar — pinned at the top, aligned to the 900px column. */
    body.ord-viewing #order-drawer .ord-page-bar {
      display: flex; align-items: center; gap: 9px; flex-shrink: 0;
      width: 100%;
      padding: var(--s4) var(--s6) var(--s3);
    }
    .ord-page-bar .opb-back {
      display: inline-flex; align-items: center; gap: 8px;
      font: 500 14px var(--f-sans); color: var(--text-2);
      background: transparent; border: 0; cursor: pointer;
      padding: 6px 10px 6px 6px; border-radius: var(--r-md);
    }
    .ord-page-bar .opb-back:hover { color: var(--text-1); background: var(--bg-elev-1); }
    .ord-page-bar .opb-back svg { width: 16px; height: 16px; }
    .ord-page-bar .opb-sep { color: var(--text-3); }
    .ord-page-bar .opb-cur { font-weight: 600; color: var(--text-1); font-variant-numeric: tabular-nums; }
    /* The order body scrolls; content sits in a readable 900px column. */
    body.ord-viewing #order-drawer .drawer-body {
      flex: 1; min-height: 0; overflow-y: auto;
      padding: 0 var(--s6) var(--s6);
    }
    body.ord-viewing #order-drawer .order-card { max-width: none; margin: 0; }
    /* Print footer pinned at the bottom of the page. */
    body.ord-viewing #order-drawer .drawer-foot {
      flex-shrink: 0; width: 100%;
      padding: var(--s3) var(--s6);
      border-top: 1px solid var(--line-soft);
    }

    /* ── Orders · New order — the same takeover, revived (§1.7a) ─────────
       body.ord-viewing is set while #tab-orders-new is the active section:
       main pins below the topbar and clips (rules above), every other
       section is display:none, and this one — out-specificity-ing the
       generic `main > section { display:none }` via its id — owns the fixed
       content height and scrolls INSIDE .onw-scroll. Rail + topbar stay. */
    body.ord-viewing main > section#tab-orders-new.active {
      display: flex; flex-direction: column;
      height: calc(100vh - 56px); min-height: 0;
      overflow: hidden;
    }
    body.ord-viewing #tab-orders-new .onw-scroll {
      flex: 1; min-height: 0; overflow: auto;
    }
  }

  /* ── Orders · Drafts view (odr-*) ────────────────────────────────────
     A draft's own row shape. Deliberately NOT #orders-table: no order number,
     no channel badge, no status pill, no delivery cell. Prefixed odr-* like
     every other retail-admin section, so nothing here collides with the base
     layer's .dot / .v / .t / .k. */
  #ord-drafts-view[hidden] { display: none; }
  #ord-drafts-view .odr-card {
    background: var(--td-surface); border: 1px solid var(--td-border);
    border-radius: 14px; clip-path: inset(0 round 14px);
  }
  #ord-drafts-view .odr-row {
    display: flex; align-items: center; gap: 16px;
    padding: 14px 18px; border-bottom: 1px solid var(--td-border);
  }
  #ord-drafts-view .odr-row:last-child { border-bottom: 0; }
  #ord-drafts-view .odr-open {
    flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 1px;
    background: none; border: 0; padding: 0; margin: 0;
    font-family: inherit; text-align: left; cursor: pointer; color: var(--td-text);
  }
  #ord-drafts-view .odr-t {
    font-size: 14px; font-weight: 500;
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  }
  /* An unlabelled draft is described, not named — set apart from a real name. */
  #ord-drafts-view .odr-t.odr-none { color: var(--td-text2); font-style: italic; }
  #ord-drafts-view .odr-m { font-size: 13px; color: var(--td-text2); }
  #ord-drafts-view .odr-v { font-size: 14px; font-weight: 500; font-variant-numeric: tabular-nums; white-space: nowrap; }
  #ord-drafts-view .odr-x {
    width: 28px; height: 28px; flex: 0 0 auto; padding: 0;
    border: 0; background: none; border-radius: 7px;
    color: var(--td-text2); cursor: pointer; display: grid; place-items: center;
  }
  #ord-drafts-view .odr-x:hover { background: var(--td-brick-tint); color: var(--td-brick-ink); }
  #ord-drafts-view .odr-x svg { width: 16px; height: 16px; display: block; }
  #ord-drafts-view .odr-open:focus-visible,
  #ord-drafts-view .odr-x:focus-visible { outline: 2px solid var(--td-dusk); outline-offset: 2px; }
  /* No drafts is what a tidy Monday looks like — nothing here apologises. */
  #ord-drafts-view .odr-empty {
    background: var(--td-surface); border: 1px solid var(--td-border); border-radius: 14px;
    padding: 34px 24px; display: flex; flex-direction: column; align-items: center; gap: 10px;
    text-align: center;
  }
  #ord-drafts-view .odr-empty h3 { font-size: 18px; font-weight: 500; margin: 0; }
  #ord-drafts-view .odr-empty p { font-size: 14px; color: var(--td-text2); max-width: 46ch; margin: 0; text-wrap: pretty; }

  /* ── Orders · New order — page chrome (Tacet-native, onw-* only) ──────
     All child classes are onw-* — the base css owns .opt/.dot/.k/.v/… and a
     bare class here silently inherits another section's look. Colour comes
     only from the shared --td-* tokens; no new palette, no hex. Below 721px
     the section renders in normal flow (no takeover) and these still apply. */
  #tab-orders-new {
    font-family: 'Inter', 'IBM Plex Sans SC', 'PingFang SC', 'Hiragino Sans GB', system-ui, sans-serif;
    font-size: 15px; line-height: 1.6; color: var(--td-text);
  }
  #tab-orders-new .onw-page { padding: 26px 34px 90px; max-width: 1240px; }
  /* §1.7a header — back link (the eyebrow) · title · two exits, one Dusk. */
  #tab-orders-new .onw-head {
    display: flex; align-items: center; gap: 14px;
    padding-bottom: 16px; border-bottom: 1px solid var(--td-border);
    margin-bottom: 22px;
  }
  /* 24px Inter 500 — §1.7's page-title spec. The mockup drew 600; Tacet
     forbids weights above 500, so the rulebook wins over the drawing. */
  #tab-orders-new .onw-title { font-size: 24px; font-weight: 500; letter-spacing: -.01em; line-height: 1.3; margin: 0; }
  #tab-orders-new .onw-acts { margin-left: auto; display: flex; gap: 10px; }
  #tab-orders-new .onw-btn {
    height: 38px; padding: 0 16px; border-radius: 8px;
    font-family: inherit; font-size: 14px; font-weight: 500;
    border: 1px solid transparent; cursor: pointer;
    display: inline-flex; align-items: center; gap: 7px;
    white-space: nowrap; color: var(--td-text); background: none;
  }
  #tab-orders-new .onw-btn svg { width: 16px; height: 16px; flex: 0 0 auto; display: block; }
  /* 🔴 THE BASE SIZE. An inline <svg> with no width/height falls back to the
     replaced-element default — 300×150 — so an unsized icon does not render
     small and wrong, it renders as a glyph the height of the form. This rule
     shipped missing on 2026-07-31: the mockup carried it as `.no-ico`, and it
     was dropped in the rename to onw-*. Every rule below sets position or
     colour and INHERITS this size; none of them can stand in for it. */
  #tab-orders-new .onw-ico { width: 16px; height: 16px; flex: 0 0 auto; display: block; }
  /* The page's ONE Dusk element (§1.8 rule 1 — the commit has first claim). */
  #tab-orders-new .onw-dusk { background: var(--td-dusk); border-color: var(--td-dusk); color: #fff; }
  #tab-orders-new .onw-dusk:hover { background: var(--td-dusk-hi); border-color: var(--td-dusk-hi); }
  /* The non-committing exit is Secondary, not Ghost (§1.7a, decided 07-30). */
  #tab-orders-new .onw-sec { border-color: var(--td-border); background: transparent; }
  #tab-orders-new .onw-sec:hover { background: rgba(var(--td-heat-rgb), .04); border-color: var(--td-text2); }
  #tab-orders-new .onw-ghost { padding: 0 10px 0 6px; color: var(--td-text2); }
  #tab-orders-new .onw-ghost:hover { background: rgba(var(--td-heat-rgb), .05); color: var(--td-text); }
  #tab-orders-new .onw-btn:disabled { opacity: .5; cursor: default; }
  #tab-orders-new .onw-btn:focus-visible { outline: 2px solid var(--td-dusk); outline-offset: 2px; }
  #tab-orders-new .onw-btn.onw-sm { height: 32px; padding: 0 12px; font-size: 13px; }

  /* ── The form — two columns, cards left, sticky summary right ──────── */
  #tab-orders-new .onw-grid { display: grid; grid-template-columns: minmax(0, 1fr) 300px; gap: 26px; align-items: start; }
  #tab-orders-new .onw-left { display: flex; flex-direction: column; gap: 20px; min-width: 0; }
  #tab-orders-new .onw-blk { background: var(--td-surface); border: 1px solid var(--td-border); border-radius: 14px; padding: 18px 20px; }
  #tab-orders-new .onw-blk[hidden] { display: none; }
  #tab-orders-new .onw-blk-h { font-size: 13px; font-weight: 500; letter-spacing: .04em; color: var(--td-text2); margin: 0 0 12px; }
  #tab-orders-new .onw-cap { font-size: 13px; color: var(--td-text2); margin: 10px 0 0; text-wrap: pretty; }

  #tab-orders-new .onw-f { display: flex; flex-direction: column; gap: 6px; min-width: 0; }
  #tab-orders-new .onw-f > label { font-size: 13px; color: var(--td-text2); }
  #tab-orders-new .onw-f.onw-wide { grid-column: 1 / -1; }
  #tab-orders-new .onw-in {
    height: 38px; width: 100%; border: 1px solid var(--td-border); border-radius: 8px;
    background: var(--td-paper); padding: 0 12px; font-family: inherit; font-size: 14px; color: var(--td-text);
  }
  #tab-orders-new .onw-in::placeholder { color: var(--td-text2); }
  #tab-orders-new .onw-num { font-variant-numeric: tabular-nums; }
  #tab-orders-new .onw-in:focus-visible { outline: 2px solid var(--td-dusk); outline-offset: 1px; border-color: var(--td-dusk); }
  #tab-orders-new .onw-row2 { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; }
  #tab-orders-new .onw-row3 { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 14px; }
  #tab-orders-new .onw-selw { position: relative; display: flex; }
  /* background-image: none kills the BASE select's own chevron data-URI — the
     wrapper draws a live-currentColor one, and without this every select on
     the page carries two arrows (the base one is also a hardcoded grey that
     ignores the theme). */
  #tab-orders-new .onw-selw select { appearance: none; background-image: none; padding-right: 34px; cursor: pointer; }
  #tab-orders-new .onw-selw .onw-ico { position: absolute; right: 11px; top: 50%; transform: translateY(-50%); color: var(--td-text2); pointer-events: none; }
  #tab-orders-new .onw-srch { position: relative; display: flex; }
  #tab-orders-new .onw-srch .onw-ico { position: absolute; left: 12px; top: 50%; transform: translateY(-50%); color: var(--td-text2); }
  #tab-orders-new .onw-srch .onw-in { padding-left: 36px; }

  /* Customer — results list, the two no-match offers, guest chip + fields. */
  #tab-orders-new .onw-res { margin-top: 8px; border: 1px solid var(--td-border); border-radius: 10px; overflow: hidden; background: var(--td-paper); }
  #tab-orders-new .onw-res button {
    display: flex; width: 100%; align-items: center; gap: 12px; padding: 10px 13px;
    background: none; border: 0; border-bottom: 1px solid var(--td-border);
    font-family: inherit; font-size: 14px; text-align: left; cursor: pointer; color: var(--td-text);
  }
  #tab-orders-new .onw-res button:last-child { border-bottom: 0; }
  #tab-orders-new .onw-res button:hover { background: rgba(var(--td-heat-rgb), .04); }
  #tab-orders-new .onw-res .nm { font-weight: 500; }
  #tab-orders-new .onw-res .ph { font-size: 13px; color: var(--td-text2); font-variant-numeric: tabular-nums; }
  #tab-orders-new .onw-res .ct { margin-left: auto; font-size: 13px; color: var(--td-text2); font-variant-numeric: tabular-nums; }
  #tab-orders-new .onw-offer {
    margin-top: 8px; border: 1px solid var(--td-border); border-radius: 10px; background: var(--td-raised);
    padding: 13px 14px; display: flex; flex-direction: column; gap: 10px; align-items: flex-start;
  }
  #tab-orders-new .onw-offer p { font-size: 13px; color: var(--td-text2); margin: 0; text-wrap: pretty; }
  #tab-orders-new .onw-chiprow { margin-bottom: 14px; }
  #tab-orders-new .onw-chip {
    display: inline-flex; align-items: center; gap: 4px; height: 30px; padding: 0 4px 0 12px;
    border: 1px solid var(--td-border); border-radius: 999px; background: var(--td-raised);
    font-size: 13px; font-weight: 500;
  }
  #tab-orders-new .onw-chip .x { width: 22px; height: 22px; border: 0; border-radius: 999px; background: none; color: var(--td-text2); cursor: pointer; display: grid; place-items: center; padding: 0; }
  #tab-orders-new .onw-chip .x:hover { background: rgba(var(--td-heat-rgb), .08); color: var(--td-text); }
  #tab-orders-new .onw-chip .x .onw-ico { width: 13px; height: 13px; }
  #tab-orders-new .onw-ig { display: flex; align-items: stretch; height: 38px; border: 1px solid var(--td-border); border-radius: 8px; background: var(--td-paper); overflow: hidden; }
  #tab-orders-new .onw-ig > span { display: flex; align-items: center; padding: 0 11px; font-size: 14px; color: var(--td-text2); background: var(--td-raised); border-right: 1px solid var(--td-border); }
  #tab-orders-new .onw-ig input { flex: 1; min-width: 0; border: 0; background: none; padding: 0 12px; font-family: inherit; font-size: 14px; color: var(--td-text); font-variant-numeric: tabular-nums; }
  #tab-orders-new .onw-ig input:focus-visible { outline: 2px solid var(--td-dusk); outline-offset: -2px; }

  /* The phone-match note — amber for a match/shared number, Brick when the
     owner is blocked. Never hidden: the save attaches to them regardless. */
  #tab-orders-new .onw-note { display: flex; gap: 9px; align-items: flex-start; margin-top: 10px; padding: 10px 12px; border-radius: 9px; font-size: 13px; line-height: 1.5; text-wrap: pretty; }
  #tab-orders-new .onw-note b { font-weight: 500; }
  #tab-orders-new .onw-note .onw-ico { margin-top: 1px; }
  #tab-orders-new .onw-note.amber { background: var(--td-amber-tint); color: var(--td-amber-ink); }
  #tab-orders-new .onw-note.brick { background: var(--td-brick-tint); color: var(--td-brick-ink); }
  #tab-orders-new .onw-cb { display: flex; align-items: center; gap: 9px; margin-top: 14px; font-family: inherit; font-size: 14px; color: var(--td-text); background: none; border: 0; padding: 0; cursor: pointer; }
  #tab-orders-new .onw-cb i { width: 18px; height: 18px; border: 1.5px solid var(--td-border); border-radius: 5px; background: var(--td-paper); display: grid; place-items: center; color: transparent; flex: 0 0 auto; }
  #tab-orders-new .onw-cb i .onw-ico { width: 12px; height: 12px; }
  #tab-orders-new .onw-cb.on i { background: var(--td-text); border-color: var(--td-text); color: var(--td-paper); }

  /* Products — §1.13a rule 1: the list scrolls, its header band pins to the
     card's own scrollport. The card clips with clip-path, NOT overflow —
     overflow would turn the card into the scrollport and park the band on
     row one. Band values are the product-wide §1.13a table: --td-header,
     13px Inter 500 Ink, 10px 12px padding, opaque, z-index 5. */
  #tab-orders-new .onw-empty { margin-top: 12px; border: 1px solid var(--td-border); border-radius: 12px; background: var(--td-paper); padding: 22px; text-align: center; font-size: 14px; color: var(--td-text2); }
  #tab-orders-new .onw-tw { margin-top: 12px; border: 1px solid var(--td-border); border-radius: 12px; background: var(--td-paper); clip-path: inset(0 round 12px); }
  #tab-orders-new .onw-tscroll { max-height: 344px; overflow: hidden auto; }
  #tab-orders-new .onw-tbl { width: 100%; table-layout: fixed; border-collapse: collapse; font-size: 14px; }
  #tab-orders-new .onw-tbl th {
    position: sticky; top: 0; z-index: 5;
    background: var(--td-header); color: var(--td-text);
    font-size: 13px; font-weight: 500; text-align: left;
    padding: 10px 12px; border-bottom: 1px solid var(--td-border);
  }
  #tab-orders-new .onw-tbl td { padding: 10px 12px; border-bottom: 1px solid var(--td-border); vertical-align: middle; }
  #tab-orders-new .onw-tbl tr:last-child td { border-bottom: 0; }
  #tab-orders-new .onw-tbl th.mo, #tab-orders-new .onw-tbl td.mo { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; }
  #tab-orders-new .onw-tbl th.mo.p { width: 104px; }
  #tab-orders-new .onw-tbl th.mo.t { width: 120px; }
  #tab-orders-new .onw-tbl th.qt, #tab-orders-new .onw-tbl td.qt { width: 132px; }
  #tab-orders-new .onw-tbl td.rm { width: 44px; text-align: right; }
  #tab-orders-new .onw-tbl td.pd { min-width: 0; }
  #tab-orders-new .onw-tbl .pn { display: block; font-weight: 500; line-height: 1.4; text-wrap: pretty; }
  #tab-orders-new .onw-tbl .ps { display: block; font-size: 12px; color: var(--td-text2); font-variant-numeric: tabular-nums; margin-top: 1px; }
  #tab-orders-new .onw-tbl td.rm button { width: 28px; height: 28px; border: 0; background: none; border-radius: 7px; color: var(--td-text2); cursor: pointer; display: grid; place-items: center; padding: 0; }
  #tab-orders-new .onw-tbl td.rm button:hover { background: var(--td-brick-tint); color: var(--td-brick-ink); }
  #tab-orders-new .onw-step { display: inline-flex; align-items: center; border: 1px solid var(--td-border); border-radius: 8px; background: var(--td-surface); overflow: hidden; }
  #tab-orders-new .onw-step button { width: 28px; height: 32px; border: 0; background: none; font-family: inherit; font-size: 15px; color: var(--td-text2); cursor: pointer; padding: 0; }
  #tab-orders-new .onw-step button:hover { background: rgba(var(--td-heat-rgb), .06); color: var(--td-text); }
  #tab-orders-new .onw-step span { min-width: 30px; text-align: center; font-variant-numeric: tabular-nums; font-weight: 500; }
  #tab-orders-new .onw-step.bad { border-color: var(--td-brick); box-shadow: 0 0 0 1px var(--td-brick); }
  /* Frame G: the error lands NEXT TO the control that failed and stays. */
  #tab-orders-new .onw-inerr { display: flex; align-items: center; gap: 5px; margin-top: 6px; font-size: 12px; line-height: 1.35; color: var(--td-brick-ink); }
  #tab-orders-new .onw-inerr .onw-ico { width: 13px; height: 13px; }

  /* Inline errors (§1.12, frame G) — the message lands next to the control that
     failed and STAYS until it is fixed. Never a toast: a toast confirms, and a
     failure the operator can miss is not an error state. */
  #tab-orders-new .onw-err {
    display: flex; gap: 8px; align-items: flex-start;
    margin-top: 12px; padding: 10px 12px; border-radius: 9px;
    background: var(--td-brick-tint); color: var(--td-brick-ink);
    font-size: 13px; line-height: 1.5; text-wrap: pretty;
  }
  #tab-orders-new .onw-err[hidden] { display: none; }
  #tab-orders-new .onw-err .onw-ico { margin-top: 1px; color: var(--td-brick-ink); }
  /* The block that owns a live error takes a Brick edge, so the card is findable
     from a scroll away rather than only the message inside it. */
  #tab-orders-new .onw-blk.onw-bad { border-color: var(--td-brick); }

  /* Notes — the two containers must not read as one pair of identical fields. */
  #tab-orders-new .onw-notes { display: flex; flex-direction: column; gap: 14px; }
  #tab-orders-new .onw-cnote, #tab-orders-new .onw-snote { border: 1px solid var(--td-border); border-radius: 12px; padding: 12px 13px; }
  #tab-orders-new .onw-cnote { background: var(--td-paper); }
  #tab-orders-new .onw-snote { background: var(--td-raised); border-style: dashed; }
  #tab-orders-new .onw-nh { display: flex; align-items: center; gap: 8px; margin-bottom: 9px; }
  #tab-orders-new .onw-nh .t { font-size: 14px; font-weight: 500; }
  #tab-orders-new .onw-pill { margin-left: auto; font-size: 12px; font-weight: 500; background: var(--td-amber-tint); color: var(--td-amber-ink); border-radius: 999px; padding: 2px 9px; }
  #tab-orders-new .onw-int { margin-left: auto; font-size: 12px; color: var(--td-text2); }
  #tab-orders-new .onw-notes textarea { width: 100%; border: 1px solid var(--td-border); border-radius: 8px; background: var(--td-surface); padding: 9px 11px; font-family: inherit; font-size: 14px; line-height: 1.55; color: var(--td-text); resize: vertical; box-sizing: border-box; }
  #tab-orders-new .onw-notes textarea::placeholder { color: var(--td-text2); }
  #tab-orders-new .onw-notes textarea:focus-visible { outline: 2px solid var(--td-dusk); outline-offset: 1px; }

  /* Summary — a running total, not a preview. Sticky against .onw-scroll
     (no ancestor between takes an overflow, same §1.13a trap). */
  #tab-orders-new .onw-right { position: sticky; top: 0; }
  #tab-orders-new .onw-sum { background: var(--td-surface); border: 1px solid var(--td-border); border-radius: 14px; padding: 18px 20px; }
  #tab-orders-new .onw-sr { display: flex; align-items: baseline; justify-content: space-between; gap: 14px; font-size: 14px; padding: 7px 0; }
  #tab-orders-new .onw-sr .v { font-variant-numeric: tabular-nums; }
  #tab-orders-new .onw-sr.onw-tot { border-top: 1px solid var(--td-border); margin-top: 6px; padding-top: 12px; font-weight: 500; }
  #tab-orders-new .onw-sr.onw-tot .v { font-size: 18px; }

  /* Narrow windows: the summary drops below the form; nothing scrolls sideways. */
  @media (max-width: 1100px) {
    #tab-orders-new .onw-grid { display: block; }
    #tab-orders-new .onw-right { position: static; margin-top: 20px; }
  }

  /* The Orders drawer hosts the existing .order-card chrome unchanged
     — but reset the card's outer chrome since the drawer already
     frames it. */
  .side-drawer .order-card {
    background: transparent;
    border: 0;
    border-radius: 0;
    padding: 0;
    margin-bottom: 0;
  }
  /* Floating close — anchored to the drawer aside (not the scrolling
     body) so it stays visible no matter how far down you've scrolled.
     Mouse counterpart to the ESC key. Used by drawers without a head
     bar (Orders, Customers); editor drawers put their close inside
     .drawer-head instead. */
  /* Option B "bare glyph" — Tacet keeps the mark, drops the chrome: no disc,
     no border, no shadow at rest. A soft raised-surface halo fades in only on
     hover (isolate + ::after behind the glyph), and the motion settles rather
     than bounces. Replaces the old black-shadowed white FAB that fought the
     paper skin. */
  .side-drawer .drawer-close {
    position: absolute;
    top: 14px; right: 14px;
    width: 32px; height: 32px;
    display: inline-flex; align-items: center; justify-content: center;
    border-radius: 999px;
    background: transparent;
    border: 0;
    color: var(--text-2);
    cursor: pointer;
    z-index: 5;
    isolation: isolate;
    transition: color var(--t-fast) ease;
  }
  .side-drawer .drawer-close::after {
    content: ""; position: absolute; inset: 0; border-radius: 999px;
    background: var(--bg-elev-1); opacity: 0; z-index: -1;
    transition: opacity var(--t-fast) ease;
  }
  .side-drawer .drawer-close:hover { color: var(--text-1); }
  .side-drawer .drawer-close:hover::after { opacity: 1; }
  .side-drawer .drawer-close svg { width: 16px; height: 16px; }
  /* The Recap drawer is a short peek with a footer Close — the floating round ×
     would be a redundant second exit and the shadowed pill fights the flat
     Tacet paper skin, so drop it here. */
  #db-activity-drawer .drawer-close { display: none; }
  /* Mobile-only back chevron in the drawer app-bar; hidden on desktop where the
     floating round × is used instead. */
  .side-drawer .drawer-back { display: none; }
  @media (max-width: 720px) {
    .side-drawer { width: 100vw; min-width: 0; border-left: 0; }
    .side-drawer.dw-wide { width: 100vw; }
    .side-drawer.dw-compact { width: 100vw; min-width: 0; }
  }
  .prod-link {
    color: inherit;
    text-decoration: none;
    display: block;
    transition: opacity var(--t-fast) ease;
  }
  .prod-link:hover {
    opacity: 0.65;
    text-decoration: underline;
    text-decoration-thickness: 1px;
    text-underline-offset: 3px;
  }
  .order-card {
    background: var(--bg-pane);
    border: 1px solid var(--line);
    border-radius: var(--r-xl);
    padding: var(--s5) var(--s5) var(--s4);
    margin-bottom: var(--s4);
  }
  .order-head {
    display: flex; align-items: flex-start; justify-content: space-between;
    gap: var(--s3);
    margin-bottom: var(--s4);
  }
  .order-head .od-head-main { flex: 1 1 auto; min-width: 0; }
  /* Primary status actions, pulled up next to the pill (see renderOpenOrder).
     Sits top-right of the header, just left of the ⋯ View popover; the eyebrow
     line keeps the order# row from feeling crowded. */
  .order-head .od-head-tx { padding-top: 0; flex: 0 0 auto; }
  /* Wrap so a long pill set (Paid + Items added + refund-approved + …) drops to
     a second line instead of overflowing into / under the header action button
     (Start preparing), which read as the two overlapping. */
  .order-head .title-row { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s2); margin-bottom: 4px; }
  .order-head .title-row b { font-size: 18px; font-weight: 500; font-family: var(--f-mono); letter-spacing: .02em; }
  /* Mono caps eyebrow under the order number — the design's "ORDER PLACED"
     micro-label, keeping the real placed timestamp. */
  .od-eyebrow {
    font-family: var(--f-mono); font-size: 9px; font-weight: 500;
    letter-spacing: .24em; text-transform: uppercase; color: var(--text-3);
  }
  /* Metadata block is a contained card so it reads as structured, matching
     the shipment + total cards below it (was a borderless grid floating on
     the drawer field, which made the top half look flat/unanchored). */
  .order-detail {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    gap: var(--s4) var(--s5);
    margin-bottom: var(--s4);
    background: var(--bg-pane);
    border: 1px solid var(--line);
    border-radius: var(--r-lg);
    padding: var(--s4) var(--s5);
    box-shadow: 0 1px 2px rgba(0,0,0,.04), 0 4px 14px rgba(0,0,0,.05);
  }
  .order-detail .full { grid-column: 1 / -1; }
  /* Info labels — mono, wider tracking, to match the design's field labels. */
  .order-detail .kicker {
    font-family: var(--f-mono);
    font-size: 10px; font-weight: 500;
    letter-spacing: .2em; text-transform: uppercase;
    color: var(--text-3); margin-bottom: 5px;
    display: inline-flex; align-items: center; gap: 6px;
  }
  /* Tiny copy icon next to a kicker label — clicks copyToClipboard() with
     the text inside data-copy. Same look + feel as the tracking-number
     copy affordance staff already know. */
  .copy-mini {
    display: inline-flex; align-items: center; justify-content: center;
    width: 18px; height: 18px;
    border: 1px solid var(--line);
    border-radius: 4px;
    background: var(--bg-elev-1);
    color: var(--text-3);
    cursor: pointer;
    transition: color var(--t-fast), background var(--t-fast), border-color var(--t-fast);
  }
  .copy-mini:hover { color: var(--text-1); background: var(--bg-elev-2); border-color: var(--line-strong); }
  .copy-mini:active { transform: translateY(1px); }
  .copy-mini svg { width: 11px; height: 11px; }
  .order-detail .val b { color: var(--text-1); font-weight: 600; }
  .order-detail .sub { color: var(--text-3); font-size: 12px; margin-top: 2px; }
  @media (max-width: 720px) { .order-detail { grid-template-columns: 1fr; } }
  .order-items table { margin: 0; }
  .order-items th { background: var(--bg-pane); }

  /* Stock reservations — collapsed by default (operators rarely need it open).
     Native <details>; we hide the default disclosure marker and supply our own
     caret that rotates 90° when the section is expanded. */
  .resv-details > summary.resv-summary {
    display: flex; align-items: center; gap: 8px;
    cursor: pointer; list-style: none; outline: none;
    user-select: none;
  }
  .resv-details > summary.resv-summary::-webkit-details-marker { display: none; }
  .resv-details[open] > summary.resv-summary { margin-bottom: var(--s2); }
  .resv-details .resv-caret {
    display: inline-block; color: var(--text-3);
    transition: transform .15s var(--ease-out, ease);
  }
  .resv-details[open] .resv-caret { transform: rotate(90deg); }
  .resv-details .resv-count {
    margin-left: auto;
    font-family: var(--f-mono); font-weight: 700; letter-spacing: 0;
    color: var(--text-3); background: var(--bg-pane);
    border: 1px solid var(--line); border-radius: 999px;
    padding: 1px 8px; font-size: 11px;
  }
  @media (prefers-reduced-motion: reduce) {
    .resv-details .resv-caret { transition: none; }
  }
  .transitions {
    display: flex; gap: 6px; flex-wrap: wrap;
    padding-top: var(--s2);
  }

  /* ─── Items table: SKU line + Pack badges + Sort chips ─── */
  .sku-line {
    font-family: var(--f-mono);
    font-size: 10px;
    color: var(--text-3);
    margin-top: 4px;
    letter-spacing: .02em;
  }
  .pack-cell { white-space: nowrap; }
  .pack-badge {
    display: inline-flex; align-items: center; gap: 4px;
    padding: 2px 8px;
    border-radius: 999px;
    font-size: 10px; font-weight: 600;
    letter-spacing: .04em;
    border: 1px solid;
    margin-right: 4px;
    line-height: 1.6;
    white-space: nowrap;
  }
  /* Strict Tea + Brass: shipping modes (air/land) = Aged Brass, flexible
     (any) = neutral. Mode is distinguished by icon + label, not by an
     off-palette colour. (Ice / cold-chain pack-type was dropped.) */
  .pack-badge.any   { background: var(--bg-elev-1); color: var(--text-3); border-color: var(--line); }
  .pack-badge.air   { background: rgba(201, 149, 74, .14); color: var(--brass); border-color: rgba(201, 149, 74, .3); }
  .pack-badge.land  { background: rgba(201, 149, 74, .14); color: var(--brass); border-color: rgba(201, 149, 74, .3); }
  /* Supplier-direct (drop-ship) — Tea Green, off the brass shipping palette so
     "the supplier ships this, don't pack it" reads as its own distinct flag. */
  .pack-badge.supplier { background: rgba(163, 180, 159, .16); color: var(--accent); border-color: rgba(163, 180, 159, .34); }

  /* ─── Order total banner — hero strip below the drawer header. Lifted to the
     same elevated card surface as the metadata block beneath it (was a recessed
     bg-app strip with an invisible border, so the hero number read flatter than
     the cards under it). ─── */
  .order-total-banner {
    display: flex; align-items: baseline; justify-content: space-between;
    gap: var(--s4);
    padding: var(--s4) var(--s5);
    background: var(--bg-pane);
    border: 1px solid var(--line);
    border-radius: var(--r-lg);
    margin-bottom: var(--s5);
    box-shadow: 0 1px 2px rgba(0,0,0,.04), 0 4px 14px rgba(0,0,0,.05);
  }
  .order-total-banner .otb-label {
    font-family: var(--f-mono); font-size: 10px; font-weight: 500;
    letter-spacing: .28em; text-transform: uppercase; color: var(--text-3);
  }
  .order-total-banner .otb-sub {
    font-family: var(--f-mono); font-size: 11px; color: var(--text-3);
    margin-top: 4px; letter-spacing: .02em;
  }
  .order-total-banner .otb-amount {
    font-size: 40px; font-weight: 500; color: var(--text-1);
    line-height: 1; letter-spacing: -.02em; white-space: nowrap;
  }
  @media (max-width: 560px) {
    .order-total-banner .otb-amount { font-size: 30px; }
  }

  /* ─── Items rendered as a numbered packing manifest (01, 02…) ───
     Sits inside a .shipment-card, so it needs no card chrome of its own —
     just hairline-separated rows. Line numbers in Aged Brass. */
  .manifest { display: flex; flex-direction: column; }
  .manifest .mf-row {
    display: flex; align-items: flex-start; gap: var(--s4);
    padding: var(--s3) 0;
    border-bottom: 1px solid var(--line-soft);
  }
  .manifest .mf-row:first-child { padding-top: var(--s1); }
  .manifest .mf-row:last-child  { border-bottom: none; padding-bottom: 0; }
  .manifest .mf-num {
    font-family: var(--f-mono); font-size: 11px; color: var(--brass);
    letter-spacing: .04em; flex-shrink: 0; min-width: 22px; padding-top: 2px;
  }
  .manifest .mf-body { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 4px; }
  .manifest .mf-top {
    display: flex; align-items: baseline; justify-content: space-between; gap: var(--s3);
  }
  .manifest .mf-name { font-size: 13px; font-weight: 500; color: var(--text-1); }
  .manifest .prod-link { text-decoration: none; color: inherit; min-width: 0; }
  .manifest .prod-link:hover .mf-name { text-decoration: underline; }
  .manifest .mf-total { font-size: 14px; font-weight: 600; color: var(--text-1); white-space: nowrap; }
  /* Refunded line: a refund pill (warm red) + a struck, dimmed row so a fully
     refunded item reads "do not ship" at a glance. Partial refunds keep the row
     normal and lean on the "n of q refunded" pill instead. */
  .pack-badge.refunded {
    background: rgba(176, 64, 56, .12); color: #b04038; border-color: rgba(176, 64, 56, .30);
    display: inline-flex; align-items: center; gap: 4px;
  }
  .pack-badge.refunded .rf-ic { flex: none; }
  .refunded-row { opacity: .6; }
  .refunded-row .mf-name, .refunded-row .mf-total {
    text-decoration: line-through; text-decoration-color: rgba(176, 64, 56, .7);
  }
  .refunded-row .oi-thumb { filter: grayscale(1); }
  .manifest .mf-meta { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
  .manifest .mf-zh { font-family: var(--f-sans); font-size: 11px; font-weight: 300; color: var(--text-3); }
  /* SKU + quantity are the packer's two critical fields — shared mono chips
     used identically across all item layouts (manifest / table / cards), so
     they read the same in every view. Unit price stays quiet; a multi-unit
     qty goes brass so a >1 line can't be shipped short. */
  .item-sku {
    font-family: var(--f-mono); font-size: 11px; font-weight: 600;
    color: var(--text-1); letter-spacing: .04em; white-space: nowrap;
    background: var(--bg-elev-1); border: 1px solid var(--line);
    border-radius: var(--r-sm); padding: 1px 7px;
  }
  .item-qty {
    display: inline-block;
    font-family: var(--f-mono); font-size: 12px; font-weight: 700;
    color: var(--text-1); font-variant-numeric: tabular-nums; letter-spacing: .02em;
    white-space: nowrap;
    background: var(--bg-elev-1); border: 1px solid var(--line);
    border-radius: var(--r-sm); padding: 1px 8px;
  }
  .item-qty.qty-multi {
    color: var(--brass);
    background: rgba(201, 149, 74, .10);
    border-color: rgba(201, 149, 74, .40);
  }
  .item-unit { font-family: var(--f-mono); font-size: 10.5px; color: var(--text-3); letter-spacing: .03em; }
  .manifest .mf-packs { margin-left: auto; display: inline-flex; align-items: center; }
  .manifest .mf-packs .pack-badge { margin-right: 0; margin-left: 4px; }

  /* Per-item thumbnail (order detail) — shared across all three item layouts
     (manifest / table / cards), giving the operator a visual cue of what was
     ordered. (Quantity emphasis lives in the shared .item-qty chip above.) */
  .oi-thumb {
    flex-shrink: 0; width: 40px; height: 40px; border-radius: var(--r-sm);
    object-fit: cover; background: var(--bg-elev-2);
    border: 1px solid var(--line-soft); display: inline-block; vertical-align: middle;
  }
  .oi-thumb.is-empty { background: var(--bg-app); }

  .sort-chips {
    display: flex; flex-wrap: wrap; gap: 6px;
    align-items: center;
    margin: 0 0 var(--s2);
  }
  .sort-chips .sort-label {
    font-size: 10px; font-weight: 600; letter-spacing: .1em;
    text-transform: uppercase; color: var(--text-3);
    margin-right: 4px;
  }
  .sort-chip {
    display: inline-flex; align-items: center; gap: 5px;
    padding: 4px 10px;
    border-radius: 999px;
    font-size: 11px; font-weight: 600;
    letter-spacing: .03em;
    border: 1px solid var(--line);
    background: var(--bg-elev-1);
    color: var(--text-3);
    cursor: pointer;
    transition: background var(--t-fast) ease, color var(--t-fast) ease, border-color var(--t-fast) ease;
    user-select: none;
  }
  .sort-chip:hover { background: var(--bg-elev-2); color: var(--text-2); }
  .sort-chip.active { background: rgba(163, 180, 159, .12); color: var(--accent-hi); border-color: rgba(163, 180, 159, .35); }

  /* ─── Shipment cards (1 or 2 per order) ─── */
  .shipment-card {
    background: var(--bg-app);
    border: 1px solid var(--line);
    border-radius: var(--r-lg);
    padding: var(--s4) var(--s4) var(--s3);
    margin-bottom: var(--s3);
  }
  /* Neutral card border in every mode — the design keeps the shipment
     group quiet; the mode reads from the header icon + label. */
  .shipment-card.mode-air,
  .shipment-card.mode-land,
  .shipment-card.mode-local { border-color: var(--line); }
  /* Supplier (drop-ship) parcel — a Tea-Green left rail so staff spot the one
     parcel they don't ship themselves (they paste the supplier's waybill). */
  .shipment-card.mode-supplier {
    border-color: var(--line);
    border-left: 3px solid var(--accent);
  }
  /* Hoisted tracking — once a parcel ships, its carrier tracking card is
     lifted to the top of the order drawer (under the status actions) so the
     operator can add a waybill + watch status without scrolling to the items
     section. One card per shipped parcel; multi-parcel orders get a label. */
  .od-tracking-top { margin: var(--s3) 0 var(--s4); }
  .od-trk-parcel {
    margin: var(--s3) 0 var(--s2);
    font-size: 10px; font-weight: 600; letter-spacing: .1em;
    text-transform: uppercase; color: var(--text-3);
  }
  .od-trk-parcel:first-child { margin-top: 0; }
  /* Shipping mode folded into the parcel label ("Parcel 2 · Land") — slightly
     darker than the label so it reads as its attribute, not a second heading. */
  .od-trk-parcel-mode { color: var(--text-2); }
  .shipment-head {
    display: flex; align-items: center; gap: var(--s3);
    margin-bottom: var(--s3);
  }
  /* Flat, neutral group label (design's quiet shipment header) — a soft
     inset chip rather than a colour-coded pill. */
  .shipment-head .mode-pill {
    display: inline-flex; align-items: center; gap: 5px;
    padding: 4px 11px; border-radius: var(--r-sm);
    font-size: 11px; font-weight: 600;
    letter-spacing: .04em;
    background: var(--bg-elev-1);
    color: var(--text-1);
    border: 1px solid var(--line);
  }
  .shipment-head .mode-pill.mode-air,
  .shipment-head .mode-pill.mode-land,
  .shipment-head .mode-pill.mode-local { background: var(--bg-elev-1); color: var(--text-1); border-color: var(--line); }
  /* Supplier-direct pill — Tea-Green accent so the drop-ship parcel stands out
     from our own (neutral) parcels in the drawer. */
  .shipment-head .mode-pill.mode-supplier {
    background: rgba(163, 180, 159, .14);
    color: var(--accent);
    border-color: rgba(163, 180, 159, .34);
  }
  /* Units-to-ship count — pushed to the right edge and given real weight so
     the packer reads the total at a glance ("4 lines · 6 units"). */
  .shipment-head .count {
    margin-left: auto;
    font-family: var(--f-mono); font-size: 11px; font-weight: 600;
    color: var(--text-2); letter-spacing: .04em;
  }
  /* Route note — brass-bordered pill (the design's shipment route pill). */
  .outside-gz-note {
    display: inline-flex; align-items: center; gap: 6px;
    padding: 5px 12px;
    background: rgba(201, 149, 74, .10);
    color: var(--brass);
    border: 1px solid rgba(201, 149, 74, .25);
    border-radius: var(--r-md);
    font-size: 12px;
    font-weight: 500;
    margin-bottom: var(--s3);
  }
  /* Split-package suggestion banner — green, positive (saves money).
     Sits above Items in the Orders drawer when buildShipmentSuggestion
     returns eligible:true. Sized to be noticed without dominating. */
  .split-suggestion {
    display: flex; flex-direction: column; gap: var(--s2);
    padding: var(--s3) var(--s3);
    background: rgba(123, 189, 59, .10);
    border: 1px solid rgba(123, 189, 59, .35);
    border-radius: var(--r-md);
    margin-bottom: var(--s3);
  }
  .split-suggestion .ss-head {
    display: flex; align-items: center; justify-content: space-between;
    gap: var(--s3); flex-wrap: wrap;
  }
  .split-suggestion .ss-title {
    display: flex; align-items: baseline; gap: var(--s2);
    font-size: 13px; font-weight: 700; color: var(--st-delivered);
  }
  .split-suggestion .ss-title .save {
    font-family: var(--f-mono);
    background: rgba(123, 189, 59, .22);
    padding: 2px 8px; border-radius: 999px;
    font-size: 12px;
  }
  .split-suggestion .ss-subtitle {
    font-size: 11px; font-weight: 500; color: var(--text-2);
  }
  .split-suggestion .ss-actions { display: flex; gap: var(--s2); }
  .split-suggestion .ss-detail {
    display: grid; grid-template-columns: 1fr 1fr; gap: var(--s3);
    font-size: 11px; color: var(--text-2);
  }
  .split-suggestion .ss-bucket {
    display: flex; flex-direction: column; gap: 2px;
    padding: var(--s2) var(--s3);
    background: rgba(255, 255, 255, .03);
    border: 1px solid rgba(255, 255, 255, .06);
    border-radius: var(--r-sm);
  }
  .split-suggestion .ss-bucket .bk-head {
    display: flex; justify-content: space-between; align-items: baseline;
    font-weight: 600; color: var(--text-1);
  }
  .split-suggestion .ss-bucket .bk-head .bk-cost {
    font-family: var(--f-mono); color: var(--text-2); font-weight: 500;
  }
  .split-suggestion .ss-bucket.air  .bk-head { color: #b2cfed; }
  .split-suggestion .ss-bucket.land .bk-head { color: #f2ca6c; }
  .split-suggestion .ss-bucket .bk-meta { font-size: 10.5px; color: var(--text-3); }
  @media (max-width: 640px) {
    .split-suggestion .ss-detail { grid-template-columns: 1fr; }
  }

  /* ═══════════════════════════════════════════════════════════════════
     SNIPPETS — mini-app copy editor
     ═══════════════════════════════════════════════════════════════════ */
  .snip-group { margin-bottom: var(--s5); }
  .snip-group h3 {
    font-size: 11px; font-weight: 600;
    letter-spacing: .12em; text-transform: uppercase;
    color: var(--text-3);
    margin-bottom: var(--s3);
  }
  .snip-row {
    background: var(--bg-pane);
    border: 1px solid var(--line-soft);
    border-radius: var(--r-md);
    padding: var(--s3) var(--s4);
    margin-bottom: 8px;
  }
  .snip-row .label { font-size: 13px; font-weight: 500; color: var(--text-1); margin-bottom: 2px; }
  .snip-row .help { font-size: 11px; color: var(--text-3); margin-bottom: 8px; line-height: 1.4; }

  /* Tea-green toggle switch — a real on/off slider (replaces bare checkboxes
     where a stateful toggle reads better than a tickbox, e.g. Holiday mode). */
  .fc-switch { display: inline-flex; align-items: center; gap: 10px; cursor: pointer; user-select: none; }
  .fc-switch input { position: absolute; opacity: 0; width: 0; height: 0; }
  .fc-switch-track {
    position: relative; flex-shrink: 0;
    width: 38px; height: 20px; border-radius: 999px;
    background: var(--bg-elev-2); border: 1px solid var(--line);
    transition: background var(--t-fast) ease, border-color var(--t-fast) ease;
  }
  .fc-switch-track::after {
    content: ''; position: absolute; top: 2px; left: 2px;
    width: 14px; height: 14px; border-radius: 50%;
    background: var(--text-2);
    transition: transform var(--t-fast) ease, background var(--t-fast) ease;
  }
  .fc-switch input:checked + .fc-switch-track { background: var(--accent); border-color: var(--accent); }
  .fc-switch input:checked + .fc-switch-track::after { transform: translateX(18px); background: var(--accent-ink); }
  .fc-switch input:focus-visible + .fc-switch-track { box-shadow: 0 0 0 3px var(--ring); }
  .snip-row .key  { font-family: var(--f-mono); font-size: 10px; color: var(--text-4); }
  .snip-row .pair { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
  .snip-row .pair .lang-wrap {
    display: flex; align-items: stretch;
    border: 1px solid var(--line);
    background: var(--bg-input);
    border-radius: var(--r-md);
    overflow: hidden;
    transition: border-color var(--t-fast) ease, box-shadow var(--t-fast) ease;
  }
  .snip-row .pair .lang-wrap:focus-within {
    border-color: rgba(163, 180, 159, .45);
    box-shadow: 0 0 0 3px var(--ring);
  }
  .snip-row .pair .lang-tag {
    padding: 0 10px;
    background: var(--bg-elev-1);
    color: var(--text-3);
    font-size: 10px; font-weight: 700; letter-spacing: .08em;
    border-right: 1px solid var(--line);
    display: flex; align-items: center;
    min-width: 36px;
  }
  .snip-row .pair input {
    flex: 1; border: 0 !important; background: transparent !important;
    padding: 8px 10px !important;
    border-radius: 0 !important; box-shadow: none !important;
  }
  .snip-row .pair input:focus { box-shadow: none !important; }
  @media (max-width: 700px) { .snip-row .pair { grid-template-columns: 1fr; } }

  /* ═══════════════════════════════════════════════════════════════════
     TOAST + UTILS
     ═══════════════════════════════════════════════════════════════════ */
  .toast {
    position: fixed; bottom: 24px; left: 50%; transform: translate(-50%, 12px);
    padding: 10px 18px;
    background: var(--bg-elev-2);
    border: 1px solid var(--line-strong);
    color: var(--text-1);
    font-size: 13px; font-weight: 500;
    border-radius: var(--r-lg);
    box-shadow: var(--sh-3);
    /* Above drawers (200) and modals (300) so confirmations stay visible
       even when a drawer or dialog is open. */
    z-index: 400;
    opacity: 0; pointer-events: none;
    transition: opacity 1.4s ease, transform var(--t-med) var(--ease-out);
  }
  .toast.show { opacity: 1; transform: translate(-50%, 0); }
  .toast.err  { background: rgba(120, 30, 40, .92); border-color: var(--danger); color: #fff; }
  .toast.ok   { background: rgba(46, 74, 34, .96); border-color: var(--success); color: #fff; }

  .empty-state {
    padding: 60px var(--s5);
    text-align: center;
    color: var(--text-3);
  }
  .empty-state .icon { color: var(--text-4); margin-bottom: 12px; }
  .empty-state .icon svg { width: 36px; height: 36px; }
  .empty-state h3 { font-weight: 500; color: var(--text-2); margin-bottom: 6px; }

  .muted { color: var(--text-3); }
  .err   { color: var(--danger); font-weight: 600; }
  .ok    { color: var(--success); font-weight: 600; }

  /* ═══════════════════════════════════════════════════════════════════
     FLASH SALE PRODUCT PICKER — searchable combobox with thumbnails.
     Replaces the native <select>: opens a popover below the trigger,
     filters on type, click-outside closes.
     ═══════════════════════════════════════════════════════════════════ */
  .fs-picker { position: relative; width: 100%; }
  .fs-picker-trigger {
    display: flex; align-items: center; gap: 10px;
    padding: 8px 10px 8px 12px;
    background: var(--bg-input);
    border: 1px solid var(--line);
    border-radius: var(--r-md);
    cursor: pointer;
    transition: border-color var(--t-fast) ease, box-shadow var(--t-fast) ease;
    min-height: 38px;
  }
  .fs-picker-trigger:hover { border-color: var(--line-strong); }
  .fs-picker.open .fs-picker-trigger {
    border-color: rgba(163, 180, 159, .45);
    box-shadow: 0 0 0 3px var(--ring);
  }
  .fs-picker.disabled .fs-picker-trigger {
    cursor: not-allowed; opacity: 0.65;
  }
  .fs-picker-thumb {
    width: 28px; height: 28px; flex-shrink: 0;
    border-radius: 4px;
    background: var(--bg-elev-1);
    overflow: hidden;
    display: flex; align-items: center; justify-content: center;
    color: var(--text-4);
  }
  .fs-picker-thumb img { width: 100%; height: 100%; object-fit: cover; }
  .fs-picker-thumb svg { width: 14px; height: 14px; }
  .fs-picker-label {
    flex: 1; min-width: 0;
    font-size: 13px;
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  }
  .fs-picker-label.empty { color: var(--text-3); }
  .fs-picker-label .sku  { color: var(--text-3); font-family: var(--f-mono); font-size: 11px; margin-left: 6px; }
  .fs-picker-label .pr   { color: var(--text-2); font-family: var(--f-mono); font-size: 11px; margin-left: 6px; }
  .fs-picker-chev {
    color: var(--text-3);
    transition: transform var(--t-fast) ease;
  }
  .fs-picker.open .fs-picker-chev { transform: rotate(180deg); color: var(--text-1); }

  .fs-picker-pop {
    position: absolute; top: calc(100% + 4px); left: 0; right: 0;
    background: var(--bg-pane);
    border: 1px solid var(--line);
    border-radius: var(--r-md);
    box-shadow: 0 12px 32px rgba(0, 0, 0, .45);
    z-index: 50;
    overflow: hidden;
    display: flex; flex-direction: column;
    max-height: 360px;
    animation: zen-pop-in 540ms cubic-bezier(.16, 1, .3, 1);
  }
  /* `display: flex` above overrides the UA stylesheet's `[hidden] { display:none }`,
     so we have to explicitly hide it ourselves. Without this, setting `hidden=true`
     in JS does nothing and the popover stays visible after selecting a product. */
  .fs-picker-pop[hidden] { display: none; }
  /* Edit mode locks the product — make the trigger truly inert so clicks
     can't fire `fsPickerToggle` even via odd event paths. */
  .fs-picker.disabled .fs-picker-trigger { pointer-events: none; }
  .fs-picker-search {
    padding: 10px;
    border-bottom: 1px solid var(--line-soft);
    background: var(--bg-pane);
    position: relative;
  }
  .fs-picker-search input {
    width: 100%;
    padding: 7px 10px 7px 30px !important;
    font-size: 12.5px;
    font-family: var(--f-sans) !important;
    letter-spacing: 0 !important;
  }
  .fs-picker-search svg {
    position: absolute; left: 19px; top: 50%; transform: translateY(-50%);
    width: 13px; height: 13px; color: var(--text-3);
    pointer-events: none;
  }
  .fs-picker-list {
    overflow-y: auto;
    flex: 1;
    padding: 4px;
  }
  .fs-picker-item {
    display: flex; align-items: center; gap: 10px;
    padding: 8px;
    border-radius: 4px;
    cursor: pointer;
    transition: background var(--t-fast) ease;
  }
  .fs-picker-item:hover, .fs-picker-item.focus {
    background: var(--bg-elev-1);
  }
  .fs-picker-item.selected { background: rgba(163, 180, 159, .08); }
  .fs-picker-item .thumb {
    width: 36px; height: 36px; flex-shrink: 0;
    border-radius: 4px;
    background: var(--bg-elev-1);
    overflow: hidden;
    display: flex; align-items: center; justify-content: center;
    color: var(--text-4);
  }
  .fs-picker-item .thumb img { width: 100%; height: 100%; object-fit: cover; }
  .fs-picker-item .thumb svg { width: 16px; height: 16px; }
  .fs-picker-item .info { flex: 1; min-width: 0; }
  .fs-picker-item .name {
    font-size: 13px; color: var(--text-1);
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  }
  .fs-picker-item .sub {
    font-size: 11px; color: var(--text-3);
    font-family: var(--f-mono);
    margin-top: 2px;
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  }
  .fs-picker-item .price {
    font-family: var(--f-mono); font-size: 12px; color: var(--text-2);
    flex-shrink: 0;
  }
  .fs-picker-item.draft .name::after {
    content: 'DRAFT';
    background: var(--bg-elev-2); color: var(--text-3);
    padding: 1px 5px; border-radius: 3px;
    font-size: 9px; letter-spacing: .08em;
    margin-left: 8px; vertical-align: 1px;
  }
  .fs-picker-empty {
    padding: 20px 12px; text-align: center;
    color: var(--text-3); font-size: 12px;
  }

  /* ═══════════════════════════════════════════════════════════════════
     FLASH SALE FORM POLISH — time row, quick-picks, layout
     ═══════════════════════════════════════════════════════════════════ */
  .fs-form-grid {
    display: grid;
    grid-template-columns: 1fr 160px 140px;
    gap: 14px;
    align-items: end;
  }
  @media (max-width: 720px) {
    .fs-form-grid { grid-template-columns: 1fr; }
  }
  .fs-time-grid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 14px;
    margin-top: 14px;
  }
  @media (max-width: 720px) {
    .fs-time-grid { grid-template-columns: 1fr; }
  }
  .fs-dt-wrap {
    display: flex; flex-direction: column; gap: 6px;
  }
  .fs-dt-input { width: 100%; }
  .fs-quick-row {
    display: flex; flex-wrap: wrap; gap: 6px;
  }
  .fs-quick-chip {
    font-size: 11px;
    font-family: var(--f-mono);
    padding: 4px 9px;
    background: var(--bg-elev-1);
    border: 1px solid var(--line-soft);
    color: var(--text-2);
    border-radius: 999px;
    cursor: pointer;
    transition: all var(--t-fast) ease;
    letter-spacing: 0.02em;
  }
  .fs-quick-chip:hover {
    background: var(--bg-elev-2);
    color: var(--text-1);
    border-color: var(--line);
  }
  .fs-action-row {
    margin-top: 18px;
    display: flex; align-items: center; gap: 12px;
    flex-wrap: wrap;
  }
  .fs-action-row .spacer { flex: 1; }
  #fs-price-hint {
    line-height: 1.4;
  }

  /* Subtle scrollbar polish */
  ::-webkit-scrollbar { width: 8px; height: 8px; }
  ::-webkit-scrollbar-track { background: transparent; }
  ::-webkit-scrollbar-thumb { background: var(--line); border-radius: 4px; }
  ::-webkit-scrollbar-thumb:hover { background: var(--line-strong); }

  /* ═══════════════════════════════════════════════════════════════════
     SETTINGS SUB-NAV  (mini-app section → its settings)
     ═══════════════════════════════════════════════════════════════════ */
  .settings-layout {
    display: grid;
    grid-template-columns: 220px 1fr;
    gap: var(--s5);
    align-items: start;
  }
  @media (max-width: 820px) {
    .settings-layout { grid-template-columns: 1fr; }
  }
  .settings-nav {
    position: sticky; top: 72px;
    display: flex; flex-direction: column; gap: 2px;
    background: var(--bg-pane);
    border: 1px solid var(--line-soft);
    border-radius: var(--r-lg);
    padding: 6px;
  }
  .settings-nav button, .settings-nav a {
    display: flex; align-items: center; gap: 10px;
    width: 100%; text-align: left;
    background: transparent; color: var(--text-2);
    border: 0;
    padding: 9px 12px;
    border-radius: var(--r-md);
    font-size: 13px; font-weight: 500;
    text-decoration: none;
    cursor: pointer;
    box-sizing: border-box;
    transition: background var(--t-fast) ease, color var(--t-fast) ease;
  }
  .settings-nav button:hover, .settings-nav a:hover { background: var(--bg-elev-1); color: var(--text-1); }
  .settings-nav button.active {
    background: var(--bg-elev-2); color: var(--text-1);
    box-shadow: inset 2px 0 0 var(--accent);
  }
  .settings-nav button svg, .settings-nav a svg {
    width: 16px; height: 16px; flex-shrink: 0; color: var(--text-3);
  }
  .settings-nav button.active svg { color: var(--accent); }
  .settings-nav a[href] { border-top: 1px solid var(--line-soft); margin-top: 4px; padding-top: 10px; }
  .settings-nav a[href] + a[href] { border-top: 0; margin-top: 0; padding-top: 9px; }

  .sub-panel { display: none; }
  .sub-panel.active { display: block; }
  .sub-panel > h2:first-child { margin-top: 0; }

  /* ── Operations panel (Settings → Operations). Integrations moved here from
     the kitchen app 2026-06-09; self-contained styles so it themes natively. ── */
  #sub-ops input[type=password] {
    font: 13px var(--f-sans); padding: 8px 10px; background: var(--bg-input);
    border: 1px solid var(--line); color: var(--text-1); border-radius: var(--r-md);
    outline: none; min-width: 0;
  }
  #sub-ops input[type=password]:focus { border-color: var(--accent); }
  #sub-ops input[type=text], #sub-ops input[type=password],
  #sub-ops select, #sub-ops textarea { width: 100%; }
  #sub-ops .field-label {
    font-size: 11px; font-weight: 600; letter-spacing: .04em; text-transform: uppercase;
    color: var(--text-3); display: block; margin-bottom: 4px;
  }
  #sub-ops .ops-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; margin-bottom: 14px; }
  #sub-ops code { background: var(--bg-elev-1); padding: 1px 5px; border-radius: 4px; }
  /* Faux 58mm thermal-paper receipt preview (always paper-white in both themes). */
  .feie-prev { display: inline-block; }
  .thermal {
    font-family: ui-monospace, Menlo, monospace; background: #fff; color: #111;
    width: 230px; padding: 10px 12px; border-radius: 3px; font-size: 11px; line-height: 1.5;
    box-shadow: 0 1px 6px rgba(0,0,0,.18);
  }
  .thermal .tln { white-space: pre-wrap; word-break: break-word; }
  .thermal .tc { text-align: center; }
  .thermal .tbold, .thermal .ttall { font-weight: 700; }
  .thermal .tbig { font-weight: 700; font-size: 13px; }

  /* Visual mockup card — phone-shaped illustration above each panel */
  .visual {
    display: flex; gap: var(--s5);
    background: linear-gradient(180deg, var(--bg-elev-1), var(--bg-pane));
    border: 1px solid var(--line-soft);
    border-radius: var(--r-lg);
    padding: var(--s5);
    margin-bottom: var(--s4);
    align-items: center;
  }
  .visual .phone {
    flex-shrink: 0;
    width: 132px; height: 240px;
    background: #0f0f12;
    border: 6px solid #2a2a30;
    border-radius: 18px;
    position: relative;
    overflow: hidden;
    box-shadow: 0 6px 20px rgba(0,0,0,.4);
  }
  .visual .phone-notch {
    position: absolute; top: 0; left: 50%;
    transform: translateX(-50%);
    width: 38px; height: 10px;
    background: #2a2a30;
    border-radius: 0 0 8px 8px;
  }
  .visual .caption {
    flex: 1;
    font-size: 12px; line-height: 1.6; color: var(--text-2);
  }
  .visual .caption strong { color: var(--text-1); font-weight: 600; }
  .visual .caption .tag {
    display: inline-block;
    background: rgba(163, 180, 159, .12); color: var(--accent-hi);
    border: 1px solid rgba(163, 180, 159, .25);
    padding: 2px 8px; border-radius: 999px;
    font-size: 10px; font-weight: 600;
    letter-spacing: .08em; text-transform: uppercase;
    margin-bottom: 8px;
  }

  /* Mini phone-screen elements — fake content inside the .phone visual */
  .pv { position: absolute; }
  .pv-bar    { left: 8px; right: 8px; top: 16px; height: 12px; background: var(--accent); border-radius: 3px; }
  .pv-hero   { left: 8px; right: 8px; top: 34px; height: 48px; background: linear-gradient(135deg, #4a5d2e, #2c3a1d); border-radius: 6px; }
  .pv-row    { left: 8px; right: 8px; height: 8px; background: #2a2a30; border-radius: 2px; }
  .pv-tile   { background: #1f1f24; border: 1px solid #2a2a30; border-radius: 4px; }
  .pv-tab    {
    position: absolute; bottom: 0; left: 0; right: 0; height: 28px;
    background: #f5f0e1; display: flex; align-items: center;
    justify-content: space-around; padding: 0 6px;
    border-top: 1px solid #d8d0bb;
  }
  .pv-tab span { width: 10px; height: 10px; background: #56544f; border-radius: 2px; }
  .pv-tab span.active { background: var(--accent); }
  .pv-highlight {
    position: absolute; outline: 2px dashed var(--accent);
    outline-offset: 2px; border-radius: 4px;
    animation: pv-pulse 1.6s ease-in-out infinite;
    pointer-events: none;
  }
  @keyframes pv-pulse {
    0%, 100% { outline-color: var(--accent); }
    50%      { outline-color: rgba(163, 180, 159, .35); }
  }

  .save-bar {
    position: sticky; bottom: 0;
    background: linear-gradient(180deg, transparent, var(--bg-app) 35%);
    margin: var(--s6) calc(var(--s5) * -1) calc(var(--s5) * -1);
    padding: var(--s4) var(--s5) var(--s5);
    display: flex; align-items: center; gap: var(--s4);
  }

  /* ═══════════════════════════════════════════════════════════════════
     SETTINGS — per-control group cards, visual pickers, side mockups
     ═══════════════════════════════════════════════════════════════════ */
  /* Group card (Settings / Delivery panels) — same elevated surface as .card
     so grouped setting blocks sit as distinct cards, matching the polished pages. */
  .group-card {
    background: var(--bg-pane);
    border: 1px solid var(--line);
    border-radius: var(--r-lg);
    padding: var(--s5);
    margin-bottom: var(--s3);
    box-shadow: 0 1px 2px rgba(0,0,0,.04), 0 4px 14px rgba(0,0,0,.05);
  }
  .group-card-head { margin-bottom: var(--s4); }
  .group-card-head h3 {
    font-size: 14px; font-weight: 600;
    margin: 0 0 4px;
  }
  /* Delivery page: section heads adopt the mono-caps rhythm (#tab-delivery
     scope wins over the shared .group-card-head h3 rule above). */
  #tab-delivery .group-card-head h3 {
    font-family: var(--f-mono);
    font-size: 11px; font-weight: 700; letter-spacing: .08em;
    text-transform: uppercase; color: var(--text-2);
    margin: 0 0 4px;
  }
  .group-card-head .muted {
    font-size: 12px; line-height: 1.55;
    color: var(--text-3);
  }

  /* Visual option picker grid */
  .opt-grid {
    display: grid;
    gap: 10px;
    grid-template-columns: repeat(auto-fill, minmax(112px, 1fr));
  }
  .opt {
    display: flex; flex-direction: column;
    background: var(--bg-elev-1);
    border: 1.5px solid var(--line);
    border-radius: var(--r-lg);
    padding: 8px;
    cursor: pointer;
    transition: border-color 120ms, background 120ms, transform 120ms;
    user-select: none;
  }
  .opt:hover { border-color: var(--line-strong); background: var(--bg-elev-2); }
  .opt.active {
    border-color: var(--accent);
    background: rgba(163, 180, 159, .06);
    box-shadow: 0 0 0 3px var(--ring);
  }
  .opt.active .opt-screen { transform: translateY(-1px); }
  .opt-screen {
    position: relative;
    height: 96px;
    background: #0f0f12;
    border-radius: 6px;
    margin-bottom: 6px;
    overflow: hidden;
    transition: transform 120ms;
  }
  .opt-label {
    font-size: 11px; font-weight: 600; color: var(--text-1);
    text-align: center;
  }
  .opt-sub {
    font-size: 10px; color: var(--text-3);
    text-align: center; margin-top: 2px; line-height: 1.4;
  }

  /* Reusable mini-screen primitives (inside .opt-screen) */
  .ms-bar    { position: absolute; left: 4px; right: 4px; top: 4px; height: 8px; border-radius: 2px; }
  .ms-hero   { position: absolute; left: 4px; right: 4px; top: 16px; height: 22px; background: linear-gradient(135deg, #4a5d2e, #2c3a1d); border-radius: 3px; }
  .ms-row    { position: absolute; left: 4px; right: 4px; height: 4px; background: #2a2a30; border-radius: 1px; }
  .ms-tile   { position: absolute; background: #1f1f24; border: 1px solid #2a2a30; border-radius: 2px; }
  .ms-tab    {
    position: absolute; bottom: 0; left: 0; right: 0; height: 16px;
    background: #f5f0e1;
    display: flex; align-items: center; justify-content: space-around;
    padding: 0 4px;
  }
  .ms-tab span { width: 6px; height: 6px; background: #56544f; border-radius: 1px; }
  .ms-tab span.active { background: var(--accent); }
  .ms-text {
    position: absolute; color: var(--text-1);
    font-size: 9px; font-weight: 600;
    text-align: center; width: 100%;
  }

  /* Side-by-side: small mockup + control */
  .illus-row {
    display: flex; gap: var(--s4);
    align-items: flex-start;
  }
  .mini-phone {
    flex-shrink: 0;
    width: 88px; height: 156px;
    background: #0f0f12;
    border: 4px solid #2a2a30;
    border-radius: 14px;
    position: relative;
    overflow: hidden;
  }
  .mini-phone .mp-notch {
    position: absolute; top: 0; left: 50%;
    transform: translateX(-50%);
    width: 22px; height: 6px;
    background: #2a2a30; border-radius: 0 0 4px 4px;
  }
  .illus-row .ctrl { flex: 1; min-width: 0; }

  /* Tiny chip preview (used for filter toggles) */
  .chip-strip {
    display: flex; gap: 4px; flex-wrap: wrap;
    background: var(--bg-elev-1);
    padding: 6px 8px;
    border-radius: var(--r-md);
    border: 1px solid var(--line);
    margin-top: 6px;
  }
  .chip-strip .chip {
    font-size: 10px; font-weight: 600;
    padding: 2px 8px; border-radius: 999px;
    background: var(--bg-elev-2); color: var(--text-2);
    border: 1px solid var(--line);
    transition: opacity 120ms;
  }
  .chip-strip .chip.dim { opacity: .25; }
  .chip-strip .chip.lit { background: rgba(163,180,159,.12); border-color: rgba(163,180,159,.35); color: var(--accent-hi); }

  /* Hide the underlying select when a picker drives it */
  .picker-source { display: none; }

  /* Currency input pill */
  .yen-input {
    display: inline-flex; align-items: center;
    background: var(--bg-input);
    border: 1px solid var(--line);
    border-radius: var(--r-md);
    overflow: hidden;
  }
  .yen-input::before {
    content: '¥';
    padding: 0 8px 0 12px;
    color: var(--text-3); font-weight: 600;
  }
  .yen-input input {
    border: 0; background: transparent;
    padding: 8px 12px 8px 0;
    width: 110px;
  }
  .yen-input input:focus { box-shadow: none; }

  /* ═════════════ MEDIA LIBRARY ═════════════ */
  .ml-layout {
    display: grid;
    grid-template-columns: 220px 1fr;
    gap: 18px;
    align-items: flex-start;
  }
  @media (max-width: 760px) { .ml-layout { grid-template-columns: 1fr; } }
  .ml-side {
    position: sticky; top: 16px;
    background: var(--bg-pane);
    border: 1px solid var(--line);
    border-radius: 12px;
    padding: 10px;
    display: flex; flex-direction: column; gap: 4px;
    max-height: calc(100vh - 32px);
    overflow: auto;
  }
  .ml-side-h {
    font-size: 10px; text-transform: uppercase; letter-spacing: .08em;
    color: var(--text-4); font-weight: 700;
    padding: 8px 8px 4px;
  }
  .ml-fld {
    display: flex; align-items: center; gap: 8px;
    padding: 7px 9px;
    border-radius: 8px;
    font-size: 12px; color: var(--text-2);
    cursor: pointer;
    transition: background var(--t-fast) ease, color var(--t-fast) ease;
    user-select: none;
  }
  .ml-fld:hover { background: var(--bg-elev-1); color: var(--text-1); }
  .ml-fld.active { background: var(--bg-elev-2); color: var(--text-1); }
  .ml-fld.over   { background: rgba(163, 180, 159, .14); color: var(--accent-hi); outline: 1px dashed var(--accent); outline-offset: -2px; }
  .ml-fld .ico  { color: var(--text-4); display: inline-flex; }
  .ml-fld.active .ico { color: var(--accent-hi); }
  .ml-fld .lbl  { flex: 1; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
  .ml-fld .n    { color: var(--text-4); font-size: 10px; font-variant-numeric: tabular-nums; }
  .ml-fld .act  { display: none; gap: 4px; }
  .ml-fld:hover .act { display: inline-flex; }
  .ml-fld .act button {
    border: 0; background: transparent; padding: 2px 4px; cursor: pointer;
    color: var(--text-3); border-radius: 4px;
  }
  .ml-fld .act button:hover { background: var(--bg-elev-2); color: var(--text-1); }
  .ml-side .ml-fld-add {
    margin-top: 6px; border-top: 1px solid var(--line); padding-top: 10px;
  }
  .ml-side .ml-fld-add input {
    width: 100%; box-sizing: border-box;
    padding: 6px 8px; font-size: 12px;
  }
  .ml-toolbar { display: flex; gap: 10px; flex-wrap: wrap; align-items: center; margin-bottom: var(--s3); }
  #ml-search { min-width: 220px; flex: 1; }
  .ml-tag-chips { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: var(--s3); }
  .ml-tag-chip {
    display: inline-flex; align-items: center; gap: 6px;
    padding: 5px 11px; border-radius: 999px;
    font-size: 11px; font-weight: 600; letter-spacing: .03em;
    border: 1px solid var(--line); background: var(--bg-elev-1);
    color: var(--text-3); cursor: pointer;
    transition: background var(--t-fast) ease, color var(--t-fast) ease;
  }
  .ml-tag-chip:hover { color: var(--text-2); background: var(--bg-elev-2); }
  .ml-tag-chip.active { background: rgba(163, 180, 159, .12); color: var(--accent-hi); border-color: rgba(163, 180, 159, .35); }
  .ml-tag-chip .n { color: var(--text-4); font-weight: 500; font-variant-numeric: tabular-nums; }

  #ml-dropzone {
    position: relative;
    border: 2px dashed var(--line);
    border-radius: 12px;
    padding: 18px;
    transition: border-color var(--t-fast) ease, background var(--t-fast) ease;
    min-height: 240px;
  }
  #ml-dropzone.drag { border-color: var(--accent); background: rgba(163, 180, 159, .04); }
  #ml-dropzone .ml-empty {
    text-align: center; color: var(--text-3);
    padding: 60px 20px; font-size: 13px;
  }
  #ml-dropzone .ml-empty svg { width: 36px; height: 36px; opacity: .35; margin-bottom: 10px; }

  .ml-grid {
    display: grid; gap: 12px;
    grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
  }
  .ml-card {
    position: relative;
    background: var(--bg-elev-1);
    border: 1px solid var(--line);
    border-radius: 10px;
    overflow: hidden;
    cursor: pointer;
    transition: border-color var(--t-fast) ease, transform var(--t-fast) ease;
  }
  .ml-card:hover { border-color: var(--line-strong); transform: translateY(-1px); }
  .ml-card.sel { border-color: var(--accent); box-shadow: 0 0 0 2px rgba(163, 180, 159, .25); }
  .ml-thumb {
    aspect-ratio: 1 / 1; width: 100%;
    background: var(--bg-elev-2);
    background-image: linear-gradient(45deg, var(--bg-elev-2) 25%, transparent 25%),
                      linear-gradient(-45deg, var(--bg-elev-2) 25%, transparent 25%),
                      linear-gradient(45deg, transparent 75%, var(--bg-elev-2) 75%),
                      linear-gradient(-45deg, transparent 75%, var(--bg-elev-2) 75%);
    background-size: 16px 16px;
    background-position: 0 0, 0 8px, 8px -8px, -8px 0;
    display: flex; align-items: center; justify-content: center;
  }
  .ml-thumb img {
    width: 100%; height: 100%; object-fit: contain; display: block;
  }
  .ml-card-info {
    padding: 8px 10px; font-size: 11px; color: var(--text-2);
    border-top: 1px solid var(--line);
    background: var(--bg-pane);
    display: flex; flex-direction: column; gap: 2px;
  }
  .ml-card-info .name { color: var(--text-1); font-weight: 600;
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
  .ml-card-info .meta { color: var(--text-4); font-size: 10px; }
  .ml-card-check {
    position: absolute; top: 6px; left: 6px;
    width: 22px; height: 22px;
    border-radius: 50%; border: 2px solid #fff;
    background: rgba(0,0,0,.45);
    display: flex; align-items: center; justify-content: center;
    color: #fff; font-size: 14px; line-height: 1;
    opacity: 0; transition: opacity var(--t-fast) ease;
    z-index: 2;
  }
  .ml-card:hover .ml-card-check { opacity: 1; }
  .ml-card.sel .ml-card-check { opacity: 1; background: var(--accent); color: #000; border-color: var(--accent); }
  .ml-card .used-badge {
    position: absolute; top: 6px; right: 6px;
    padding: 2px 7px; border-radius: 999px;
    font-size: 10px; font-weight: 700; letter-spacing: .04em;
    background: rgba(0, 0, 0, .55); color: #fff;
  }
  .ml-card .used-badge.zero { background: rgba(0, 0, 0, .35); color: rgba(255,255,255,.6); }

  .ml-bulkbar {
    position: sticky; bottom: 16px;
    margin-top: var(--s3);
    display: none;
    align-items: center; gap: 10px;
    padding: 10px 14px;
    background: var(--bg-pane);
    border: 1px solid var(--line-strong);
    border-radius: 999px;
    box-shadow: var(--sh-3);
    font-size: 12px; z-index: 5;
  }
  .ml-bulkbar.on { display: inline-flex; }
  .ml-bulkbar .count {
    display: inline-flex; align-items: center;
    padding: 3px 10px; border-radius: 999px;
    background: var(--bg-elev-2); color: var(--text-1);
    font-weight: 700;
  }

  /* Detail drawer extras */
  .ml-tag-input-row { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; }
  .ml-tag-input-row .ml-tag {
    display: inline-flex; align-items: center; gap: 4px;
    padding: 3px 8px; border-radius: 999px;
    background: rgba(163, 180, 159, .12); color: var(--accent-hi);
    border: 1px solid rgba(163, 180, 159, .35);
    font-size: 11px; font-weight: 600;
  }
  .ml-tag-input-row .ml-tag .x {
    cursor: pointer; color: var(--text-3);
    border: 0; background: transparent; padding: 0; font-size: 14px; line-height: 1;
  }
  .ml-tag-input-row input { flex: 1; min-width: 100px; }

  .ml-preview {
    background: var(--bg-elev-2);
    background-image: linear-gradient(45deg, var(--bg-elev-1) 25%, transparent 25%),
                      linear-gradient(-45deg, var(--bg-elev-1) 25%, transparent 25%),
                      linear-gradient(45deg, transparent 75%, var(--bg-elev-1) 75%),
                      linear-gradient(-45deg, transparent 75%, var(--bg-elev-1) 75%);
    background-size: 18px 18px;
    background-position: 0 0, 0 9px, 9px -9px, -9px 0;
    border-radius: 10px;
    display: flex; align-items: center; justify-content: center;
    min-height: 220px; max-height: 320px;
    overflow: hidden;
  }
  .ml-preview img { max-width: 100%; max-height: 320px; object-fit: contain; display: block; }

  /* ── Mobile shell + dashboard CSS — relocated into the head stylesheet so the
     fixed bottom tab bar paints in the first render pass. Previously this lived
     in a trailing <style> ~20k lines down, so on slow links the bar appeared a
     beat after the (head-styled) dashboard. ── */
  /* ════════════════════════════════════════════════════════════════════
     MOBILE SHELL (Phase 1) — operator mini-app web-view.
     Everything here is ADDITIVE and scoped to phones (≤720px). The desktop
     layout is never altered: the horizontal tab bar is hidden and a fixed
     bottom tab bar + a "More" bottom-sheet take over navigation. The bottom
     bar reuses the existing desktop [data-tab] buttons, so all section
     switching, badges and lazy iframe loads keep working unchanged.
     See memory: feature_operator_mobile_miniapp.
     ════════════════════════════════════════════════════════════════════ */
  #rm-bottomnav { display: none; }
  .rm-sheet { display: none; }
  html.rm-noscroll { overflow: hidden; }

  /* ════════════════════════════════════════════════════════════════════
     PORTRAIT LOCK — a phone held in landscape gets a "rotate upright"
     cover instead of the desktop layout. The mobile shell is width-gated
     at ≤720px (portrait); a landscape phone's width jumps past that and
     the page would fall back to the desktop layout, which is meaningless
     on a phone mini-app. This blocks that on phone-sized devices ONLY:
     coarse pointer (touch) + a short viewport (≤520px tall) in landscape
     — excluding tablets (taller in landscape) and desktop browsers (fine
     pointer). Companion fix lives in the WeChat wrapper as
     pageOrientation:"portrait"; this overlay is the in-page safety net.
     See memory: feature_operator_mobile_miniapp.
     ════════════════════════════════════════════════════════════════════ */
  #rm-rotate-lock { display: none; }
  @media (orientation: landscape) and (max-height: 520px) and (pointer: coarse) {
    #rm-rotate-lock {
      display: flex; position: fixed; inset: 0; z-index: 100001;
      flex-direction: column; align-items: center; justify-content: center;
      gap: 16px; padding: 24px;
      background: var(--bg-app); color: var(--text-1); text-align: center;
    }
    #rm-rotate-lock svg { width: 54px; height: 54px; color: var(--brass); }
    #rm-rotate-lock .rl-ttl { font-size: 16px; font-weight: 700; line-height: 1.3; }
    #rm-rotate-lock .rl-sub {
      font-size: 13px; color: var(--text-3); max-width: 280px; line-height: 1.5;
    }
  }

  @media (max-width: 720px) {
    /* Desktop left rail → replaced by the bottom bar. */
    .tabs { display: none !important; }

    /* Embedded editors on phones: the base rule pins them at top:56px / left:212px
       (below the topbar, right of the desktop rail) with bottom:0. On mobile the
       rail is hidden and the nav is at the BOTTOM, so that left inset was dead
       space and bottom:0 hid the editor's own bottom bar (Add / Design / Save)
       behind the bottom nav. Reset left:0 and go full-bleed
       from the top down to just above the bottom nav, and lift z-index above the
       (now-redundant) global topbar so the focused editor owns the screen. The
       bottom nav (z:60) stays tappable above the editor's lifted bottom edge. */
    #tab-mini-app-editor, #tab-product-editor {
      top: 0; left: 0; right: 0;
      bottom: calc(52px + env(safe-area-inset-bottom));
      z-index: 50;
    }

    /* Topbar stays slim on phones: brand mark + search + account avatar.
       Text-size / theme / language live in the mobile More sheet (and now also
       in the avatar dropdown); the old loose topbar toggles were removed. */
    /* On phones the brand bar scrolls away (position:static, not sticky) so the
       per-tab filter bar below can pin to the very top (top:0) and BE the sticky
       header — otherwise the filter bar floated 56px down with an empty gap above
       it once the brand bar scrolled off, reading as "not attached to anything". */
    /* Rail is hidden on phones, so the topbar reclaims full width AND shows its
       own brand again (the desktop rule hides it because the wordmark lives in
       the rail there). */
    .topbar { padding: 0 14px; gap: 8px; position: static; left: auto; right: auto; margin-left: 0; }
    .topbar .brand { display: flex; }
    .brand .sub, .brand .divider { display: none; }

    /* Full-bleed on phones: the desktop `max-width:min(1440px,92vw)` cap + the
       24px side padding stacked to ~39px gutters, squeezing the dashboard to
       ~80% width. Drop the cap and use the prototype's 16px side gutters so the
       content fills the screen. Bottom padding reserves room for the fixed bar. */
    main {
      max-width: 100%; margin: 0;
      padding: var(--s5) 16px calc(74px + env(safe-area-inset-bottom));
    }

    /* ── Fixed bottom tab bar ── */
    #rm-bottomnav {
      display: flex;
      position: fixed; left: 0; right: 0; bottom: 0; z-index: 60;
      background: var(--bg-pane);
      border-top: 1px solid var(--line-soft);
      padding-bottom: env(safe-area-inset-bottom);
      box-shadow: 0 -2px 16px rgba(0, 0, 0, .18);
    }
    #rm-bottomnav button {
      flex: 1 1 0; min-width: 0;
      display: flex; flex-direction: column; align-items: center; justify-content: center;
      gap: 3px; padding: 8px 2px 7px;
      background: none; border: 0; cursor: pointer;
      color: var(--text-3); font: inherit; position: relative;
      -webkit-tap-highlight-color: transparent;
    }
    #rm-bottomnav button svg { width: 22px; height: 22px; }
    #rm-bottomnav .rm-lab {
      font-size: 10px; line-height: 1; letter-spacing: .01em;
      max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
    }
    #rm-bottomnav button.active { color: var(--brass); }
    #rm-bottomnav .rm-badge {
      position: absolute; top: 4px; left: calc(50% + 5px);
      min-width: 16px; height: 16px; padding: 0 4px;
      border-radius: 9px; background: #dc2626; color: #fff;
      font-size: 10px; font-weight: 800; line-height: 16px; text-align: center;
      display: none;
    }
    #rm-bottomnav .rm-badge.on { display: block; }
    #rm-bottomnav .rm-dot {
      position: absolute; top: 7px; left: calc(50% + 8px);
      width: 8px; height: 8px; border-radius: 50%; background: #dc2626; display: none;
    }
    #rm-bottomnav .rm-dot.on { display: block; }

    /* ── "More" bottom-sheet ── (scoped to #rm-more-sheet so this does not
       leak `inset:0`/`top:0` onto the newer shared Sort/Manage sheet, which
       is bottom-anchored via its own `.rm-sheet` rule further down) */
    /* Mirror the proven Sort/Manage sheet (.rm-sheet @ ~5015): the panel is a
       TOP-LEVEL `position:fixed; bottom:0` element and the backdrop is a separate
       fixed sibling — NOT a fixed panel nested inside a fixed full-screen box.
       That nesting was the bug: under the open-time scroll lock (overflow:hidden),
       the nested panel anchored to the layout-viewport bottom (below the fold), so
       the sheet slid off-screen and only the grip peeked up. `display:contents`
       dissolves the wrapper so its children position against the viewport directly,
       exactly like #rm-bottomnav and the Sort sheet (both of which sit correctly on
       the visible bottom edge). */
    #rm-more-sheet.rm-sheet { display: contents; }
    #rm-more-sheet.rm-sheet[hidden] { display: none; }
    .rm-sheet-backdrop {
      position: fixed; inset: 0; z-index: 70;
      background: var(--bg-overlay, rgba(0, 0, 0, .45));
      animation: rmFade 550ms ease;
    }
    @keyframes rmFade { from { opacity: 0; } to { opacity: 1; } }
    .rm-sheet-panel {
      position: fixed; left: 0; right: 0; bottom: 0; z-index: 71;
      max-height: 82vh; max-height: 82dvh; overflow-y: auto;
      background: var(--bg-pane);
      border-radius: 18px 18px 0 0;
      border-top: 1px solid var(--line);
      padding: 8px 12px calc(16px + env(safe-area-inset-bottom));
      animation: rmSheetUp 550ms cubic-bezier(.22, .61, .36, 1);
    }
    @keyframes rmSheetUp { from { transform: translateY(100%); } to { transform: translateY(0); } }
    .rm-sheet-grip {
      width: 38px; height: 4px; border-radius: 4px;
      background: var(--line-strong); margin: 6px auto 14px;
    }
    .rm-sheet-list { display: grid; grid-template-columns: 1fr 1fr; gap: 6px; }
    .rm-sheet-list button {
      display: flex; align-items: center; gap: 10px;
      padding: 13px 12px; border: 1px solid var(--line-soft);
      border-radius: var(--r-md); background: var(--bg-elev-1);
      color: var(--text-1); font: inherit; font-size: 13px;
      text-align: left; cursor: pointer; -webkit-tap-highlight-color: transparent;
    }
    .rm-sheet-list button svg { width: 18px; height: 18px; flex: 0 0 auto; color: var(--text-2); }
    .rm-sheet-list button.active { border-color: var(--brass); color: var(--brass); }
    .rm-sheet-list button.active svg { color: var(--brass); }
    .rm-sheet-sec {
      grid-column: 1 / -1; font-size: 11px; letter-spacing: .08em;
      text-transform: uppercase; color: var(--text-3); margin: 12px 2px 2px;
    }
    .rm-sheet-ctl { grid-column: 1 / -1; display: flex; gap: 8px; margin-top: 12px; }
    .rm-sheet-ctl button { flex: 1; justify-content: center; }
  }

  /* ════════════════════════════════════════════════════════════════════
     MOBILE DAILY-OPS (Phase 2) — make the five operator screens
     (Dashboard, Orders, Invoices, Customers, Products) thumb-friendly.
     Everything ADDITIVE and ≤720px-only; the desktop layout is never
     touched. Companion to the Phase 1 shell above.
     See memory: feature_operator_mobile_miniapp.
     ════════════════════════════════════════════════════════════════════ */
  @media (max-width: 720px) {
    /* ── KPI strips → a tidy 2-up grid. The desktop auto-fit/minmax can
       leave a lone orphan tile stretched full-width on a narrow phone;
       a fixed 2-col grid reads cleanly. Trim the headline a touch so the
       figures don't overflow their cell. ── */
    .fc-kpis, .db-kpis { grid-template-columns: 1fr 1fr; gap: 10px; }
    .fc-kpi .val { font-size: 26px; }
    .db-kpi .val { font-size: 24px; }

    /* ── Wide tables (Orders, Customers, ledgers) scroll sideways instead
       of clipping — the wrap is overflow:hidden on desktop. Momentum
       scroll on iOS; a min-width keeps columns legible rather than
       crushed. ── */
    .table-wrap { overflow-x: auto; -webkit-overflow-scrolling: touch; }
    .table-wrap > table { min-width: 560px; }
    /* Empty state: drop the 560px floor and the column header so the centered
       placeholder fills the viewport instead of sitting off-screen in a wide,
       horizontally-scrolling table. */
    .table-wrap > table.is-empty { min-width: 0; }
    .table-wrap > table.is-empty thead { display: none; }

    /* ── Comfortable tap targets for the controls operators hit all day. ── */
    .btn { min-height: 38px; }
    .btn-sm { min-height: 32px; }
    .status-chip { padding: 9px 14px; font-size: 13px; }
    th, td { padding: 12px 14px; }

    /* Toolbar search fields claim the full row; the filters/buttons wrap
       onto the next line (the toolbars are already flex-wrap). */
    .row > input[type=text],
    .pa-toolbar input[type=text],
    #cu-q, #pa-search { flex: 1 1 100%; min-width: 0; }

    /* Stop iOS Safari from auto-zooming the page when a sub-16px field is
       focused — keeps the web-view from jumping on every tap into a box. */
    input, select, textarea { font-size: 16px; }

    /* Drawer footer actions stack to full width so they're easy to thumb. */
    .side-drawer .drawer-foot { flex-wrap: wrap; gap: 8px; }
    .side-drawer .drawer-foot > .btn,
    .side-drawer .drawer-foot > button { flex: 1 1 auto; }
  }

  /* ════════════════════════════════════════════════════════════════════
     MOBILE ORDERS REDESIGN — Variant A "full-screen push" backbone.
     Phone-only (≤720px). The desktop orders TABLE + dense toolbar are
     hidden; the SAME live order data renders as tap-friendly cards
     (#rm-orders-cards, fed by rmRenderOrderCards). Tap a card → the
     existing full-screen order drawer (openOrder). Swipe a card left →
     Advance / Call (the Variant C blend). KPI strip → horizontal rail;
     status chips → sticky single-row filter. Theme-aware (reuses app
     tokens so dark/light + View>theme still work). Desktop is untouched.
     Built to match design bundle: mobile/orders-a.html + fc-mobile.css.
     See memory: feature_operator_mobile_miniapp.
     ════════════════════════════════════════════════════════════════════ */
  #rm-orders-cards { display: none; }
  @media (max-width: 720px) {
    /* The order/customer detail drawers host the desktop layout. drawer-body
       has overflow-y:auto, which makes overflow-x COMPUTE to auto — so a wide
       child (e.g. an items/reservations .table-wrap forced to min-width:560px
       by the Phase 2 rule) turns the whole panel into a sideways scroller and
       clips the left edge. Clip horizontal overflow here; inner tables still
       scroll within their own .table-wrap (overflow-x:auto). Tighter padding
       reclaims a little width. */
    .side-drawer .drawer-body { overflow-x: hidden; padding-left: var(--s4); padding-right: var(--s4); }

    /* Order-detail header → mobile app-bar matching the mockup:
       ‹ R260607000002  •Paid              •••
       Left back-chevron replaces the floating ×; the order number + status pill
       sit on one sticky line; the "View" display-options popover collapses to a
       ⋯ kebab on the right. The "Sort items" chips (desktop-only) stay hidden. */
    #order-drawer .drawer-close { display: none; }
    #order-drawer .sort-chips { display: none; }
    #order-drawer .od-eyebrow { display: none; }

    /* Kill the scroll-container's top padding (orders only) so the sticky
       app-bar pins flush to the very top of the drawer — otherwise the bar
       sticks BELOW the padding, leaving a gap where scrolled content (the
       ¥total banner) peeks through and pushing the order# below the chevron. */
    #order-detail-mount { padding-top: 0; }

    /* The in-card "← Back to customer" button is redundant on phones — the
       app-bar back chevron already routes back to the customer (orderDrawerBack).
       Hide it here; desktop keeps it since it has no phone-style chevron. */
    .order-back-cust { display: none; }

    /* Back chevron — left side of the app-bar (sibling of .drawer-body, fixed to
       the drawer top so it stays put while the body scrolls). Vertically
       centered against the 52px bar: (52-36)/2 = 8px. */
    #order-drawer .drawer-back {
      display: inline-flex; align-items: center; justify-content: center;
      position: absolute; top: 8px; left: 6px; z-index: 8;
      width: 36px; height: 36px; padding: 0;
      background: transparent; border: 0; border-radius: 999px;
      color: var(--text-1); cursor: pointer;
      -webkit-tap-highlight-color: transparent;
    }
    #order-drawer .drawer-back svg { width: 22px; height: 22px; }
    #order-drawer .drawer-back:active { background: var(--bg-elev-1); }

    /* Sticky single-line app-bar. Full-bleed by cancelling the drawer-body pad;
       left pad clears the chevron, right pad clears the ⋯ kebab. */
    #order-drawer .order-head {
      display: flex; align-items: center; justify-content: flex-start;
      position: sticky; top: 0; z-index: 7;
      margin: 0 calc(-1 * var(--s4)) var(--s4);
      padding: 12px 50px 12px 44px;
      background: var(--bg-pane);
      border-bottom: 1px solid var(--line);
      box-shadow: 0 8px 14px -12px rgba(0, 0, 0, .45);
      min-height: 52px;
    }
    #order-drawer .order-head > div:first-child { min-width: 0; flex: 1; }
    #order-drawer .order-head .title-row { margin-bottom: 0; flex-wrap: nowrap; min-width: 0; }
    #order-drawer .order-head .title-row b {
      flex: 0 1 auto; min-width: 0;
      overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
    }
    /* Status pill keeps its full width — only the order# ellipsizes — so the
       pill never bleeds past the right pad into the ⋯ kebab. */
    #order-drawer .order-head .title-row .pill { flex: 0 0 auto; }

    /* Status actions ride a second, full-width line under the order# row so
       they don't squeeze the (sticky) app-bar title. The ⋯ kebab stays pinned
       top-right; the buttons sit below it. */
    #order-drawer .order-head { flex-wrap: wrap; }
    #order-drawer .order-head .od-head-tx { flex-basis: 100%; margin-top: 8px; }

    /* Stock Reservations: drop the secondary columns (Expires / Committed /
       Released) on phones so the 4 core columns — Product, Qty, Channel, Status
       — fit without the side-scroll that was clipping the "committed" pill. */
    #order-drawer .resv-table { min-width: 0; }
    #order-drawer .resv-table th:nth-child(n+5),
    #order-drawer .resv-table td:nth-child(n+5) { display: none; }

    /* Hide the desktop table + the whole dense toolbar on phones. The topbar
       power-search (the magnifier / Ctrl-K) already finds orders live by #,
       recipient name or phone and jumps straight into them, so the in-list
       search box + store filter + refresh button were all redundant on the
       narrow mobile layout — drop the entire row. */
    #tab-orders .table-wrap { display: none; }
    #tab-orders > .row { display: none; }

    /* Pin the status-filter bar directly under the topbar at ALL scroll
       positions. In source order the KPI rail sits BETWEEN the topbar and the
       chips, so until you scrolled past the KPIs the sticky chips floated
       detached in the middle (the "disconnect" from the Online Store bar).
       Make the orders section a flex column and pull the chips to the front
       (order:-1) so they're the first thing under the topbar and the KPIs /
       search / cards all scroll underneath them. The desktop page title is
       redundant on phones (the bottom nav already labels the tab) — hide it. */
    #tab-orders.active { display: flex; flex-direction: column; }
    #tab-orders > h2 { display: none; }
    #tab-orders > #orders-status-chips { order: -1; }

    /* KPI strip → horizontal snap rail (override the Phase 2 2-up grid). */
    #ord-kpis {
      display: flex; grid-template-columns: none;
      gap: 10px; overflow-x: auto; -webkit-overflow-scrolling: touch;
      scroll-snap-type: x proximity; padding-bottom: 6px;
    }
    #ord-kpis::-webkit-scrollbar { display: none; }
    #ord-kpis > .fc-kpi { flex: 0 0 47%; min-width: 150px; scroll-snap-align: start; }

    /* Status chips → sticky single-row scroller, always reachable. Render it as
       a SOLID filter bar (not the page bg) with a divider + drop shadow so
       cards visibly scroll UNDER it; with bg-app it was invisible and content
       bled through around the pills, reading as a broken float. Full-bleed to
       the screen edges (cancel main's horizontal padding) so the bar spans the
       whole width like the topbar above it. */
    #orders-status-chips {
      flex-wrap: nowrap; overflow-x: auto; -webkit-overflow-scrolling: touch;
      position: sticky; top: 0; z-index: 8;
      /* Full-bleed to the screen edges: cancel main's 16px gutter EXACTLY. The
         old -24px (--s6) over-pulled 8px past the edge, crowding the "All" chip
         against the left (and nudging the row off-screen). Re-pad so the first
         chip sits a touch inside the gutter; scroll-padding keeps that inset
         when the row snaps back to the start. */
      margin-left: -16px; margin-right: -16px;
      padding: 10px 16px; padding-left: var(--s6);
      scroll-padding-left: var(--s6);
      background: var(--bg-pane);
      border-bottom: 1px solid var(--line);
      box-shadow: 0 8px 14px -12px rgba(0, 0, 0, .45);
      scroll-snap-type: x proximity;
    }
    #orders-status-chips::-webkit-scrollbar { display: none; }
    #orders-status-chips > .status-chip { flex: 0 0 auto; scroll-snap-align: start; }

    /* ── Card list ── */
    #rm-orders-cards { display: block; }
    .rm-ocard-wrap {
      position: relative; margin-bottom: 10px;
      border-radius: var(--r-lg); overflow: hidden;
    }
    /* Swipe-revealed action lane (sits behind the card). */
    .rm-ocard-actions { position: absolute; inset: 0; display: flex; justify-content: flex-end; }
    .rm-ocard-actions button {
      border: 0; cursor: pointer; width: 88px;
      display: flex; flex-direction: column; align-items: center; justify-content: center;
      gap: 5px; font: 600 12px var(--f-sans);
      -webkit-tap-highlight-color: transparent;
    }
    .rm-ocard-actions svg { width: 20px; height: 20px; }
    .rm-act-advance { background: linear-gradient(180deg, var(--brass-hi), var(--brass)); color: var(--brass-ink, #1A140A); }
    .rm-act-call    { background: var(--accent); color: #fff; }

    .rm-ocard {
      position: relative; z-index: 1;
      background: var(--bg-pane);
      border: 1px solid var(--line-soft);
      border-radius: var(--r-lg);
      padding: 13px 14px;
      transition: transform .26s cubic-bezier(.16, 1, .3, 1);
      -webkit-tap-highlight-color: transparent; cursor: pointer;
      touch-action: pan-y;
    }
    .rm-ocard.swiped { transform: translateX(-176px); }
    .rm-ocard:active { background: var(--bg-elev-1); }

    .rm-oc-top { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
    .rm-oc-no { font: 600 13px var(--f-mono); color: var(--text-1); font-variant-numeric: tabular-nums; letter-spacing: .01em; }
    .rm-oc-new { display: inline-block; margin-right: 6px; padding: 1px 6px; border-radius: 5px;
      background: var(--brass); color: #fff; font: 700 9px var(--f-mono); letter-spacing: .06em; vertical-align: 1px; }

    .rm-oc-mid { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; margin-top: 9px; }
    .rm-oc-cust { min-width: 0; }
    .rm-oc-name { font-size: 15px; font-weight: 600; color: var(--text-1); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
    .rm-oc-phone { font: 12px var(--f-mono); color: var(--text-3); margin-top: 2px; }
    .rm-oc-total { font: 700 17px var(--f-mono); color: var(--text-1); white-space: nowrap; }

    .rm-oc-foot { display: flex; align-items: center; flex-wrap: nowrap; justify-content: space-between; gap: 10px; margin-top: 8px; font-size: 11.5px; color: var(--text-3); }
    .rm-oc-foot .sep { opacity: .5; }
    .rm-oc-foot .shipby { white-space: nowrap; }
    .rm-oc-foot .shipby.late { color: var(--danger); font-weight: 600; }
    /* Combined: ship-by promoted to the right with a chevron that rotates open. */
    .rm-oc-meta { flex: 1 1 auto; min-width: 0; display: flex; flex-wrap: wrap; align-items: center; gap: 6px; }
    .rm-oc-go { display: flex; align-items: center; gap: 6px; flex: 0 0 auto; }
    .rm-oc-go .shipby { font: 600 13px var(--f-sans); color: var(--text-2); }
    .rm-oc-go .shipby.act { color: var(--brass); }
    .rm-oc-go .shipby.late { color: var(--danger); }
    .rm-oc-go .rm-oc-chev { display: flex; color: var(--text-3); transition: transform .5s cubic-bezier(.22,.61,.36,1); }
    .rm-oc-go .rm-oc-chev svg { width: 16px; height: 16px; }
    .rm-ocard.open .rm-oc-go .rm-oc-chev { transform: rotate(90deg); }

    .rm-oc-hint {
      display: flex; align-items: center; gap: 6px; width: 100%;
      margin-top: 11px; padding-top: 10px;
      border-top: 1px solid var(--line-soft);
      font-size: 12.5px; font-weight: 600; color: var(--brass);
    }
    /* When it's the forward-action quick button: strip native chrome, keep look. */
    button.rm-oc-hint {
      background: none; border: 0; border-top: 1px solid var(--line-soft);
      font-family: inherit; text-align: left; cursor: pointer;
      -webkit-tap-highlight-color: transparent;
      transition: opacity .12s, color .12s;
    }
    button.rm-oc-hint:active { opacity: .55; }
    .rm-oc-hint svg { width: 14px; height: 14px; flex: 0 0 auto; }
    .rm-oc-hint.done { color: var(--text-3); font-weight: 500; }

    /* ── Combined: inline peek — tap a card to expand a quick summary + actions
       in place; "Open full details" still launches the existing drawer. ── */
    .rm-oc-exp { display: grid; grid-template-rows: 0fr; transition: grid-template-rows .55s cubic-bezier(.22,.61,.36,1); }
    .rm-ocard.open .rm-oc-exp { grid-template-rows: 1fr; }
    .rm-oc-exp > .inner { overflow: hidden; }
    .rm-oc-exp-pad { margin-top: 12px; padding-top: 12px; border-top: 1px dashed var(--line-soft); }
    .rm-oc-dl { display: grid; grid-template-columns: auto 1fr; gap: 7px 12px; align-items: baseline; margin-bottom: 12px; }
    .rm-oc-dl dt { font: 600 9.5px var(--f-mono); letter-spacing: .1em; text-transform: uppercase; color: var(--text-3); white-space: nowrap; }
    .rm-oc-dl dd { margin: 0; font-size: 13px; color: var(--text-1); text-align: right; }
    .rm-oc-acts { display: flex; gap: 8px; }
    .rm-oc-acts .rm-pbtn { flex: 1; display: flex; align-items: center; justify-content: center; gap: 7px;
      height: 44px; border: 0; border-radius: 11px; cursor: pointer; font: 600 13.5px var(--f-sans);
      -webkit-tap-highlight-color: transparent; }
    .rm-oc-acts .rm-pbtn svg { width: 17px; height: 17px; }
    .rm-oc-acts .rm-pbtn.adv { background: linear-gradient(180deg, var(--brass-hi), var(--brass)); color: var(--brass-ink, #1A140A); }
    .rm-oc-acts .rm-pbtn.call { flex: 0 0 auto; width: 52px; background: var(--bg-elev-1); color: var(--text-1); }
    .rm-oc-full { display: flex; align-items: center; justify-content: center; gap: 7px; width: 100%;
      margin-top: 9px; padding: 11px; border: 1px solid var(--line); background: transparent; border-radius: 11px;
      color: var(--text-2); font: 500 13px var(--f-sans); cursor: pointer; -webkit-tap-highlight-color: transparent; }
    .rm-oc-full svg { width: 16px; height: 16px; }
    .rm-oc-full:active { background: var(--bg-elev-1); }
    .rm-oc-items { background: var(--bg-elev-1); border: 1px solid var(--line-soft); border-radius: 11px; padding: 2px 12px; margin-bottom: 12px; }
    .rm-oc-item { display: flex; align-items: center; gap: 10px; padding: 9px 0; border-top: 1px solid var(--line-soft); font-size: 13.5px; color: var(--text-1); }
    .rm-oc-item:first-child { border-top: 0; }
    .rm-oc-item .nm { flex: 1; min-width: 0; }
    .rm-oc-item .nm .sku { display: block; margin-top: 2px; font: 11px var(--f-mono); color: var(--text-3); letter-spacing: .02em; }
    .rm-oc-item .nm .opts { display: block; margin-top: 3px; font-size: 12px; line-height: 1.35; color: var(--brass); }
    .rm-oc-item .q { font: 12px var(--f-mono); color: var(--text-3); flex: 0 0 auto; }
    /* >1 unit — bold + brass so a multi-unit line can't be shipped short. */
    .rm-oc-item .q.qty-multi { font-weight: 700; color: var(--brass); }
    .rm-oc-item .a { font-weight: 600; min-width: 54px; text-align: right; font-variant-numeric: tabular-nums; flex: 0 0 auto; }
    /* Tracking block in the inline peek — carrier + waybill + live state +
       latest scan + one-tap carrier link, so operators answer "where's my
       parcel?" without opening the full drawer. Split orders list each parcel. */
    .rm-oc-trk { background: var(--bg-elev-1); border: 1px solid var(--line-soft); border-radius: 11px; padding: 10px 12px; margin-bottom: 12px; }
    .rm-trk-h { display: flex; align-items: center; gap: 6px; font: 600 9.5px var(--f-mono); letter-spacing: .1em; text-transform: uppercase; color: var(--text-3); margin-bottom: 8px; }
    .rm-trk-h svg { width: 13px; height: 13px; flex: 0 0 auto; }
    .rm-trk-row + .rm-trk-row { margin-top: 10px; padding-top: 10px; border-top: 1px solid var(--line-soft); }
    .rm-trk-parcel { font: 600 10px var(--f-mono); color: var(--text-3); margin-bottom: 4px; }
    .rm-trk-line { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
    .rm-trk-carrier { font: 600 11px var(--f-mono); letter-spacing: .04em; color: var(--text-2); }
    .rm-trk-num { font: 600 13px var(--f-mono); color: var(--text-1); letter-spacing: .02em; }
    .rm-trk-live { font-size: 10px; font-weight: 600; color: #5a8c2a; }
    .rm-trk-manual { font-size: 10px; color: var(--text-3); }
    .rm-trk-meta { display: flex; align-items: center; gap: 10px; margin-top: 6px; }
    .rm-trk-status { font-size: 12px; font-weight: 600; color: var(--text-1); }
    .rm-trk-link { display: inline-flex; align-items: center; gap: 4px; margin-left: auto; font-size: 12px; font-weight: 600; color: var(--brass); text-decoration: none; }
    .rm-trk-link svg { width: 13px; height: 13px; }
    .rm-trk-link:active { opacity: .6; }
    .rm-trk-ev { display: flex; gap: 8px; justify-content: space-between; margin-top: 6px; font-size: 11.5px; line-height: 1.4; color: var(--text-3); }
    .rm-trk-ev .d { min-width: 0; }
    .rm-trk-ev .t { flex: 0 0 auto; font: 11px var(--f-mono); color: var(--text-3); }
    .rm-oc-loading { color: var(--text-3); font-size: 13px; padding: 12px 2px; }

    .rm-oc-empty { text-align: center; color: var(--text-3); padding: 44px 20px; font-size: 13.5px; }
  }

  /* ════════════════════════════════════════════════════════════════════
     MOBILE ORDERS — Variant B blend: bottom sheets. Two phone-only entry
     points layered on top of the A backbone + C swipe without disturbing
     them: (1) a Sort button (#rm-mob-tools) above the card list, and (2) a
     ⋯ Manage button on every card. Both raise the shared bottom sheet
     (.rm-sheet, created once in <body> by the mobile-orders script). The
     desktop triggers are display:none; the sheet overlay is only ever opened
     from these phone-only buttons. See memory: feature_operator_mobile_miniapp.
     ════════════════════════════════════════════════════════════════════ */
  #rm-mob-tools { display: none; }
  .rm-oc-more { display: none; }
  @media (max-width: 720px) {
    /* Sort toolbar — right-aligned above the cards; scrolls under the sticky chips. */
    #rm-mob-tools { display: flex; justify-content: flex-end; margin-bottom: 10px; }
    .rm-sort-btn {
      display: inline-flex; align-items: center; gap: 7px;
      height: 38px; padding: 0 14px; border-radius: 999px;
      border: 1px solid var(--line); background: var(--bg-pane);
      color: var(--text-1); font: 500 13px var(--f-sans); cursor: pointer;
      -webkit-tap-highlight-color: transparent;
    }
    .rm-sort-btn svg { width: 16px; height: 16px; }
    .rm-sort-btn .cur { color: var(--text-3); }
    .rm-sort-btn:active { background: var(--bg-elev-1); }

    /* ⋯ Manage button on each card foot (right-aligned, doesn't block tap/swipe). */
    .rm-oc-foot .rm-oc-meta {
      display: flex; align-items: center; flex-wrap: wrap; gap: 6px;
      flex: 1 1 auto; min-width: 0;
    }
    .rm-oc-more {
      display: inline-flex; align-items: center; justify-content: center;
      flex: 0 0 auto; margin-left: auto;
      width: 30px; height: 30px; padding: 0; border-radius: 999px;
      border: 1px solid var(--line-soft); background: var(--bg-pane);
      color: var(--text-2); cursor: pointer; -webkit-tap-highlight-color: transparent;
    }
    .rm-oc-more svg { width: 18px; height: 18px; }
    .rm-oc-more:active { background: var(--bg-elev-1); }
  }

  /* Shared bottom sheet (Sort + Manage). Created once in <body>; hidden until
     .show. Fixed/full-bleed; only opened from the phone-only triggers above. */
  .rm-sheet-scrim {
    position: fixed; inset: 0; z-index: 1200;
    background: rgba(0, 0, 0, .42); opacity: 0; pointer-events: none;
    transition: opacity .55s cubic-bezier(.22, .61, .36, 1);
  }
  .rm-sheet-scrim.show { opacity: 1; pointer-events: auto; }
  .rm-sheet {
    position: fixed; left: 0; right: 0; bottom: 0; z-index: 1201;
    background: var(--bg-pane); border-radius: 20px 20px 0 0;
    box-shadow: 0 -18px 40px -16px rgba(0, 0, 0, .5);
    transform: translateY(100%);
    transition: transform .55s cubic-bezier(.22, .61, .36, 1);
    padding-bottom: calc(env(safe-area-inset-bottom, 0px) + 8px);
    max-height: 80vh; display: flex; flex-direction: column;
  }
  .rm-sheet.show { transform: translateY(0); }
  .rm-sheet-grip { display: flex; justify-content: center; padding: 9px 0 3px; }
  .rm-sheet-grip span { width: 40px; height: 5px; border-radius: 3px; background: var(--line); }
  .rm-sheet-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 10px; padding: 6px 18px 10px; }
  .rm-sheet-head h3 { margin: 0; font: 600 16px var(--f-sans); color: var(--text-1); }
  .rm-sheet-head .sub { font: 12px var(--f-mono); color: var(--text-3); margin-top: 2px; }
  .rm-sheet-close { width: 32px; height: 32px; flex: 0 0 auto; border: 0; border-radius: 9px; background: var(--bg-elev-1); color: var(--text-1); cursor: pointer; font-size: 16px; line-height: 1; }
  .rm-sheet-body { overflow-y: auto; padding: 2px 12px 10px; }
  .rm-sheet-row {
    display: flex; align-items: center; gap: 13px; width: 100%;
    padding: 14px 10px; border: 0; background: transparent;
    border-bottom: 1px solid var(--line-soft); cursor: pointer;
    font: 500 15px var(--f-sans); color: var(--text-1); text-align: left;
  }
  .rm-sheet-row:last-child { border-bottom: 0; }
  .rm-sheet-row:active { background: var(--bg-elev-1); }
  .rm-sheet-row .ri {
    width: 36px; height: 36px; flex: 0 0 auto; border-radius: 10px;
    display: flex; align-items: center; justify-content: center;
    background: var(--bg-elev-1); color: var(--text-2);
  }
  .rm-sheet-row .ri svg { width: 19px; height: 19px; }
  .rm-sheet-row.primary .ri { background: var(--brass); color: #fff; }
  .rm-sheet-row .sub { display: block; font: 400 12px var(--f-sans); color: var(--text-3); margin-top: 1px; }
  .rm-sheet-row .rcheck { margin-left: auto; color: var(--brass); display: inline-flex; }
  .rm-sheet-row .rcheck svg { width: 18px; height: 18px; }

  /* ════════════════════════════════════════════════════════════════════
     MOBILE CUSTOMERS REDESIGN — Variant A "full-screen push" backbone.
     Phone-only (≤720px). The desktop customer TABLE + dense toolbar + pager
     are hidden; the SAME live customer rows render as tap-friendly cards
     (#rm-cu-cards, fed by rmRenderCustomerCards). Tap a card → the existing
     full-screen customer drawer (cuOpenDetail). Swipe a card left → Adjust /
     Block (the Variant C blend; both open the drawer then fire the wired
     flow). KPI strip → horizontal rail; tier filter → sticky chip row.
     Theme-aware (reuses app tokens). Desktop is untouched.
     Built to match design bundle: mobile/customers-a.html + fc-cust.css.
     See memory: feature_operator_mobile_miniapp.
     ════════════════════════════════════════════════════════════════════ */
  #rm-cu-cards { display: none; }
  #cu-mob-tiers { display: none; }
  @media (max-width: 720px) {
    /* Hide the desktop table and the whole filter/search toolbar on phones —
       the search row and tier chips aren't wanted in the mobile view.
       The PAGER stays: it stacks (see .t-pager's own phone rules). Hiding it
       here used to strand phone operators on page 1 of the roster. */
    #tab-customers .table-wrap { display: none; }
    #cu-toolbar { display: none; }

    /* Stat strip → horizontal snap rail (override the desktop 3-up grid). */
    #cu-stats {
      display: flex; grid-template-columns: none;
      gap: 10px; overflow-x: auto; -webkit-overflow-scrolling: touch;
      scroll-snap-type: x proximity; padding-bottom: 6px;
    }
    #cu-stats::-webkit-scrollbar { display: none; }
    #cu-stats > .cul-stat { flex: 0 0 62%; min-width: 160px; scroll-snap-align: start; }
    #cu-stats > .cul-stat.is-hidden { display: none; }

    /* Tier filter → sticky single-row chip scroller, always reachable. Pins to
       top:0 (not 56px) now that the brand bar scrolls away on phones, so it sits
       flush at the top instead of floating with a gap above it. */
    #cu-mob-tiers {
      display: none; gap: 8px; flex-wrap: nowrap;
      overflow-x: auto; -webkit-overflow-scrolling: touch;
      position: sticky; top: 0; z-index: 6;
      /* Solid bar (full-bleed + divider + shadow) so cards visibly scroll UNDER
         it; with bg-app it was see-through and content bled around the chips. */
      margin-left: -16px; margin-right: -16px;
      padding: 10px 16px; margin-bottom: 4px;
      background: var(--bg-pane);
      border-bottom: 1px solid var(--line);
      box-shadow: 0 8px 14px -12px rgba(0, 0, 0, .45);
      scroll-snap-type: x proximity;
    }
    #cu-mob-tiers::-webkit-scrollbar { display: none; }
    .cu-tchip {
      flex: 0 0 auto; scroll-snap-align: start;
      height: 34px; padding: 0 15px; border-radius: 999px;
      border: 1px solid var(--line); background: var(--bg-pane);
      color: var(--text-2); font: 500 13px var(--f-sans); cursor: pointer;
      -webkit-tap-highlight-color: transparent;
    }
    .cu-tchip.on { background: var(--text-1); color: var(--bg-pane); border-color: var(--text-1); }

    /* ── Card list ── */
    #rm-cu-cards { display: block; }
    .rm-cucard-wrap {
      position: relative; margin-bottom: 10px;
      border-radius: var(--r-lg); overflow: hidden;
    }
    /* Swipe-revealed action lane (sits behind the card). */
    .rm-cucard-actions { position: absolute; inset: 0; display: flex; justify-content: flex-end; }
    .rm-cucard-actions button {
      border: 0; cursor: pointer; width: 88px;
      display: flex; flex-direction: column; align-items: center; justify-content: center;
      gap: 5px; font: 600 12px var(--f-sans);
      -webkit-tap-highlight-color: transparent;
    }
    .rm-cucard-actions svg { width: 20px; height: 20px; }
    .rm-cu-adjust { background: linear-gradient(180deg, var(--brass-hi), var(--brass)); color: var(--brass-ink, #1A140A); }
    .rm-cu-block  { background: var(--danger, #E8556A); color: #fff; }
    .rm-cu-block.is-unblock { background: var(--accent); }

    .rm-cucard {
      position: relative; z-index: 1;
      background: var(--bg-pane);
      border: 1px solid var(--line-soft);
      border-radius: var(--r-lg);
      transition: transform .26s cubic-bezier(.16, 1, .3, 1);
      -webkit-tap-highlight-color: transparent;
      touch-action: pan-y;
    }
    .rm-cucard.swiped { transform: translateX(-176px); }
    /* header row (the tappable summary; tapping toggles the inline peek) */
    .rm-cu-head { display: flex; align-items: center; gap: 13px; padding: 13px 14px; cursor: pointer; border-radius: var(--r-lg); }
    .rm-cu-head:active { background: var(--bg-elev-1); }

    .rm-cu-av {
      flex: 0 0 auto; width: 44px; height: 44px; border-radius: 50%;
      display: flex; align-items: center; justify-content: center;
      font: 600 18px var(--f-sans);
      background: var(--text-1); color: var(--bg-pane);
    }
    .rm-cu-av.guest { background: rgba(127,122,114,.14); color: var(--text-3); }
    .rm-cu-av.guest svg { width: 20px; height: 20px; }

    .rm-cu-body { flex: 1; min-width: 0; }
    .rm-cu-name { display: flex; align-items: center; gap: 8px; font-size: 15.5px; font-weight: 600; color: var(--text-1); }
    .rm-cu-name .nm { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
    .rm-cu-name.guest .nm { color: var(--text-3); font-weight: 500; font-style: italic; }
    .rm-cu-meta { font: 11.5px var(--f-mono); color: var(--text-3); margin-top: 3px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

    .rm-cu-stat { flex: 0 0 auto; text-align: right; }
    .rm-cu-stat .big { font: 600 15px var(--f-mono); color: var(--text-1); font-variant-numeric: tabular-nums; }
    .rm-cu-stat .k { display: block; font: 10px var(--f-mono); letter-spacing: .06em; color: var(--text-4); margin-top: 1px; }

    .rm-cu-chev { flex: 0 0 auto; color: var(--text-4); display: flex; transition: transform .5s cubic-bezier(.22,.61,.36,1); }
    .rm-cu-chev svg { width: 18px; height: 18px; }
    .rm-cucard.open .rm-cu-chev { transform: rotate(90deg); }

    /* ── inline profile peek (Combined: tap a card to expand in place; the
       tristat reads from the list row, no extra fetch — Full profile opens the
       existing drawer for the deep view) ── */
    .rm-cu-exp { display: grid; grid-template-rows: 0fr; transition: grid-template-rows .55s cubic-bezier(.22,.61,.36,1); }
    .rm-cucard.open .rm-cu-exp { grid-template-rows: 1fr; }
    .rm-cu-exp > .inner { overflow: hidden; }
    .rm-cu-exp-pad { padding: 13px 14px 14px; border-top: 1px dashed var(--line-soft); }
    .rm-cu-tri { display: grid; grid-template-columns: 1fr 1fr; gap: 9px; margin-bottom: 12px; }
    .rm-cu-tri > div { background: var(--bg-elev-1); border-radius: 12px; padding: 11px 8px; text-align: center; }
    .rm-cu-tri b { display: block; font: 600 18px var(--f-sans); color: var(--text-1); font-variant-numeric: tabular-nums; }
    .rm-cu-tri span { display: block; font: 500 9px var(--f-mono); letter-spacing: .08em; text-transform: uppercase; color: var(--text-3); margin-top: 3px; }
    .rm-cu-exp-actions { display: grid; grid-template-columns: 1fr 1fr; gap: 9px; }
    .rm-cu-exp-actions button {
      border: 1px solid var(--line); background: var(--bg-pane); color: var(--text-1);
      border-radius: 11px; padding: 12px; font: 500 13.5px var(--f-sans); cursor: pointer;
      -webkit-tap-highlight-color: transparent;
    }
    .rm-cu-exp-actions button:active { background: var(--bg-elev-1); }
    .rm-cu-exp-actions button.primary { background: var(--brass); color: #fff; border-color: var(--brass); }

    .rm-cu-empty { text-align: center; color: var(--text-3); padding: 44px 20px; font-size: 13.5px; }
  }

  /* ════════════════════════════════════════════════════════════════════
     MOBILE DASHBOARD REDESIGN — Variant A "full-screen push".
     Phone-only (≤720px). The desktop bento + deep-dive are hidden; the SAME
     live DASH_DATA renders as a dark live hero + a grid of report tiles
     (#rm-dash, fed by rmRenderDash from dashRenderAll). Tap a tile → a
     full-screen report pushes in (#rm-dash-report), its body built from the
     same aggregates the desktop charts use (cloned/reformatted — no second
     fetch, no backend change). Period chips drive dashSetRange. Theme-aware;
     desktop untouched. Sibling of the Orders redesign.
     Built to match design bundle: mobile/dashboard-a.html.
     See memory: feature_operator_mobile_miniapp.
     ════════════════════════════════════════════════════════════════════ */
  #rm-dash { display: none; }
  #rm-dash-report { display: none; }
  @media (max-width: 720px) {
    /* Desktop dashboard surfaces off; the mobile view replaces them. */
    #tab-dashboard .db-head,
    #tab-dashboard .td-wrap { display: none; }
    #rm-dash { display: block; }

    /* Compact one-line header — eyebrow + title share a baseline row so the
       dashboard's front door (period chips + live hero) sits higher. */
    .rm-dh-title { display: flex; align-items: baseline; gap: 8px; padding: 0 2px; }
    .rm-dh-title .eyebrow { font: 500 10px/1 var(--f-mono); letter-spacing: .18em; text-transform: uppercase; color: var(--brass); }
    .rm-dh-title h2 { margin: 0; font-size: 18px; font-weight: 600; letter-spacing: -.02em; }

    /* period chips */
    .rm-dh-periods { display: flex; gap: 7px; margin: 8px 0 4px; }
    .rm-dh-periods button {
      flex: 1; height: 36px; border-radius: 10px;
      border: 1px solid var(--line-soft); background: var(--bg-pane);
      color: var(--text-3); font: 500 13px var(--f-sans); cursor: pointer;
      -webkit-tap-highlight-color: transparent;
    }
    .rm-dh-periods button.active { background: var(--text-1); color: var(--bg-pane); border-color: var(--text-1); }

    /* dark live hero (always dark, like the desktop #db-hero) */
    .rm-dh-hero {
      margin: 8px 0 4px; padding: 16px; border-radius: 18px; color: #F2EFE7;
      background: linear-gradient(160deg, #15130E, #221C12);
      box-shadow: 0 18px 40px -24px rgba(40, 34, 20, .7);
    }
    .rm-dh-live { display: flex; align-items: center; gap: 7px; font: 500 10px var(--f-mono); letter-spacing: .18em; text-transform: uppercase; color: var(--td-sage-ink); }
    .rm-dh-live .d { width: 7px; height: 7px; border-radius: 50%; background: var(--accent); box-shadow: 0 0 0 0 rgba(163, 180, 159, .6); animation: rmDhPulse 2s infinite; }
    @keyframes rmDhPulse { 0% { box-shadow: 0 0 0 0 rgba(163, 180, 159, .5); } 70% { box-shadow: 0 0 0 7px rgba(163, 180, 159, 0); } 100% { box-shadow: 0 0 0 0 rgba(163, 180, 159, 0); } }
    .rm-dh-big { font: 600 42px var(--f-sans); letter-spacing: -.03em; margin: 10px 0 2px; font-variant-numeric: tabular-nums; }
    .rm-cu { font-variant-numeric: tabular-nums; }
    .rm-dh-cap { font-size: 12px; color: #B9B3A4; display: flex; align-items: center; gap: 8px; }
    .rm-dh-cap .rm-dh-delta.up { color: #9FC6A3; }
    .rm-dh-cap .rm-dh-delta.down { color: #D9A38A; }
    .rm-dh-mini { display: flex; gap: 18px; margin-top: 14px; padding-top: 14px; border-top: 1px solid rgba(255, 255, 255, .12); }
    .rm-dh-mini .m { min-width: 0; }
    .rm-dh-mini .v { font: 600 18px var(--f-sans); font-variant-numeric: tabular-nums; }
    .rm-dh-mini .k { font: 500 9px var(--f-mono); letter-spacing: .14em; text-transform: uppercase; color: var(--td-text2); margin-top: 2px; }

    /* report tile grid */
    .rm-dh-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; margin-top: 12px; }
    .rm-dh-tile {
      text-align: left; background: var(--bg-pane); border: 1px solid var(--line-soft);
      border-radius: 16px; padding: 14px; cursor: pointer; min-height: 116px;
      display: flex; flex-direction: column; transition: transform .08s;
      -webkit-tap-highlight-color: transparent; color: inherit;
    }
    .rm-dh-tile:active { transform: scale(.98); }
    .rm-dh-tile .ic { width: 32px; height: 32px; border-radius: 9px; background: var(--bg-elev-1); display: flex; align-items: center; justify-content: center; color: var(--brass); margin-bottom: auto; flex: none; }
    .rm-dh-tile .ic svg { width: 17px; height: 17px; }
    .rm-dh-tile .lab { font: 500 9.5px var(--f-mono); letter-spacing: .14em; text-transform: uppercase; color: var(--text-3); margin-top: 12px; }
    .rm-dh-tile .val { font: 600 22px var(--f-sans); letter-spacing: -.02em; margin-top: 3px; color: var(--text-1); font-variant-numeric: tabular-nums; }
    .rm-dh-tile .spk { margin-top: 8px; height: 26px; color: var(--accent); }
    .rm-dh-tile .spk .db-spark { width: 100%; height: 100%; display: block; }

    .rm-dh-tile.wide { grid-column: 1 / -1; flex-direction: row; align-items: center; justify-content: space-between; gap: 12px; min-height: 0; }
    .rm-dh-tile.wide .l { display: flex; align-items: center; gap: 12px; min-width: 0; }
    .rm-dh-tile.wide .ic { margin: 0; }
    .rm-dh-tile.wide .tx { min-width: 0; display: flex; flex-direction: column; }
    .rm-dh-tile.wide .lab { margin: 0; }
    .rm-dh-tile.wide .val { margin: 2px 0 0; font-size: 16px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
    .rm-dh-tile.alarm .ic { background: var(--warn-wash, rgba(201, 149, 74, .16)); }
    .rm-dh-badge { flex: none; font: 600 11px var(--f-sans); color: var(--brass); background: var(--warn-wash, rgba(201, 149, 74, .16)); padding: 4px 10px; border-radius: 999px; }
    .rm-dh-tile.wide .arrow { color: var(--text-3); flex: none; display: flex; }
    .rm-dh-tile.wide .arrow svg { width: 18px; height: 18px; }
    .rm-dh-empty { text-align: center; color: var(--text-3); padding: 30px 16px; font-size: 13px; }

    /* ── full-screen report (push). Lives inside #tab-dashboard, so it hides
       automatically when another tab becomes active (section{display:none}). ── */
    #rm-dash-report {
      display: flex; flex-direction: column; position: fixed; inset: 0; z-index: 80;
      background: var(--bg-app); transform: translateX(100%);
      /* visibility:hidden at rest so the panel can't flash at the right edge if
         the transform animates on first paint; the delayed visibility flip keeps
         it on-screen through the .3s slide-out when closing. */
      visibility: hidden;
      transition: transform .55s cubic-bezier(.22, .61, .36, 1), visibility 0s linear .55s;
    }
    #rm-dash-report.show { transform: none; visibility: visible; transition: transform .55s cubic-bezier(.22, .61, .36, 1); }
    @media (prefers-reduced-motion: reduce) { #rm-dash-report { transition: none; } }
    .rm-dh-rnav {
      flex: none; display: flex; align-items: center; gap: 4px; padding: 0 8px;
      padding-top: env(safe-area-inset-top); min-height: 54px;
      background: var(--bg-pane); border-bottom: 1px solid var(--line-soft);
    }
    .rm-dh-back { width: 42px; height: 42px; border: 0; border-radius: 11px; background: transparent; color: var(--text-1); display: flex; align-items: center; justify-content: center; cursor: pointer; }
    .rm-dh-back:active { background: var(--bg-elev-1); }
    .rm-dh-back svg { width: 22px; height: 22px; }
    .rm-dh-rtitle { font: 600 16px var(--f-sans); color: var(--text-1); }
    .rm-dh-rbody { flex: 1; overflow-y: auto; -webkit-overflow-scrolling: touch; padding: 14px 16px calc(30px + env(safe-area-inset-bottom)); }

    .rm-dh-block { background: var(--bg-pane); border: 1px solid var(--line-soft); border-radius: 14px; padding: 15px; margin-bottom: 14px; }
    .rm-dh-block > .h { font: 500 10px var(--f-mono); letter-spacing: .16em; text-transform: uppercase; color: var(--brass); margin-bottom: 12px; }
    .rm-dh-kpis { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; margin-bottom: 14px; }
    .rm-dh-stat { background: var(--bg-pane); border: 1px solid var(--line-soft); border-radius: 12px; padding: 12px 13px; }
    .rm-dh-stat .lbl { font-size: 11px; color: var(--text-3); }
    .rm-dh-stat .v { font: 600 20px var(--f-sans); color: var(--text-1); margin-top: 3px; font-variant-numeric: tabular-nums; }
    .rm-dh-stat .v.brass { color: var(--brass); }
    .rm-dh-trend { position: relative; height: 120px; color: var(--accent); touch-action: pan-y; cursor: crosshair; }
    /* Faint cue while a long-press reading is active. */
    .rm-dh-trend.is-reading { background: color-mix(in srgb, var(--accent) 5%, transparent); border-radius: 8px; }
    .rm-dh-trend .db-spark { width: 100%; height: 100%; display: block; }
    .rm-dh-heatscroll { overflow-x: auto; -webkit-overflow-scrolling: touch; }

    /* ── Combined design: swipeable KPI carousel + inline accordion ──
       Layered over the Variant-A hero. The carousel scans secondary KPIs
       (each card opens a .rm-sheet breakdown); the accordion expands report
       sections in place (bodies cloned from the same desktop chart nodes).
       Built to match design bundle: mobile/dashboard-combined.html. */
    .rm-dh-hint { display: flex; align-items: center; gap: 8px; margin: 12px 2px 2px; font-size: 11.5px; color: var(--text-3); }
    .rm-dh-carousel {
      display: flex; gap: 11px; overflow-x: auto; margin: 8px -2px 0; padding: 0 2px 6px;
      scroll-snap-type: x mandatory; -webkit-overflow-scrolling: touch;
    }
    .rm-dh-carousel::-webkit-scrollbar { display: none; }
    .rm-dh-kpi {
      flex: 0 0 165px; scroll-snap-align: start; text-align: left; color: inherit;
      background: var(--bg-pane); border: 1px solid var(--line-soft); border-radius: 16px;
      padding: 15px; cursor: pointer; transition: transform .08s; -webkit-tap-highlight-color: transparent;
    }
    .rm-dh-kpi:active { transform: scale(.98); }
    .rm-dh-kpi .lab { font: 500 9.5px var(--f-mono); letter-spacing: .14em; text-transform: uppercase; color: var(--text-3); }
    .rm-dh-kpi .val { font: 600 26px var(--f-sans); letter-spacing: -.02em; margin-top: 4px; color: var(--text-1); font-variant-numeric: tabular-nums; }
    .rm-dh-kpi .delta { font: 500 11px var(--f-mono); margin-top: 4px; }
    .rm-dh-kpi .delta.up { color: var(--td-sage-ink); }
    .rm-dh-kpi .delta.down { color: var(--danger, #b1483a); }
    .rm-dh-kpi .spk { display: block; margin-top: 11px; height: 30px; color: var(--accent); }
    .rm-dh-kpi .spk .db-spark { width: 100%; height: 100%; display: block; }
    .rm-dh-dots { display: flex; gap: 5px; justify-content: center; padding: 2px 0 4px; }
    .rm-dh-dots i { width: 5px; height: 5px; border-radius: 50%; background: var(--line); transition: width .2s, background .2s; }
    .rm-dh-dots i.on { background: var(--brass); width: 16px; border-radius: 3px; }

    /* Swipe-dot pager for the Orders / Customers KPI strips. Hidden by default —
       on desktop those strips are a full grid (no overflow); the mobile rule
       below (in the max-width:720px block) flips it on. Mirrors .rm-dh-dots. */
    .fc-kpi-dots { display: none; gap: 5px; justify-content: center; padding: 0 0 10px; margin-top: -4px; }
    .fc-kpi-dots i { width: 5px; height: 5px; border-radius: 50%; background: var(--line); transition: width .2s, background .2s; }
    .fc-kpi-dots i.on { background: var(--brass); width: 16px; border-radius: 3px; }
    /* Phone-only: reveal the swipe dots under the scrollable KPI strips. Must
       come AFTER the display:none default above so it wins on equal specificity. */
    @media (max-width: 720px) { .fc-kpi-dots { display: flex; } }

    .rm-dh-acc { display: flex; flex-direction: column; gap: 10px; margin-top: 8px; }
    .rm-dh-ai { background: var(--bg-pane); border: 1px solid var(--line-soft); border-radius: 15px; overflow: hidden; }
    .rm-dh-ai-head {
      display: flex; align-items: center; gap: 12px; width: 100%; padding: 15px;
      border: 0; background: transparent; cursor: pointer; text-align: left; color: inherit;
      font-family: inherit; -webkit-tap-highlight-color: transparent;
    }
    .rm-dh-ai-head .ic { width: 32px; height: 32px; border-radius: 10px; background: var(--bg-elev-1); display: flex; align-items: center; justify-content: center; color: var(--brass); flex: none; }
    .rm-dh-ai-head .ic svg { width: 17px; height: 17px; }
    .rm-dh-ai-head .tx { flex: 1; min-width: 0; }
    .rm-dh-ai-head .t { font: 600 14.5px var(--f-sans); color: var(--text-1); display: block; }
    .rm-dh-ai-head .hv { font: 600 15px var(--f-sans); color: var(--text-1); margin-right: 4px; font-variant-numeric: tabular-nums; }
    .rm-dh-ai-head .hv.brass { color: var(--brass); }
    .rm-dh-ai-head .chev { color: var(--text-3); transition: transform .5s cubic-bezier(.22,.61,.36,1); display: flex; flex: none; }
    .rm-dh-ai-head .chev svg { width: 18px; height: 18px; }
    .rm-dh-ai.open .rm-dh-ai-head .chev { transform: rotate(90deg); }
    .rm-dh-ai-body { display: grid; grid-template-rows: 0fr; transition: grid-template-rows .55s cubic-bezier(.22,.61,.36,1); }
    .rm-dh-ai.open .rm-dh-ai-body { grid-template-rows: 1fr; }
    .rm-dh-ai-body > .inner { overflow: hidden; }
    .rm-dh-ai-pad { padding: 0 14px 16px; }
  }

  /* ════════════════════════════════════════════════════════════════════
     MOBILE SETTINGS REDESIGN — Variant A "settings hub" (iOS-Settings depth).
     Phone-only (≤720px). On desktop the settings sidebar (#settings-nav) and
     body (.settings-body, which holds every .sub-panel + the shared save bar)
     sit side-by-side. On phones the sidebar becomes a full-width tappable hub
     list, and the body becomes a full-screen push-over panel with its own back
     header and a pinned save bar — one control surface per screen. Reuses the
     SAME live controls (accent grid, notification strip, language, mascots,
     footer) and the existing #settings-nav sub-switcher JS; only the flow
     changes. Theme-aware; desktop untouched. Sibling of the Orders / Dashboard
     / Customers redesigns. Built to match design bundle: mobile/settings-a.html
     + fc-settings.css. See memory: feature_operator_mobile_miniapp.
     ════════════════════════════════════════════════════════════════════ */
  .rm-set-hubhead, .rm-set-pnav { display: none; }
  @media (max-width: 720px) {
    /* layout → single column; the body overlays as a push panel */
    #tab-config .settings-layout { display: block; position: relative; }

    /* hub head */
    #tab-config .rm-set-hubhead { display: block; padding: 2px 2px 14px; }
    #tab-config .rm-set-hubhead .eyebrow { font: 500 10px/1 var(--f-sans); letter-spacing: .18em; text-transform: uppercase; color: var(--td-text2); }
    #tab-config .rm-set-hubhead h2 { margin: 5px 0 0; font-size: 22px; font-weight: 600; letter-spacing: -.02em; }

    /* nav → iOS-style settings list */
    #tab-config #settings-nav {
      position: static; top: auto; padding: 0; gap: 0;
      background: transparent; border: 0; border-radius: var(--r-lg); overflow: hidden;
    }
    #tab-config #settings-nav button, #tab-config #settings-nav a {
      background: var(--bg-pane); border: 1px solid var(--line); border-top: 0;
      border-radius: 0; padding: 14px; gap: 13px; font-size: 15px; color: var(--text-1);
      margin: 0;
    }
    #tab-config #settings-nav button:first-child, #tab-config #settings-nav a:first-child { border-top: 1px solid var(--line); border-radius: var(--r-lg) var(--r-lg) 0 0; }
    #tab-config #settings-nav button:last-child, #tab-config #settings-nav a:last-child { border-radius: 0 0 var(--r-lg) var(--r-lg); }
    #tab-config #settings-nav button.active { background: var(--bg-pane); box-shadow: none; }
    #tab-config #settings-nav button svg, #tab-config #settings-nav a svg {
      width: 34px; height: 34px; padding: 8px; box-sizing: border-box; flex: none;
      background: var(--bg-elev-1); border-radius: 10px; color: var(--brass);
    }
    #tab-config #settings-nav button.active svg { color: var(--brass); }
    /* trailing chevron */
    #tab-config #settings-nav button::after, #tab-config #settings-nav a::after {
      content: ""; width: 8px; height: 8px; margin-left: auto; flex: none;
      border-right: 2px solid var(--text-3); border-top: 2px solid var(--text-3);
      transform: rotate(45deg);
    }

    /* body → full-screen push panel (lives inside #tab-config, so it auto-hides
       on tab switch via section{display:none}) */
    #tab-config .settings-body {
      position: fixed; inset: 0; z-index: 80; background: var(--bg-app);
      display: flex; flex-direction: column; padding: 0;
      transform: translateX(100%);
      transition: transform .55s cubic-bezier(.22, .61, .36, 1);
    }
    #tab-config .settings-body.rm-open { transform: none; }
    @media (prefers-reduced-motion: reduce) { #tab-config .settings-body { transition: none; } }

    /* back header */
    #tab-config .rm-set-pnav {
      flex: none; display: flex; align-items: center; gap: 4px;
      height: 52px; padding: 0 8px;
      background: var(--bg-pane); border-bottom: 1px solid var(--line);
    }
    #tab-config .rm-set-back {
      width: 40px; height: 40px; border: 0; background: transparent; color: var(--text-1);
      display: flex; align-items: center; justify-content: center; cursor: pointer;
      border-radius: 10px; -webkit-tap-highlight-color: transparent;
    }
    #tab-config .rm-set-back:active { background: var(--bg-elev-1); }
    #tab-config .rm-set-back svg { width: 22px; height: 22px; }
    #tab-config .rm-set-title { font: 600 16px var(--f-sans); color: var(--text-1); }

    /* the active panel scrolls between the back header and the pinned save bar */
    #tab-config .settings-body > .sub-panel {
      flex: 1; overflow-y: auto; -webkit-overflow-scrolling: touch;
      padding: 14px 16px 28px;
    }
    #tab-config .sub-panel > h2:first-child { display: none; }
    #tab-config .group-card { padding: var(--s4); }

    /* shared save bar → flat pinned footer of the push panel */
    #tab-config .settings-body > .save-bar {
      position: static; flex: none; margin: 0; background: var(--bg-pane);
      border-top: 1px solid var(--line);
      padding: 12px 16px calc(14px + env(safe-area-inset-bottom));
    }
    #tab-config .settings-body > .save-bar .btn { flex: 1; justify-content: center; }

    /* ── Combined: touch-native Me-page menu. The tiny show/hide checkbox
       becomes a switch and the rows get phone-sized tap targets — reuses the
       existing checkbox logic (renderMeRows), pure presentation. ── */
    #tab-config #me-rows-list > li { padding: 13px 14px; gap: 12px; }
    #tab-config #me-rows-list label { gap: 12px; }
    #tab-config #me-rows-list .me-row-cb {
      appearance: none; -webkit-appearance: none; flex: 0 0 auto; cursor: pointer; margin: 0;
      width: 46px; height: 28px; border-radius: 999px; position: relative;
      background: var(--line-strong, var(--line)); transition: background .2s;
    }
    #tab-config #me-rows-list .me-row-cb::after {
      content: ""; position: absolute; top: 3px; left: 3px; width: 22px; height: 22px;
      border-radius: 50%; background: #fff; transition: transform .2s; box-shadow: 0 1px 3px rgba(0,0,0,.3);
    }
    #tab-config #me-rows-list .me-row-cb:checked { background: var(--accent); }
    #tab-config #me-rows-list .me-row-cb:checked::after { transform: translateX(18px); }
    #tab-config #me-rows-list .me-row-up,
    #tab-config #me-rows-list .me-row-down { padding: 7px 14px !important; font-size: 15px; }
  }

/* ════════════════════════════════════════════════════════════════════════════
   GUIDED PRODUCT WIZARD (#pw-bg) — reuses the global vw-* classes (goals/dots/
   foot/lab/help) from the coupon wizard; these are the pw-* specifics. PC only.
   ════════════════════════════════════════════════════════════════════════════ */
#pw-body.vw-leaving  { opacity: 0; transition: opacity .22s var(--ease-out, cubic-bezier(.16,1,.3,1)); }
#pw-body.vw-entering { animation: vwStepIn .45s var(--ease-out, cubic-bezier(.16,1,.3,1)); }
@media (prefers-reduced-motion: reduce) { #pw-body, #pw-body.vw-leaving, #pw-body.vw-entering { transition: none; animation: none; opacity: 1; } }

#pw-bg .field { margin: 0 0 14px; }
#pw-bg .field input[type="text"], #pw-bg .field input[type="number"], #pw-bg select, #pw-bg textarea {
  font: inherit; padding: 9px 11px; border: 1px solid var(--line, #d9d3c6); border-radius: 9px; background: var(--bg-input, #fff); color: var(--ink, #1d1d1a);
}
#pw-bg .field input:focus, #pw-bg select:focus, #pw-bg textarea:focus { outline: none; border-color: var(--brass); }

.pw-row { display: flex; flex-wrap: wrap; align-items: center; }
.pw-row-end { display: flex; gap: 8px; justify-content: flex-end; }
.pw-row2 { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; }
@media (max-width: 560px) { .pw-row2 { grid-template-columns: 1fr; } }

.pw-photo { width: 64px; height: 64px; flex: none; border: 1.5px dashed var(--line, #d9d3c6); border-radius: 11px; display: flex; align-items: center; justify-content: center; font-size: 24px; color: var(--muted, #6b6b63); cursor: pointer; background: var(--bg-elev-1, #faf7f0); }
.pw-photo:hover { border-color: var(--brass); }

.pw-seg { display: inline-flex; gap: 0; border: 1px solid var(--line, #d9d3c6); border-radius: 10px; overflow: hidden; }
.pw-seg-btn { font: inherit; padding: 10px 18px; background: var(--bg-input, #fff); border: none; cursor: pointer; color: var(--ink, #1d1d1a); }
.pw-seg-btn + .pw-seg-btn { border-left: 1px solid var(--line, #d9d3c6); }
.pw-seg-btn.on { background: var(--accent); color: #fff; }

.pw-check { display: inline-flex; align-items: center; gap: 8px; font-size: 13.5px; font-weight: 500; cursor: pointer; margin: 4px 0; }
.pw-check input { width: 16px; height: 16px; }

.pw-sizes { display: flex; flex-direction: column; gap: 8px; margin-bottom: 10px; }
.pw-size-row { display: flex; align-items: center; gap: 8px; }
.pw-size-row input[type="text"] { flex: 1 1 auto; }
.pw-size-row input[type="number"] { width: 84px; }
.pw-size-cur { display: inline-flex; align-items: center; gap: 2px; color: var(--muted, #6b6b63); }
.pw-size-cur input { width: 84px; }
.pw-vars { display: flex; flex-direction: column; gap: 10px; margin-bottom: 12px; }
.pw-var { border: 1px solid var(--line, #d9d3c6); border-radius: 11px; padding: 10px 12px; background: var(--bg-elev-1, #faf7f0); }
.pw-var .pw-row2 input { width: 100%; box-sizing: border-box; }
.pw-var-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 8px; min-height: 22px; }
.pw-var-n { font-size: 11.5px; font-weight: 600; letter-spacing: .04em; text-transform: uppercase; color: var(--muted, #6b6b63); }
.pw-var-line { display: flex; align-items: center; gap: 8px; margin-top: 8px; }
.pw-var-wt { flex: 1 1 auto; }
.pw-var-stk { width: 84px; }
.pw-x { background: none; border: none; color: var(--muted, #6b6b63); cursor: pointer; font-size: 13px; padding: 4px 6px; border-radius: 6px; }
.pw-x:hover { color: var(--danger, #b65656); background: rgba(182,86,86,.08); }
.pw-x-spacer, .pw-radio-spacer { width: 26px; flex: none; display: inline-block; }

.pw-chips { display: flex; flex-wrap: wrap; gap: 8px 14px; padding-top: 4px; }
.pw-chip { display: inline-flex; align-items: center; gap: 7px; font-weight: 500; cursor: pointer; }
.pw-chip input { width: 15px; height: 15px; }

.pw-opts { display: flex; flex-direction: column; gap: 12px; }
.pw-opt { border: 1px solid var(--line, #d9d3c6); border-radius: 12px; padding: 14px; background: var(--bg-elev-1, #faf7f0); }
.pw-opt-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 8px; }
.pw-opt-tag { font-size: 11px; letter-spacing: .06em; text-transform: uppercase; font-weight: 600; color: var(--brass); }
.pw-opt-q { width: 100%; box-sizing: border-box; font: inherit; padding: 8px 10px; border: 1px solid var(--line, #d9d3c6); border-radius: 8px; background: var(--bg-input, #fff); margin-bottom: 8px; }
.pw-choices { display: flex; flex-direction: column; gap: 6px; margin-bottom: 8px; }
.pw-choice { display: flex; align-items: center; gap: 8px; }
.pw-clab { flex: 1 1 auto; font: inherit; padding: 7px 9px; border: 1px solid var(--line, #d9d3c6); border-radius: 8px; background: var(--bg-input, #fff); }
.pw-cur { display: inline-flex; align-items: center; gap: 2px; color: var(--muted, #6b6b63); white-space: nowrap; }
.pw-cur input { width: 72px; font: inherit; padding: 7px 8px; border: 1px solid var(--line, #d9d3c6); border-radius: 8px; }
.pw-addc { margin-top: 2px; }
.pw-link { display: inline-flex; align-items: center; gap: 4px; font-size: 12px; padding: 4px 8px; border-radius: 999px; background: rgba(163,180,159,.12); color: var(--accent); }
.pw-link.on { font-weight: 600; }
.pw-link-btn { white-space: nowrap; }
.pw-linkpick { margin-top: 6px; }
.pw-linkpick input { width: 100%; box-sizing: border-box; font: inherit; padding: 7px 9px; border: 1px solid var(--line, #d9d3c6); border-radius: 8px; }

.pw-pick-list { max-height: 240px; overflow: auto; border: 1px solid var(--line, #d9d3c6); border-radius: 9px; }
.pw-pick { padding: 9px 12px; cursor: pointer; border-bottom: 1px solid var(--line, #ece7db); font-size: 14px; }
.pw-pick:last-child { border-bottom: none; }
.pw-pick:hover { background: var(--bg-elev-1, #faf7f0); }
.pw-pick.muted { color: var(--muted, #6b6b63); cursor: default; }

.pw-review { display: flex; gap: 16px; align-items: flex-start; }
.pw-rev-photo { width: 96px; height: 96px; flex: none; border-radius: 12px; background-size: cover; background-position: center; background-color: var(--bg-elev-1, #faf7f0); }
.pw-rev-photo-empty { display: flex; align-items: center; justify-content: center; font-size: 32px; color: var(--muted, #6b6b63); }
.pw-rev-rows { flex: 1 1 auto; display: flex; flex-direction: column; gap: 7px; }
.pw-rev-row { display: flex; gap: 12px; font-size: 14px; }
.pw-rev-k { flex: 0 0 92px; color: var(--muted, #6b6b63); }
.pw-rev-v { flex: 1 1 auto; color: var(--ink, #1d1d1a); }
.pw-warn { margin-top: 14px; padding: 10px 13px; border: 1px solid var(--brass); border-radius: 9px; background: rgba(201,149,74,.08); font-size: 13px; }

/* The "new version available" dock lived here. It is gone: versionWatch
   (10-extras.js) now reloads silently once the tab is out of sight or idle
   and nothing is unsaved, so there is nothing to show the operator. */

/* ─────────────────────────────────────────────────────────────────────────
   New-tenant setup guide (part 17). "Open your store" checklist, dashboard
   card, welcome / done / chooser modals. Uses the theme tokens throughout so
   it tracks dark + light. Tea (--accent) = done; Brass (--brass) = next / CTA.
   ───────────────────────────────────────────────────────────────────────── */
  .tabs button.tabs-setup { color: var(--brass); }
  .tab-badge.setup-badge {
    background: var(--brass); color: var(--accent-ink);
    font-family: var(--f-mono); font-size: 10px; font-weight: 700;
    min-width: 0; padding: 1px 7px; border-radius: 999px;
  }

  .su-loading { padding: 40px; text-align: center; color: var(--text-3); }

  .su-card { max-width: 860px; }
  .su-ghead {
    display: flex; align-items: center; justify-content: space-between; gap: 18px;
    padding: 0 0 18px; margin-bottom: 4px; border-bottom: 1px solid var(--line);
  }
  .su-ghead h2 { font-size: 20px; }
  .su-sub { margin: 6px 0 0; font-size: 13px; color: var(--text-2); }

  .su-ring { position: relative; flex: none; }
  .su-ring svg { display: block; }
  .su-ring-lbl {
    position: absolute; inset: 0; display: grid; place-items: center;
    font-family: var(--f-mono); font-weight: 700; color: var(--text-1);
  }

  .su-tasks { margin-top: 6px; }
  .su-task {
    display: flex; align-items: center; gap: 15px;
    padding: 16px 4px; border-bottom: 1px solid var(--line-soft);
  }
  .su-task:last-child { border-bottom: 0; }
  .su-st {
    width: 32px; height: 32px; border-radius: 999px; flex: none;
    display: grid; place-items: center;
    border: 2px solid var(--line-strong); color: var(--text-3);
    background: var(--bg-elev-1);
  }
  .su-st svg { width: 15px; height: 15px; }
  .su-task.now .su-st { border-color: var(--brass); color: var(--brass); }
  .su-task.done .su-st { background: var(--accent); border-color: var(--accent); color: var(--accent-ink); }
  .su-task.opt .su-st { border-style: dashed; }
  .su-tx { flex: 1; min-width: 0; }
  .su-tx b { display: block; font-size: 14.5px; color: var(--text-1); }
  .su-task.done .su-tx b { color: var(--text-3); text-decoration: line-through; text-decoration-thickness: 1px; }
  .su-tx span { display: block; font-size: 12.5px; color: var(--text-3); margin-top: 3px; line-height: 1.45; }
  .su-time {
    font-family: var(--f-mono); font-size: 11px; color: var(--text-3);
    background: var(--bg-elev-1); border: 1px solid var(--line);
    border-radius: 999px; padding: 3px 10px; flex: none;
  }
  .su-donechip { display: inline-flex; align-items: center; gap: 5px; font-size: 12px; font-weight: 700; color: var(--accent); }
  .su-donechip svg { width: 12px; height: 12px; }
  .su-divider {
    padding: 14px 4px 6px; font-family: var(--f-mono); font-size: 10.5px;
    font-weight: 700; letter-spacing: .13em; text-transform: uppercase; color: var(--text-3);
  }

  /* Dashboard continue-setup card */
  #setup-dash-card { margin-bottom: var(--s5); }
  .su-dashcard {
    display: flex; align-items: center; gap: 18px;
    background: var(--bg-pane); border: 1px solid var(--line);
    border-left: 3px solid var(--brass); border-radius: var(--r-xl);
    padding: 16px 20px; box-shadow: var(--sh-1);
  }
  .su-dashcard-tx { flex: 1; min-width: 0; }
  .su-dashcard-tx b { display: block; font-size: 14.5px; color: var(--text-1); }
  .su-dashcard-tx span { display: block; font-size: 12.5px; color: var(--text-2); margin-top: 3px; }

  /* Modals (welcome / done / chooser) */
  .su-modal-ov {
    position: fixed; inset: 0; z-index: 9998; padding: 24px;
    background: var(--bg-overlay); display: flex; align-items: center; justify-content: center;
    opacity: 0; transition: opacity var(--t-fast) ease;
  }
  .su-modal-ov.open { opacity: 1; }
  .su-modal {
    background: var(--bg-pane); border: 1px solid var(--line-strong);
    border-radius: var(--r-xl); box-shadow: var(--sh-3);
    width: 100%; max-width: 500px; max-height: 90vh; overflow-y: auto;
    padding: 22px 24px;
  }
  .su-modal.wide { max-width: 760px; }
  .su-mhead { display: flex; align-items: flex-start; justify-content: space-between; gap: 14px; }
  .su-mhead h3 { margin: 0; font-size: 18px; color: var(--text-1); }
  .su-x {
    border: 1px solid var(--line); background: var(--bg-elev-1); color: var(--text-2);
    border-radius: var(--r-md); width: 30px; height: 30px; flex: none; cursor: pointer;
  }
  .su-x:hover { color: var(--text-1); border-color: var(--line-strong); }
  .su-mnote { margin: 10px 0 4px; font-size: 13.5px; color: var(--text-2); line-height: 1.55; }
  .su-mfoot { display: flex; align-items: center; justify-content: flex-end; gap: 10px; margin-top: 20px; }
  .su-mfoot .btn { min-width: 0; }

  .su-wsteps { display: grid; grid-template-columns: repeat(5, 1fr); gap: 10px; margin: 18px 0 4px; }
  .su-wstep {
    background: var(--bg-elev-1); border: 1px solid var(--line);
    border-radius: var(--r-lg); padding: 14px 10px; text-align: center;
  }
  .su-wico {
    display: grid; place-items: center; width: 36px; height: 36px; margin: 0 auto 9px;
    border-radius: var(--r-md); background: color-mix(in srgb, var(--brass) 16%, transparent); color: var(--brass);
  }
  .su-wico svg { width: 17px; height: 17px; }
  .su-wstep b { display: block; font-size: 12.5px; color: var(--text-1); line-height: 1.35; }
  .su-wt { display: block; font-family: var(--f-mono); font-size: 10.5px; color: var(--text-3); margin-top: 5px; }
  .su-promise {
    display: flex; gap: 13px; align-items: flex-start; margin: 18px 0 4px;
    background: color-mix(in srgb, var(--accent) 12%, transparent);
    border: 1px solid color-mix(in srgb, var(--accent) 30%, transparent);
    border-radius: var(--r-lg); padding: 14px 16px;
  }
  .su-promise svg { width: 18px; height: 18px; color: var(--accent); flex: none; margin-top: 1px; }
  .su-promise p { margin: 0; font-size: 13px; color: var(--text-1); line-height: 1.55; font-weight: 500; }

  .su-choose { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; margin: 16px 0 4px; }
  .su-choice {
    display: flex; flex-direction: column; align-items: flex-start; gap: 4px;
    background: var(--bg-elev-1); border: 1px solid var(--line); border-radius: var(--r-lg);
    padding: 16px; cursor: pointer; text-align: left; color: var(--text-1);
    transition: border-color var(--t-fast) ease, background var(--t-fast) ease;
  }
  .su-choice:hover { border-color: var(--brass); background: var(--bg-elev-2); }
  .su-choice-ic {
    display: grid; place-items: center; width: 34px; height: 34px; margin-bottom: 6px;
    border-radius: var(--r-md); background: color-mix(in srgb, var(--brass) 16%, transparent); color: var(--brass);
  }
  .su-choice-ic svg { width: 16px; height: 16px; }
  .su-choice b { font-size: 14px; }
  .su-choice span { font-size: 12px; color: var(--text-3); }

  .su-done-hero { text-align: center; padding: 12px 8px 4px; }
  .su-done-big {
    width: 60px; height: 60px; border-radius: 999px; margin: 0 auto 14px;
    display: grid; place-items: center; background: var(--accent); color: var(--accent-ink);
  }
  .su-done-big svg { width: 26px; height: 26px; }
  .su-done-hero h3 { margin: 0 0 6px; font-size: 20px; color: var(--text-1); }
  .su-done-hero p { margin: 0; font-size: 13.5px; color: var(--text-2); line-height: 1.6; }
  .su-pend {
    margin-top: 18px; background: color-mix(in srgb, var(--brass) 12%, transparent);
    border: 1px solid color-mix(in srgb, var(--brass) 28%, transparent);
    border-radius: var(--r-lg); padding: 15px 17px;
  }
  .su-pend > b { font-size: 13.5px; color: var(--text-1); }
  .su-pend-row { display: flex; align-items: center; gap: 11px; margin-top: 11px; }
  .su-pend-row svg { width: 14px; height: 14px; color: var(--brass); flex: none; }
  .su-pend-row span { flex: 1; font-size: 12.8px; color: var(--text-2); }

  /* "Tell us about your store" wizard — one question per step */
  .su-wiz-crumb {
    font-family: var(--f-mono); font-size: 11px; letter-spacing: .06em;
    text-transform: uppercase; color: var(--text-3);
  }
  .su-wiz-dots { display: flex; gap: 7px; margin: 14px 0 4px; }
  .su-wiz-dot {
    height: 5px; flex: 1; border-radius: 999px; background: var(--line);
    transition: background var(--t-fast) ease;
  }
  .su-wiz-dot.done { background: var(--accent); }
  .su-wiz-dot.on { background: var(--brass); }
  .su-wiz-q { margin: 16px 0 6px; font-size: 16.5px; font-weight: 650; color: var(--text-1); line-height: 1.4; }
  .su-wiz-in {
    width: 100%; box-sizing: border-box; margin-top: 5px;
    background: var(--bg-elev-1); border: 1px solid var(--line-strong);
    border-radius: var(--r-md); padding: 11px 13px; font-size: 14.5px; color: var(--text-1);
  }
  .su-wiz-in:focus { outline: none; border-color: var(--brass); }
  .su-wiz-link {
    display: inline-block; margin-top: 11px; background: none; border: none; padding: 0;
    color: var(--brass); font-size: 13px; font-weight: 600; cursor: pointer;
  }
  .su-wiz-link:hover { text-decoration: underline; }
  .su-wiz-hint {
    display: flex; gap: 8px; align-items: flex-start; margin-top: 14px;
    font-size: 12.3px; color: var(--text-3); line-height: 1.5;
  }
  .su-wiz-hint svg { width: 14px; height: 14px; color: var(--accent); flex: none; margin-top: 1px; }
  .su-wiz-check {
    display: flex; gap: 9px; align-items: flex-start; cursor: pointer;
    margin-top: 6px; font-size: 13.5px; color: var(--text-1); line-height: 1.45;
  }
  .su-wiz-check input { width: 16px; height: 16px; margin-top: 1px; flex: none; }
  .su-wiz-map {
    position: relative; margin-top: 12px; height: 240px;
    border: 1px solid var(--line-strong); border-radius: var(--r-md); overflow: hidden;
  }
  .su-wiz-map-canvas { position: absolute; inset: 0; }
  .su-wiz-map-pin {
    position: absolute; left: 50%; top: 50%; transform: translate(-50%, -100%);
    width: 30px; height: 30px; color: var(--brass); pointer-events: none; z-index: 5;
    filter: drop-shadow(0 2px 3px rgba(0,0,0,.35));
  }
  .su-wiz-map-pin svg { width: 30px; height: 30px; }
  .su-wiz-tzline { font-size: 13.5px; color: var(--text-2); }
  .su-wiz-tzline b { color: var(--text-1); font-weight: 600; }

  @media (max-width: 640px) {
    .su-wsteps { grid-template-columns: repeat(2, 1fr); }
    .su-choose { grid-template-columns: 1fr; }
    .su-task { gap: 11px; }
    .su-time { display: none; }
  }

  /* ════════════════════════════════════════════════════════════════════
     TACET DASHBOARD + REPORTS SKIN (td-*)
     The first Tacet-brand surfaces in the admin (spec: tacet-tokens.css /
     tacet-design-system.md). Scoped to #tab-dashboard + #tab-reports so
     the rest of the chrome keeps its current look until each screen is
     touched. Rules of the system worth keeping honest here:
       - Dusk (#6E67AF) is scarce: it means "the system did this". On the
         dashboard it appears ONLY as The Recap's dots; in Reports only on
         the Apply button (the screen's one primary action).
       - Colour is status, never decoration; amber warns, brick errors,
         sage means all-clear.
       - Motion "settles": strong ease-out, no bounce; cards rise 8px once.
     ════════════════════════════════════════════════════════════════════ */
  /* Tacet tokens live at :root (not on the tab sections): the phone report
     sheets clone Reports markup into body-level containers, and the clones
     would lose every colour if the custom properties stayed tab-scoped. */
  :root {
    /* Tacet tokens — dark theme first (admin default). Paper/Ink swap in
       light; sage/amber/brick hold their roles in both. */
    --td-surface:    #18233A;
    --td-raised:     #1E2A44;
    --td-border:     #2A3550;
    /* --td-header = the column-header band on EVERY table. Deliberately deeper
       than --td-raised, which sat too close to the body rows to separate the
       header at a glance.
       --td-unseen = the row surface for a record nobody has opened yet.
       Deliberately LIGHTER than --td-header: a row's unseen state is transient
       content and must never read as heavy as the page's own chrome. In any
       frame the header band must read visibly stronger than any unseen row.
       Pairs with a Sage left edge drawn as an inset box-shadow, never a border.
       Do not "correct" the two toward each other. */
    --td-header:     #364058;
    --td-unseen:     #182329;
    --td-text:       #FBF9F4;
    --td-text2:      #9AA3B2;
    --td-heat-rgb:   251, 249, 244;   /* heatmap tint = the theme's ink, as rgb() parts */
    --td-dusk:       #6E67AF;
    --td-dusk-hi:    #7E77BF;
    --td-dusk-tint:  #262A4A;
    --td-sage:       #A3B49F;
    --td-sage-tint:  #1E2C26;
    --td-sage-ink:   #A3B49F;
    --td-amber:      #C9954A;
    --td-amber-tint: #33290F;
    --td-amber-ink:  #C9954A;
    --td-brick:      #B4533E;
    --td-brick-tint: #351B14;
    --td-brick-ink:  #D07A66;
  }
  :root[data-theme="light"] {
    --td-surface:    #FFFFFF;
    --td-raised:     #F6F3EC;
    --td-border:     #E4E0D5;
    --td-header:     #DAD5C6;
    --td-unseen:     #F4F5F0;
    --td-text:       #111A2B;
    --td-text2:      #58606E;
    --td-heat-rgb:   17, 26, 43;
    --td-dusk-tint:  #EEEDF6;
    --td-sage-tint:  #EDF1EC;
    --td-sage-ink:   #4A6249;
    --td-amber-tint: #F7EEDE;
    --td-amber-ink:  #8A6425;
    --td-brick-tint: #F6E7E3;
    --td-brick-ink:  #B4533E;
  }
  /* Chart accents inside these sections follow Tacet's functional colours
     (amber warns, brick errors). --accent (tea) stays for chart lines —
     it is close kin to sage and keeps contrast in both themes. */
  #tab-dashboard, #tab-reports, #tab-sales, #prod-view-activity {
    --brass: var(--td-amber);
    --danger: var(--td-brick-ink);
    /* Render the whole dashboard in Inter (like the mockup). The Inter remap in
       the HTML <style> is scoped to orders/customers only; without this the
       dashboard inherited the body's IBM Plex, which reads smaller + heavier and
       never matched the mockup. Set on the section root so every child inherits. */
    --f-sans: 'Inter', 'IBM Plex Sans SC', 'PingFang SC', 'Hiragino Sans GB', system-ui, sans-serif;
    font-family: 'Inter', 'IBM Plex Sans SC', 'PingFang SC', 'Hiragino Sans GB', system-ui, sans-serif;
  }

  /* Tacet page ground — warm Paper in light, Ink in dark. Painted on <body>
     (fills the whole viewport, so no seam under short content) only while a
     Tacet tab is active, via :has(); the fixed topbar + sidebar carry their own
     --bg-pane so they're untouched. Other tabs keep the admin's default tan. */
  :root { --td-paper: #111A2B; }
  :root[data-theme="light"] { --td-paper: #FBF9F4; }
  /* Paint the whole page ground in warm Paper on every admin tab — not just the
     Tacet dashboards — so no tab ever shows the darker tan --bg-app (it was
     leaking as a dark strip on the left when the sidebar folded, and made the
     Orders ground read heavier than Reports). Topbar + sidebar keep their own
     --bg-pane, so this only repaints the content gutter. */
  body { background: var(--td-paper); }

  /* ════════════════════════════════════════════════════════════════════
     TACET SKIN — top bar + account menu (chrome reskin, slice one).
     Scoped to .topbar so nothing else repaints; the account dropdown is a
     DOM child of .topbar, so it's covered too. Reuses the shared --td-*
     tokens (no new palette). What this fixes vs. the leftover Full Circle
     look: the Tea-green avatar → Ink circle · the red inbox badge → calm
     Amber (attention, never alarmist) · the pink Sign-out → Brick · IBM
     Plex → Inter · one Dusk element only (the wordmark's automation dot).
     The left rail + everything else keep the old look until their turn.
     ════════════════════════════════════════════════════════════════════ */
  .topbar {
    font-family: 'Inter', 'IBM Plex Sans SC', 'PingFang SC', 'Hiragino Sans GB', system-ui, sans-serif;
    border-bottom-color: var(--td-border);
  }

  /* Search — neutral pill, never Dusk. Ctrl K in a quiet cream chip. */
  .topbar .gsearch-trigger { background: var(--td-raised); border-color: var(--td-border); color: var(--td-text2); }
  .topbar .gsearch-trigger:hover { border-color: var(--td-text2); }
  .topbar .gsearch-label { color: var(--td-text2); font-weight: 500; }
  .topbar .gsearch-kbd { color: var(--td-text2); border-color: var(--td-border); background: var(--td-surface); font-weight: 500; }

  /* Icon buttons → graphite, hover raised. */
  .topbar .topbar-ico { color: var(--td-text2); }
  .topbar .topbar-ico:hover { background: var(--td-raised); color: var(--td-text); }

  /* Unseen inbox marker → Graphite, never Dusk. Dusk means "Tacet acted"; an
     inbox count is customers acting, so it doesn't earn the automation colour
     — same rule this chrome applies everywhere else. Count/shape/position
     unchanged, colour only. */
  /* Unseen inbox = ONE 6px Sage dot, never a count, never red (§1.1). Covers the
     Inbox trigger pip and the Reviews/Feedback rows in the dropdown (all .tab-badge
     inside .topbar). The count text is suppressed; the size is pinned even when the
     badge has content, out-specificity-ing the base :not(:empty) count-pill rule. */
  .topbar .tab-badge,
  .topbar .has-unseen .tab-badge:not(:empty) {
    min-width: 6px; width: 6px; height: 6px; padding: 0;
    border-radius: 50%; background: var(--td-sage);
    box-shadow: 0 0 0 2px var(--bg-pane);
    animation: none; font-size: 0; color: transparent;
  }

  /* Avatar → Ink circle, Paper initial (no Tea green), Graphite focus ring —
     not Dusk. The circle is real data (initials from /me) doing an ordinary
     job (identify the operator), not automation, so it doesn't earn Dusk
     either; same rule as the badge above. Zero Dusk left in global chrome. */
  .topbar .acct-circle { background: var(--td-text); color: var(--td-paper); box-shadow: 0 0 0 1px var(--td-border) inset; }
  .topbar .acct-btn:hover .acct-circle,
  .topbar .acct-btn:focus-visible .acct-circle,
  .topbar .acct-btn[aria-expanded="true"] .acct-circle { box-shadow: 0 0 0 2px var(--td-text2); }

  /* Account dropdown — Tacet card: 14px radius, warm border, one soft shadow. */
  .topbar .acct-menu {
    background: var(--td-surface); border-color: var(--td-border);
    border-radius: var(--r-xl);
    box-shadow: 0 1px 2px rgba(17, 26, 43, .05), 0 4px 16px rgba(17, 26, 43, .06);
  }
  .topbar .acct-name { color: var(--td-text); font-weight: 500; font-size: 15px; }
  .topbar .acct-item { color: var(--td-text); }
  .topbar .acct-item svg { color: var(--td-text2); opacity: 1; }
  .topbar .acct-item:hover { background: var(--td-raised); color: var(--td-text); }
  .topbar .acct-val { color: var(--td-text2); font-family: inherit; font-variant-numeric: tabular-nums; }
  .topbar .acct-danger { color: var(--td-brick-ink); }
  .topbar .acct-danger svg { color: var(--td-brick-ink); }
  .topbar .acct-danger:hover { background: var(--td-brick-tint); color: var(--td-brick-ink); }

  /* Page identity retired from the topbar 2026-07-25 (§1.1d — the bar is CLOSED).
     The crumb (#tb-crumb), the Products/Purchasing sub-view mirrors
     (.tb-subviews / #tb-pu-subviews) and the ⓘ mirror (#tb-info-wrap) are gone,
     with their styles. Nothing in the chrome names the page; each page names
     itself with its own in-content heading. The @media(min-width:721px) rule that
     hid those in-content headings (because the crumb had named them) is removed
     too, in the SAME change — otherwise 16 tabs would ship with no visible title
     at all, and Products/Purchasing would lose their now-restored in-content
     sub-view switch (#stk-views / .pu2-subnav). meituan/guanyi/eleme were never in
     that rule (no rail button ⇒ no crumb ⇒ they already keep their own title). */

  /* ── shared chrome ── */
  /* Reports keeps the centered 1120px reading column: its charts are plotted
     into a fixed viewBox and stretching them past this just thins the ink. */
  #tab-reports .db-head, #tab-reports .db-rangebar, #tab-reports #reports-body {
    max-width: 1120px; margin-left: auto; margin-right: auto;
  }
  /* The Dashboard does NOT. It is a grid of cards, not a reading column, and a
     1120px cap left a third column impossible on any screen and a lake of empty
     paper on a wide one. It runs to whatever `main` allows (1440px, shared with
     every other tab and deliberately untouched here) and resolves its own column
     count from that width.
     container-type makes .td-wrap the query container the card grid measures
     against — which is also why the page title lives INSIDE it: header and cards
     share one box, so their right edges cannot drift apart. Safe to contain: the
     dashboard's two drawers are siblings of .td-wrap, not descendants, so nothing
     position:fixed is trapped by the containment this creates. */
  #tab-dashboard .td-wrap { container-type: inline-size; }
  .td-head { display: flex; align-items: center; gap: 14px; margin-bottom: 24px; }
  /* Tacet page title — a large sentence-case heading (like the reference
     mockups' h1), NOT the admin's tiny mono-uppercase eyebrow that the global
     `h2` rule would otherwise give it. */
  .td-head h2 {
    /* Match the Orders/Invoices page title (24px/500) so every Tacet page's
       heading is the same size. */
    font-family: var(--f-sans); font-size: 24px; font-weight: 500;
    letter-spacing: -.01em; text-transform: none; color: var(--td-text);
  }
  /* This used to read "the title stands alone", on the reasoning that every
     honest candidate for a line under it restates something Needs-you or a card
     already says — a census of what is already on screen. That objection was
     right and it still governs: it is why the line under the greeting carries
     the DATE and, at most, one comparison against the same weekday. Neither is
     on any card. The weekday is what makes the delta arrows legible (they are
     measured against the last four same weekdays, not against yesterday), and
     the comparison is the one fact a single-day dashboard cannot otherwise
     show. Anything that merely counts what a card already states still does not
     belong here — see the rules in tdObservationRender. */
  .td-hd-col { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
  .td-head .td-hd-col h2 { margin: 0; }
  .td-hd-sub { margin: 0; font-size: 13px; color: var(--td-text2); }
  /* The separator belongs to the observation, not to the line, so hiding the
     observation takes its dot with it and the date is never left trailing one. */
  #td-observation[hidden] { display: none; }
  #td-observation::before { content: '·'; margin: 0 .5em; color: var(--td-border); }
  .td-head-sp { flex: 1; }
  .td-kicker {
    font-size: 11px; font-weight: 500; letter-spacing: .1em; text-transform: uppercase;
    color: var(--td-text2); margin: 40px 0 14px;
  }
  #tab-reports .card {
    background: var(--td-surface);
    border: 1px solid var(--td-border);
    border-radius: 14px;
    box-shadow: 0 1px 2px rgba(17, 26, 43, .05), 0 4px 16px rgba(17, 26, 43, .04);
  }
  .td-muted { font-size: 13px; color: var(--td-text2); }
  .td-meta  { font-size: 13px; color: var(--td-text2); }

  /* Cards settle in once on load — 8px rise, strong ease-out, no bounce. */
  @keyframes td-settle { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: none; } }
  @media (prefers-reduced-motion: reduce) {
    .td-dusk-dot { animation: none; }
  }

  .td-recap-rows { display: grid; gap: 12px; }
  .td-recap-row { display: flex; align-items: baseline; gap: 12px; font-size: 15px; color: var(--td-text); }
  .td-recap-row b { font-weight: 500; font-variant-numeric: tabular-nums; }
  .td-dusk-dot {
    width: 8px; height: 8px; border-radius: 50%; flex: none; position: relative; top: -1px;
    background: var(--td-dusk);
    animation: td-breathe 2.4s cubic-bezier(.16,1,.3,1) infinite;
  }
  @keyframes td-breathe {
    0%, 100% { opacity: 1; transform: scale(1); }
    50%      { opacity: .45; transform: scale(.8); }
  }
  /* The Recap's closing row: what the card has to say for itself on the left,
     the "Show the work" fold toggle pinned right. The rule and the spacing that
     used to belong to the closing line now belong to this row, because the
     toggle lives here too and the closing line is hidden on plenty of days —
     hanging the border off a sometimes-absent element left the button floating
     against the rows with no separation. align-items:baseline so a 13px
     sentence and a 13px control sit on one line rather than on their boxes. */
  .td-recap-foot {
    margin-top: 18px; padding-top: 16px; border-top: 1px solid var(--td-border);
    display: flex; align-items: baseline; gap: 12px; flex: none;
  }
  .td-recap-close { margin: 0; min-width: 0; font-size: 13px; color: var(--td-text2); }
  /* Pinned right by the spring, and allowed to keep its full width while the
     sentence beside it ellipsises — a half-visible control is worse than a
     shortened sentence. */
  .td-recap-work-btn {
    margin-left: auto; flex: 0 0 auto; border: 0; background: none; padding: 0;
    font-family: var(--f-sans); font-size: 13px; color: var(--td-text2);
    white-space: nowrap; cursor: pointer;
    transition: color .2s cubic-bezier(.16,1,.3,1);
  }
  .td-recap-work-btn:hover { color: var(--td-text); }

  /* ── "Show the work" fold — the banner unfolds in place into the day's feed.
     Away-window rows carry a dusk tint; the boundary is the "you left here"
     line. Rows reuse .td-feed-row (same drill-through as the drawer). ── */
  .td-recap-work {
    display: grid; gap: 4px; margin-top: 18px; padding-top: 16px;
    border-top: 1px solid var(--td-border);
    max-height: 420px; overflow-y: auto;
    /* A reading column, not the full 1120px card — otherwise the right-aligned
       amount floats to the far edge, miles from its row text. */
    max-width: 692px;
    /* Right gutter so the scroll track + amounts don't jam against the border
       (the tinted rows still bleed 8px each side via their negative margin). */
    padding-right: 12px;
  }
  /* Author `display:grid` above beats the UA `[hidden]{display:none}`, so the
     fold would never collapse without this — "Hide the work" needs it. */
  .td-recap-work[hidden] { display: none; }
  .td-recap-work::-webkit-scrollbar { width: 6px; }
  .td-recap-work::-webkit-scrollbar-thumb { background: var(--td-border); border-radius: 3px; }
  .td-recap-work .td-feed-row.td-work-away { background: var(--td-dusk-tint); }
  .td-left-line {
    display: flex; align-items: center; gap: 12px; margin: 8px 0;
    font-size: 11px; font-weight: 500; letter-spacing: .06em; text-transform: uppercase;
    color: var(--td-dusk);
  }
  .td-left-line::before, .td-left-line::after {
    content: ""; flex: 1; height: 1px; background: var(--td-dusk); opacity: .35;
  }

  /* ── Needs you — one line per fire ─────────────────────────────────────────
     icon · what it is · how many · how long the oldest has waited · the door.

     NOTHING RESERVES A COLUMN FOR THE AGE. The pill and the meta run are welded
     to the label (flex: 0 0 auto, left to right) and a single spring pushes only
     the link to the right edge. That is what lets the one fire with no age —
     Restock, which is a state, not a queue — read as a shorter sentence rather
     than a row with a hole in it: "Restock needed · 15 · out of stock, 8 running
     low" simply ends before "Delivery needs a call · 2 · oldest 4h" does. A
     right-aligned age column is the obvious first shape and is exactly what
     produces the hole; there is no empty cell here because there is no cell.

     One pill per row, in the row's severity colour, and the icon repeats that
     colour so severity survives a glance without a second chip. */
  .td-nrow {
    display: flex; align-items: center; gap: 12px; width: 100%; text-align: left;
    padding: 14px 4px; border: 0; border-bottom: 1px solid var(--td-border);
    background: transparent; cursor: pointer; color: var(--td-text);
    font-family: var(--f-sans); border-radius: 8px;
  }
  /* The last row keeps the SAME padding as every other row, and gives its
     trailing space back with a negative margin instead.

     It used to drop to padding-bottom:2px, which was fine while a row was just
     text on a line: it stopped the list adding a band of space inside the card.
     But the row is a hoverable rounded block now, and 14px of padding above the
     text against 2px below means that block is not centred on its own text —
     roomy at the top, shaved off at the bottom. On a card showing a single fire,
     where the only row is also the last one, that is the whole highlight
     sitting crooked.

     The -12px cancels the 12px the restored padding adds, so the card is exactly
     as tall as before; only the hover block changed shape. It lives on the row
     rather than on #td-attn so it cannot pull up the "nothing waiting" state,
     which has no row at all. */
  .td-nrow:last-child { border-bottom: 0; margin-bottom: -12px; }
  .td-nrow:hover { background: rgba(var(--td-heat-rgb), .03); }
  .td-nrow:focus-visible { outline: 2px solid var(--td-dusk); outline-offset: -2px; }
  .td-nico { display: flex; flex: 0 0 auto; }
  .td-nico svg { display: block; }
  .td-nico.is-amber { color: var(--td-amber-ink); }
  .td-nico.is-brick { color: var(--td-brick-ink); }
  .td-nlbl { font-size: 15px; font-weight: 500; white-space: nowrap; flex: 0 0 auto; }
  .td-npill {
    flex: 0 0 auto; font-size: 12px; font-weight: 500; line-height: 1.7;
    padding: 0 9px; border-radius: 999px; font-variant-numeric: tabular-nums;
    white-space: nowrap;
  }
  .td-npill.is-amber { background: var(--td-amber-tint); color: var(--td-amber-ink); }
  .td-npill.is-brick { background: var(--td-brick-tint); color: var(--td-brick-ink); }
  /* The state phrase truncates at a narrow width rather than wrapping: a row
     that grows to two lines makes a five-row card jump every time an age ticks
     over, and a jumping card is worse than one that asks for a hover. */
  .td-nmeta {
    font-size: 13px; color: var(--td-text2); font-variant-numeric: tabular-nums;
    flex: 0 1 auto; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  }
  .td-nspring { flex: 1; }
  .td-ncta { font-size: 14px; color: var(--td-text2); white-space: nowrap; flex: 0 0 auto; }
  .td-nchev { flex: 0 0 auto; width: 15px; height: 15px; color: var(--td-text2); opacity: .55; }
  .td-nrow:hover .td-ncta { color: var(--td-text); }
  .td-nrow:hover .td-nchev { opacity: 1; }

  .td-pill {
    flex: none; font-size: 13px; font-weight: 500; padding: 2px 12px;
    border-radius: 999px; font-variant-numeric: tabular-nums;
  }
  /* Per-store filter chip in the Running-low header (Option A). */
  .td-store-sel {
    flex: none; appearance: none; -webkit-appearance: none;
    font: 500 12.5px var(--f-sans); color: var(--td-text2);
    background: var(--td-surface); border: 1px solid var(--td-border);
    border-radius: 8px; padding: 3px 24px 3px 10px; cursor: pointer; max-width: 160px;
    background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="%2358606E" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round"><polyline points="6 9 12 15 18 9"/></svg>');
    background-repeat: no-repeat; background-position: right 8px center;
    transition: border-color .2s cubic-bezier(.16,1,.3,1);
  }
  .td-store-sel:hover { border-color: var(--td-text2); }
  .td-store-sel:focus { outline: none; border-color: var(--td-dusk); }
  .td-pill-amber { background: var(--td-amber-tint); color: var(--td-amber-ink); }
  .td-attn-clear { font-size: 14px; color: var(--td-text2); padding: 2px 0; }
  /* Running-low rolling window: the rows box cross-fades as it pages through the
     out/low pool (see tdLowRotTick). 220ms matches the JS fade timing. */
  .td-low-rows { display: grid; gap: 4px; transition: opacity .22s ease; }
  @media (prefers-reduced-motion: reduce) { .td-low-rows { transition: none; } }
  .td-attn-row {
    display: flex; align-items: baseline; gap: 10px; width: 100%; text-align: left;
    border: 0; background: transparent; cursor: pointer; padding: 4px 8px; margin: 0 -8px;
    border-radius: 6px; font-size: 14px; color: var(--td-text); font-family: var(--f-sans);
  }
  .td-attn-row:hover { background: var(--td-raised); }
  /* One line, not a stacked grid: this row sits in a fixed-height bento tile
     (.bn-card overflow:hidden) sized for single-line rows — a two-line stack
     pushes the last row past the clip when an item happens to have a SKU. */
  .td-attn-row .name { min-width: 0; display: flex; align-items: baseline; gap: 6px; }
  .td-attn-row .name .nm { min-width: 0; flex: 0 1 auto; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .td-attn-row .name .sku { flex: 0 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
    font-size: 11.5px; color: var(--td-text2); font-family: var(--f-mono, ui-monospace, monospace); }
  .td-attn-row .val { margin-left: auto; flex: none; font-size: 13px; color: var(--td-text2); font-variant-numeric: tabular-nums; }
  .td-attn-more { margin-top: 10px; font-size: 13px; color: var(--td-text2); }


  /* ════════════════════════════════════════════════════════════════
     THE CARD GRID — the dashboard as the operator's own space.

     A CSS grid inside a container query on .td-wrap. Columns: 1 below 760px of
     available width, 2 to 1140px, 3 above — three is the ceiling, because a
     fourth column narrows every card past the point where its numbers read.
     A container query, not a media query, so the count follows the space the
     cards actually have: folding the rail widens the grid by 174px and can earn
     a column without the window moving at all.

     What this replaced: a packing solver that chose each card's SHAPE from a
     menu of six-column tiles, positioned it absolutely at a computed pixel rect,
     and then folded the card's CONTENT away with [data-tier] until it fit the
     box it had been handed. Two rules stand in its place, and the height always
     traces back to real content in that row rather than to a shape:

       A — cards in a row stretch to the tallest card's own content (grid
           stretches its items by default), and each card's footer pins to the
           bottom edge with margin-top:auto, so the slack collects above the
           footer and the row ends flat.
       B — a card that would sit alone in the last row spans the row. That one
           needs script — CSS cannot count tiles against the resolved column
           count — so dashRender adds .td-span. Nothing else about the layout
           is computed.

     The .td-block wrapper + data-block stay as the grid/order contract; the
     visible surface is bn-* markup, painted by the same loaders as before. */
  .td-blocks {
    display: grid; grid-template-columns: 1fr; gap: 16px; align-items: stretch;
  }
  /* Never draw more columns than there are cards to put in them. Two cards in a
     three-column grid left the pair hugging the left edge with a column of
     nothing beside them — the grid describing a card that is not there. The cap
     is applied per band, not globally, so it can only ever REMOVE a column the
     width would have allowed: two cards in the narrow band stay one-up.
     dashRender sets the class, because CSS cannot count how many cards are
     actually showing once quiet and role-hidden ones drop out. */
  @container (min-width: 760px)  {
    .td-blocks { grid-template-columns: 1fr 1fr; }
    .td-blocks.is-cols-1 { grid-template-columns: 1fr; }
  }
  @container (min-width: 1140px) {
    .td-blocks { grid-template-columns: 1fr 1fr 1fr; }
    .td-blocks.is-cols-1 { grid-template-columns: 1fr; }
    .td-blocks.is-cols-2 { grid-template-columns: 1fr 1fr; }
  }
  .td-block { min-width: 0; animation: td-settle .6s cubic-bezier(.16,1,.3,1) both; }
  /* Rule B. `1 / -1` spans whatever the column count currently is, so the span
     survives a resize across a band edge even before dashRender re-runs. */
  .td-block.td-span { grid-column: 1 / -1; }
  @media (prefers-reduced-motion: reduce) { .td-block { animation: none; } }
  .td-block.is-hidden { display: none; }

  /* Needs you — full width, first, and not a grid item at all: it sits outside
     #td-blocks, which is what makes it unmovable rather than a rule saying so. */
  .td-needs { margin-bottom: 16px; }

  /* the card: surface on paper, 1px, 14r, one soft shadow. It fills its grid
     cell, which is as tall as the tallest card in the row — so `height: 100%`
     here is what turns Rule A from an intention into a flat row edge.
     NOT overflow:hidden any more: nothing is clipped to a fixed shape now, and
     hiding overflow on a content-sized card can only ever hide real content. */
  .bn-card {
    position: relative; width: 100%; height: 100%;
    background: var(--td-surface); border: 1px solid var(--td-border);
    border-radius: 14px; box-shadow: 0 1px 2px rgba(17,26,43,.05), 0 4px 16px rgba(17,26,43,.04);
    display: flex; flex-direction: column;
  }
  .bn-inner { padding: 22px 24px; display: flex; flex-direction: column; flex: 1; min-height: 0; }
  /* padding-right reserves the top-right gutter the hover × hide button lives in
     (position:absolute at right:10px) so a header never runs under it. */
  /* The head has to survive the narrowest card in the 3-column band (~390px)
     holding a title, a timestamp AND a control. Everything textual may shrink
     and ellipsise; only the control is rigid, because a half-visible button is
     worse than a shortened title. Without the min-width:0 the nowrap title
     refuses to shrink and shoves the control clean off the card — and with the
     card no longer clipping its overflow, "off the card" now means visibly
     spilling over the neighbour. */
  .bn-head { display: flex; align-items: baseline; gap: 10px; margin-bottom: 16px; flex: none; padding-right: 26px; }
  .bn-head .bn-ico { width: 17px; height: 17px; color: var(--td-text2); flex: none; position: relative; top: 3px; }
  .bn-head h3 {
    margin: 0; font: 600 17px var(--f-sans); color: var(--td-text);
    white-space: nowrap; flex: 0 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis;
  }
  .bn-head .bn-sub, .bn-meta {
    font-size: 13px; font-weight: 400; color: var(--td-text2);
    white-space: nowrap; flex: 0 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis;
  }
  .bn-go { margin-left: auto; flex: 0 0 auto; font-family: var(--f-sans); font-size: 13px; color: var(--td-text2);
    background: none; border: 0; cursor: pointer; white-space: nowrap; padding: 0;
    transition: color .2s cubic-bezier(.16,1,.3,1); }
  .bn-go:hover { color: var(--td-text); }
  .bn-body { flex: 1; min-height: 0; display: flex; flex-direction: column; }

  /* ── metrics ──
     Label above figure, two across, every card. There used to be an .is-three
     variant running three across, because a three-metric card in a two-column
     grid orphans the third and leaves a ragged bottom. Customers was its only
     user and now carries four, so the variant is retired rather than left
     behind as a rule nothing selects — if a three-metric card ever comes back,
     the orphan problem is real and this is the fix for it.
     When a card spans its row the metrics run one row across, so the width the
     span won is used rather than left sitting beside them. */
  .bn-mgrid { display: grid; grid-template-columns: 1fr 1fr; gap: 22px 20px; margin-bottom: 20px; }
  .td-span .bn-mgrid { grid-template-columns: none; grid-auto-flow: column; grid-auto-columns: 1fr; gap: 22px 32px; }
  /* A spanning card's one-row-across cannot hold on a restricted role's phone,
     which keeps this desktop surface instead of #rm-dash. Two up there. */
  @media (max-width: 720px) {
    .bn-mgrid,
    .td-span .bn-mgrid { grid-template-columns: 1fr 1fr; grid-auto-flow: row; }
  }
  .bn-ml { font-size: 13px; color: var(--td-text2); }
  .bn-mv {
    font-size: 30px; font-weight: 600; letter-spacing: -.01em; line-height: 1.15;
    margin-top: 4px; color: var(--td-text); font-variant-numeric: tabular-nums;
  }
  .bn-mc { font-size: 13px; color: var(--td-text2); margin-top: 5px; font-variant-numeric: tabular-nums; }
  .bn-mc:empty { display: none; }

  /* generated-content wrappers */
  .bn-recap-rows { flex: 1; min-height: 0; }

  /* "Show the work" unfolds the full feed in place. The one-line digest above it
     would then say the same thing twice, so it folds away and the feed owns the
     card.

     The fold fills the card and scrolls INSIDE it — the card itself does not
     change size. That only works because tdRecapToggleWork pins the block's
     height before revealing the fold: a content-height card has nothing bounding
     it, so an open fold would otherwise grow to the full length of the day's
     feed and the flat-row rule would stretch every other card in the row to
     match. With the pin in place the chain is card → inner → body → fold, every
     link min-height:0 so the flex algorithm can actually shrink it, and the
     overflow-y:auto from the base rule does the rest. Drop the pin and this
     silently goes back to growing the page. */
  .bn-body:has(> .td-recap-work:not([hidden])) .bn-recap-rows { display: none; }
  .bn-body:has(> .td-recap-work:not([hidden])) .td-recap-work {
    flex: 1 1 auto; min-height: 0; max-height: none;
    margin-top: 0; padding-top: 0; border-top: 0;
  }
  /* The closing row keeps its place under the feed rather than being squeezed
     out of it by the flexing fold. It is the ROW that must hold now, not the
     closing line inside it — the line is hidden on plenty of days and the fold
     toggle beside it is not. */
  .bn-body:has(> .td-recap-work:not([hidden])) .td-recap-foot { flex: none; }
  /* Each row is a grid item, so its automatic minimum size is its min-content —
     and a nowrap line's min-content is the whole sentence, which is what put a
     horizontal scrollbar under the feed once the card narrowed from the old
     full-width tile to a third of the page. Let the rows shrink; .td-feed-text
     already ellipsises inside them. */
  .td-recap-work .td-feed-row { min-width: 0; }

  /* RULE A, the half CSS owns: every card's footer pins to the bottom edge, so
     when the grid stretches a short card to match the tallest one in its row,
     the slack collects ABOVE the footer and the row still ends on one flat line.
     margin-top:auto is the whole mechanism — remove it and short cards float
     their footers mid-card and the row edge goes ragged. */
  .td-block .td-sec-foot { margin: auto 24px 0; padding: 14px 0 22px; }
  .td-block .td-chan-foot { margin-top: auto; padding-top: 8px; padding-bottom: 2px; }
  .td-block .td-inv-low { margin-top: 8px; }

  /* Arrange is stood down this release (its drag read the retired solver's pixel
     rects); the control and each card's hide ✕ stay in the markup and are hidden
     here, so the rebuild is a behaviour change and not a markup change. */
  .td-arrange-btn, .td-arrange-hint, .td-tray, .bn-hide { display: none; }

  /* ── Today — the pulse ── */
  .td-live { display: inline-flex; align-items: baseline; gap: 8px; margin-left: auto; font-size: 13px; color: var(--td-text2); }
  .td-live-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--td-sage); align-self: center; opacity: .45; }
  .td-live.is-on .td-live-dot { opacity: 1; }
  .td-live-num { font-weight: 500; color: var(--td-text); font-variant-numeric: tabular-nums; }
  .td-live.is-zero .td-live-num { display: none; }
  .db-hero-sync[hidden] { display: none; }
  /* Sits between the card's padded inner and its cream footer, so it carries
     its own side padding or it runs flush to the card edge. Left-aligned: the
     only thing this element ever shows now is a sentence (see dashRenderSync),
     and a sentence ranged right reads as a caption rather than a warning. */
  .td-block .db-hero-sync { padding: 0 24px 12px; }
  /* The stale state — the ONLY state that renders. Amber, because this is the
     same "something is wrong and it is on you" register as a Needs-you row, and
     a shade heavier than the muted grey it inherits so it does not read as the
     card's small print. */
  .db-hero-sync.is-stale { color: var(--td-amber-ink); font-weight: 500; }

  /* The card's footer: its one attention line, and the way in. Amber dot = something waits, sage = all clear.
     Empty (no data yet) collapses so the card doesn't show a bare strip. */
  /* One footer treatment for every card: a hairline inset to the card's own
     text column, no fill. This used to be a full-bleed --td-raised band, which
     made Today and Customers read as a different kind of card from the Recap,
     Unhandled, On the road and Ready — all of which already ended on a plain
     inset rule (.td-recap-close, .td-chan-foot). Four cards against two, and the
     odd two sat either side of the Recap in the owner's row, so the mismatch was
     the first thing the eye caught along that bottom edge. The signed-off
     footer has no fill either. Horizontal inset matches .bn-inner's 24px and the
     bottom padding matches its 22px, so the rule lines up with the content above
     it rather than cutting the full width of the card. */
  .td-sec-foot {
    display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
    border-top: 1px solid var(--td-border);
    font-size: 13.5px; color: var(--td-text2);
  }
  .td-sec-foot:empty { display: none; }
  .td-sec-dot { width: 6px; height: 6px; border-radius: 50%; background: var(--td-amber); flex: none; }
  .td-sec-dot.is-clear { background: var(--td-sage); }
  .td-sec-foot .td-sec-link {
    border: 0; background: none; cursor: pointer; padding: 0;
    font-family: inherit; font-size: 13.5px; font-weight: 500; color: var(--td-text);
  }
  .td-sec-foot .td-sec-link:hover { text-decoration: underline; text-underline-offset: 2px; }
  .td-sec-foot .td-sec-sep { color: var(--td-border); }
  /* The live-browsing indicator sits in the Customers footer — it already
     pushes itself right with margin-left:auto, which we don't want here. */
  .td-sec-foot .td-live { margin-left: 0; }
  /* The card's way in, pinned right. It used to sit in the card header; the
     footer is where the eye already is once the numbers are read, and putting it
     here lets every card's footer read the same shape: a dot, what is happening,
     and the door. margin-left:auto is what pins it — with flex-wrap on, it also
     drops onto its own line rather than crushing the state text on a narrow
     card. */
  .td-sec-foot .td-sec-go {
    margin-left: auto; border: 0; background: none; cursor: pointer; padding: 0;
    font-family: inherit; font-size: 13.5px; color: var(--td-text2);
    transition: color .2s cubic-bezier(.16,1,.3,1);
  }
  .td-sec-foot .td-sec-go:hover { color: var(--td-text); text-decoration: underline; text-underline-offset: 2px; }
  /* Icon-only door — a chevron, no words. Only for cards whose link lands where
     the card title already says (Today, Customers); a card pointing SOMEWHERE
     ELSE keeps its label, because there the words are the only thing naming the
     destination. The negative margins buy a 28px touch target without moving
     the glyph off the footer's padding edge, and margin-left stays auto so it
     is still what pins the button right. Nothing visible is underlined here, so
     the inherited hover rule is dropped — colour alone carries the hover. */
  .td-sec-foot .td-sec-go.is-icon {
    display: inline-flex; align-items: center;
    padding: 6px; margin: -6px -6px -6px auto;
  }
  .td-sec-foot .td-sec-go.is-icon:hover { text-decoration: none; }
  .td-sec-go .td-sec-chev { width: 16px; height: 16px; flex: none; }

  @media (max-width: 720px) {
    /* Same inset rule, tightened to the phone card's own padding. */
    .td-block .td-sec-foot { margin: auto 20px 0; padding: 12px 0 18px; }
  }

  /* ── The Running-low list, inside the Inventory card ──
     Rows of real product names under the two numbers that describe them. It
     sits between the vitals and the cream footer, so the footer still bleeds
     to the card's rounded edge. Hidden entirely when everything is stocked. */
  .td-inv-low { border-top: 1px solid var(--td-border); }
  .td-inv-low[hidden] { display: none; }
  .td-inv-low-head {
    display: flex; align-items: center; gap: 10px;
    padding: 14px 32px 6px; font-size: 12.5px; color: var(--td-text2);
  }
  .td-inv-low-head .td-store-sel { margin-left: auto; }
  .td-inv-low .td-low-rows { transition: opacity .22s cubic-bezier(.16,1,.3,1); }
  .td-inv-low .td-attn-row { padding-left: 32px; padding-right: 32px; }
  .td-inv-low .td-attn-more { padding: 4px 32px 14px; }
  .td-inv-low .td-attn-clear { padding: 10px 32px 16px; font-size: 13.5px; color: var(--td-text2); }
  @media (max-width: 720px) {
    .td-inv-low-head, .td-inv-low .td-attn-more,
    .td-inv-low .td-attn-row { padding-left: 22px; padding-right: 22px; }
  }

  /* A block with nothing to say leaves the page — a shorter dashboard is the
     good news, and there is no empty state to read past. It stays in the DOM so
     the next fire brings it straight back without rebuilding anything, and
     dashRender skips it when working out which card is alone in its row. */
  .td-block.is-quiet { display: none; }

  /* The Recap's quiet state: one line of text where a card would otherwise
     stand and say nothing. It sits above the grid, not in it, so a calm morning
     carries no cream at all and reads as quiet rather than as empty furniture. */
  .td-recap-line { font-size: 15px; color: var(--td-text2); margin: 0 0 16px; }
  .td-recap-line[hidden] { display: none; }
  /* The way into the day's log from the quiet state. Reads as a quiet link, not
     a button — it sits inside a sentence. The gap is margin, never a " · ", so
     the i18n walker can't mistake the line for an EN·CN pair and split it. */
  .td-recap-line-btn {
    margin-left: 10px; padding: 0; border: 0; background: none; cursor: pointer;
    font-family: var(--f-sans); font-size: 15px; color: var(--td-text2);
    text-decoration: underline;
    text-decoration-color: color-mix(in srgb, var(--td-text2) 45%, transparent);
    text-underline-offset: 3px;
    transition: color .2s cubic-bezier(.16,1,.3,1);
  }
  .td-recap-line-btn:hover { color: var(--td-text); text-decoration-color: currentColor; }
  .td-recap-line-btn:focus-visible { outline: 2px solid var(--td-dusk); outline-offset: 3px; border-radius: 4px; }

  /* Activity strip — clean prose rows: time · sentence · right-aligned amount,
     no icon, first-order pill only. Toggle reveals the full day (scrolls inside
     the card past ~360px so the page below stays reachable). */
  .td-feed .db-feed-list { display: flex; flex-direction: column; gap: 4px; max-height: 360px; }
  .td-feed .db-feed-empty { font-size: 13px; color: var(--td-text2); padding: 6px 0; }

  .td-chan-foot {
    margin: 14px 0 0; padding-top: 12px; border-top: 1px solid var(--td-border);
    font-size: 12.5px; color: var(--td-text2);
  }
  /* On the way — in-transit PO list (bx-* scoped). ETA in the "arrives today"
     amber when due on/before today. Rows past the 2nd wear .detail/.detail2. */
  .bx-in-list { display: grid; gap: 2px; margin-top: 4px; align-content: start; }
  /* Section caption for the merged "On the way / Drafts" card — a small quiet
     label above each of its two lists; the second gets a hairline separator. */
  .bx-in-cap { font: 600 11px var(--f-sans); color: var(--td-text2); margin: 0 0 2px; }
  .bx-in-cap.bx-in-cap-2 { margin-top: 12px; padding-top: 10px; border-top: 1px solid var(--td-raised); }
  .bx-in-row { display: flex; align-items: baseline; gap: 12px; padding: 7px 0; font-size: 13.5px; }
  .bx-in-row + .bx-in-row { border-top: 1px solid var(--td-raised); }
  .bx-in-nm { min-width: 0; color: var(--td-text); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-variant-numeric: tabular-nums; }
  .bx-in-sub { color: var(--td-text2); font-size: 12px; margin-left: 8px; }
  .bx-in-eta { margin-left: auto; flex: none; color: var(--td-text2); font-size: 12.5px; white-space: nowrap; }
  .bx-in-eta.is-soon { color: var(--td-amber-ink); font-weight: 500; }
  .td-feed-row { display: flex; align-items: baseline; gap: 12px; padding: 5px 8px; margin: 0 -8px; border-radius: 6px; border: 0; font-size: 14px; line-height: 1.4; cursor: pointer; }
  .td-feed-row:hover { background: var(--td-raised); }
  .td-feed-row .db-feed-ts { flex: none; width: 40px; color: var(--td-text2); font-size: 12.5px; font-variant-numeric: tabular-nums; }
  .td-feed-text { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--td-text); }
  .td-feed-amt { margin-left: auto; flex: none; color: var(--td-text2); font-size: 13px; font-variant-numeric: tabular-nums; }
  .td-feed-badge { flex: none; font-size: 11.5px; font-weight: 500; padding: 1px 9px; border-radius: 999px; background: var(--td-sage-tint); color: var(--td-sage-ink); }
  .td-feed-badge.td-feed-badge-mute { background: var(--td-raised); color: var(--td-text2); }
  /* The amount always right-aligns to a single column (margin-left:auto) so the
     money reads as a clean vertical line down the feed, badge or not. The badge
     stays inline after the text. */

  /* "Show the work" drawer — the Recap's receipts. Chrome comes from the Tacet
     inline scope (#db-activity-drawer); these style the day headers + rows +
     empty state so it reads like the Activity card. */
  #db-activity-drawer .drawer-body { display: flex; flex-direction: column; gap: 2px; }
  #db-activity-drawer .td-feed-row { font-size: 14px; }
  .td-drawer-day { font-size: 12px; font-weight: 500; letter-spacing: .04em; color: var(--td-text2); margin: 18px 0 6px; }
  .td-drawer-day:first-child { margin-top: 2px; }

  /* ── Running-low stock drawer — the full list behind "and N more" ──
     Chrome comes from .side-drawer + the Tacet inline scope (#db-stock-drawer);
     these style the trigger link, group headers (out = brick, low = amber) and
     the rows with their optional per-shelf count line. */
  .td-attn-more-link {
    display: inline-flex; align-items: center; gap: 4px; margin: 0; padding: 0;
    border: 0; background: transparent; cursor: pointer;
    font-family: var(--f-sans); font-size: 13px; color: var(--td-text);
    text-decoration: underline; text-decoration-color: var(--td-border);
    text-underline-offset: 3px; transition: text-decoration-color .2s cubic-bezier(.16,1,.3,1);
  }
  .td-attn-more-link:hover { text-decoration-color: var(--td-text2); }
  .td-attn-more-link svg { color: var(--td-text2); flex: none; }
  #db-stock-drawer .drawer-body { display: flex; flex-direction: column; gap: 2px; }
  .td-stock-scope { margin: 2px 0 10px; }
  .td-stock-grp {
    display: flex; align-items: baseline; gap: 8px; margin: 16px 0 4px;
    font-size: 11px; font-weight: 600; letter-spacing: .1em; text-transform: uppercase;
  }
  .td-stock-grp:first-child, .td-stock-scope + .td-stock-grp { margin-top: 2px; }
  .td-stock-grp.is-out { color: var(--td-brick-ink); }
  .td-stock-grp.is-low { color: var(--td-amber-ink); }
  .td-stock-grp .n { font-weight: 500; letter-spacing: 0; }
  .td-stock-grp::after { content: ""; flex: 1; align-self: center; border-top: 1px solid var(--td-border); opacity: .6; }
  .td-stock-row {
    display: flex; align-items: baseline; gap: 10px; width: 100%; text-align: left;
    border: 0; background: transparent; cursor: pointer; padding: 6px 8px;
    border-radius: 6px; font-size: 14px; color: var(--td-text); font-family: var(--f-sans);
  }
  .td-stock-row:hover { background: var(--td-raised); }
  .td-stock-row .name { min-width: 0; display: grid; gap: 1px; }
  .td-stock-row .name .nm,
  .td-stock-row .name .sku,
  .td-stock-row .name .sub { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .td-stock-row .name .sub { font-size: 12px; color: var(--td-text2); }
  .td-stock-row .name .sku { font-size: 11.5px; color: var(--td-text2); font-family: var(--f-mono, ui-monospace, monospace); }
  .td-stock-row .val { margin-left: auto; flex: none; font-size: 13px; color: var(--td-text2); font-variant-numeric: tabular-nums; white-space: nowrap; }
  .td-stock-row .val.out { color: var(--td-brick-ink); }

  .td-foot { margin-top: 40px; text-align: center; }
  .td-foot-link {
    border: 0; background: transparent; cursor: pointer; font-family: var(--f-sans);
    font-size: 13.5px; color: var(--td-text2); padding: 4px 8px;
    border-bottom: 1px solid var(--td-border); transition: color .2s cubic-bezier(.16,1,.3,1);
  }
  .td-foot-link:hover { color: var(--td-text); }

  /* ── Reports chrome ── */
  #tab-reports .td-kicker:first-of-type { margin-top: 40px; }
  #tab-reports .db-rangebar { margin: 14px 0 32px; }
  /* Controls rail — quiet paper segments and inputs; the active range segment
     inverts to ink-on-paper. Dusk appears exactly once: the Apply button. */
  #tab-reports .db-range .seg {
    background: var(--td-surface); border: 1px solid var(--td-border);
    border-radius: 8px;
  }
  #tab-reports .db-range .seg button {
    color: var(--td-text2); font-size: 13.5px; padding: 7px 16px;
    border-right: 1px solid var(--td-border);
    transition: color .2s cubic-bezier(.16,1,.3,1);
  }
  #tab-reports .db-range .seg button:last-child { border-right: 0; }
  #tab-reports .db-range .seg button:hover { color: var(--td-text); }
  #tab-reports .db-range .seg button.active { background: var(--td-text); color: var(--td-paper); font-weight: 500; }
  #tab-reports .db-range input[type=date],
  #tab-reports .db-select,
  #tab-reports .db-btn {
    background: var(--td-surface); border: 1px solid var(--td-border); color: var(--td-text);
    border-radius: 8px; padding: 7px 12px; font-size: 13.5px; font-family: inherit;
  }
  #tab-reports .db-select:hover, #tab-reports .db-btn:hover { border-color: var(--td-text2); }

  /* ── Reports chapters + the AI voice ──
     Dusk on this screen belongs to the AI ("Tacet did this for you"): the
     breathing dot on The read / chapter notes / Ask. Apply is a quiet
     secondary. Spec: reference/reports-ai-mockup.html. */
  #tab-reports .db-chtabs {
    display: flex; gap: 26px; border-bottom: 1px solid var(--td-border);
    margin: 4px 0 24px; flex-wrap: wrap;
  }
  #tab-reports .db-chtabs button {
    background: transparent; border: 0; cursor: pointer; font-family: inherit;
    font-size: 14px; color: var(--td-text2); padding: 0 2px 10px;
    border-bottom: 2px solid transparent; margin-bottom: -1px;
    transition: color .2s cubic-bezier(.16,1,.3,1);
  }
  #tab-reports .db-chtabs button:hover { color: var(--td-text); }
  #tab-reports .db-chtabs button.active { color: var(--td-text); border-color: var(--td-text); font-weight: 500; }
  #tab-reports .db-chapter { display: none; }
  #tab-reports .db-chapter.active { display: block; }
  #tab-reports .db-grid-2 .db-span2 { grid-column: span 2; }
  @media (max-width: 980px) { #tab-reports .db-grid-2 .db-span2 { grid-column: auto; } }

  /* Breathing Dusk dot = "the AI wrote this". Anything without the dot is
     computed arithmetic, never a guess. */
  @keyframes tdAiBreathe { 0%, 100% { opacity: 1; } 50% { opacity: .45; } }
  .td-ai-dot {
    display: inline-block; flex: none; width: 7px; height: 7px; border-radius: 50%;
    background: var(--td-dusk); animation: tdAiBreathe 2.4s cubic-bezier(.16,1,.3,1) infinite;
  }
  @media (prefers-reduced-motion: reduce) { .td-ai-dot { animation: none; } }

  /* The read — the analysis card under the KPIs. */
  #tab-reports .db-read {
    background: var(--td-surface); border: 1px solid var(--td-border);
    border-left: 3px solid var(--td-dusk); border-radius: 14px;
    padding: 20px 24px; margin-bottom: 16px;
  }
  #tab-reports .db-read .rh { display: flex; align-items: center; gap: 9px; margin-bottom: 8px; }
  #tab-reports .db-read .rh h3 { margin: 0; font-size: 15px; font-weight: 500; color: var(--td-text); }
  #tab-reports .db-read .rh .meta { margin-left: auto; font-size: 12.5px; color: var(--td-text2); }
  #tab-reports .db-read p { margin: 0; font-size: 14.5px; line-height: 1.75; color: var(--td-text); max-width: 72ch; }
  #tab-reports .db-read p + p { margin-top: 8px; }
  #tab-reports .db-read .rfoot { margin-top: 12px; font-size: 12px; color: var(--td-text2); }
  #tab-reports .db-read .rload { display: flex; align-items: center; gap: 9px; font-size: 13.5px; color: var(--td-text2); }

  /* The read, drawn — pulse verdict + insight cards inside the read card.
     Numbers are computed from DASH_DATA; the AI only picked captions/levels.
     Spec: reference/reports-read-combo-mockup.html (combo 2). */
  #tab-reports .db-read .rv-verdict { display: flex; align-items: center; gap: 14px; flex-wrap: wrap; padding: 4px 0 2px; color: var(--td-text); }
  #tab-reports .db-read .rv-verdict svg { flex: none; color: var(--td-text); }
  #tab-reports .db-read .rv-verdict .v { font-size: 16.5px; font-weight: 500; letter-spacing: -0.01em; }
  #tab-reports .db-read .rv-verdict .chips { margin-left: auto; display: flex; gap: 8px; flex-wrap: wrap; }
  #tab-reports .db-read .rv-chip { display: inline-flex; align-items: center; gap: 7px; padding: 2px 12px; border-radius: 999px; font-size: 12.5px; font-weight: 500; }
  #tab-reports .db-read .rv-chip i { width: 8px; height: 8px; border-radius: 50%; }
  #tab-reports .db-read .rv-chip.ok { background: var(--td-sage-tint); color: var(--td-sage-ink); }
  #tab-reports .db-read .rv-chip.ok i { background: var(--td-sage); }
  #tab-reports .db-read .rv-chip.warn { background: var(--td-amber-tint); color: var(--td-amber-ink); }
  #tab-reports .db-read .rv-chip.warn i { background: var(--td-amber); }
  #tab-reports .db-read .rv-cards { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; margin-top: 14px; }
  #tab-reports .db-read .rv-card { border: 1px solid var(--td-border); border-radius: 10px; padding: 16px 18px; min-width: 0; }
  #tab-reports .db-read .rv-card.rv-span2 { grid-column: span 2; }
  @media (max-width: 980px) {
    #tab-reports .db-read .rv-cards { grid-template-columns: 1fr; }
    #tab-reports .db-read .rv-card.rv-span2 { grid-column: auto; }
  }
  #tab-reports .db-read .rv-cap { display: flex; align-items: center; gap: 9px; font-size: 14.5px; font-weight: 500; color: var(--td-text); }
  #tab-reports .db-read .rv-cap .t { flex: 1; min-width: 0; }
  #tab-reports .db-read .rv-pill { flex: none; padding: 1px 10px; border-radius: 999px; font-size: 12px; font-weight: 500; }
  #tab-reports .db-read .rv-pill.warn { background: var(--td-amber-tint); color: var(--td-amber-ink); }
  #tab-reports .db-read .rv-pill.ok { background: var(--td-sage-tint); color: var(--td-sage-ink); }
  #tab-reports .db-read .rv-sub { font-size: 12.5px; color: var(--td-text2); margin: 2px 0 10px; }
  #tab-reports .db-read .rv-frow { display: grid; grid-template-columns: 130px 1fr 130px; align-items: center; gap: 12px; margin-top: 10px; }
  #tab-reports .db-read .rv-frow .l { font-size: 13px; color: var(--td-text2); }
  #tab-reports .db-read .rv-frow .v { font-size: 13px; font-weight: 500; color: var(--td-text); text-align: right; font-variant-numeric: tabular-nums; }
  #tab-reports .db-read .rv-frow .v small { display: block; font-weight: 400; font-size: 11.5px; color: var(--td-text2); }
  @media (max-width: 720px) { #tab-reports .db-read .rv-frow { grid-template-columns: 96px 1fr 96px; gap: 8px; } }
  #tab-reports .db-read .rv-bar { height: 9px; border-radius: 5px; background: var(--td-raised); overflow: hidden; display: flex; }
  #tab-reports .db-read .rv-bar i { display: block; height: 100%; background: var(--td-text); border-radius: 5px; }
  #tab-reports .db-read .rv-bar i.ghost { background: var(--td-border); }
  #tab-reports .db-read .rv-bar i.lost { background: var(--td-brick); border-radius: 0 5px 5px 0; }
  #tab-reports .db-read .rv-dots { display: grid; grid-template-columns: repeat(20, 10px); gap: 5px; margin: 4px 0 10px; }
  #tab-reports .db-read .rv-dots i { width: 9px; height: 9px; border-radius: 50%; border: 1.5px solid var(--td-border); box-sizing: border-box; }
  #tab-reports .db-read .rv-dots i.on { background: var(--td-sage); border-color: var(--td-sage); }
  #tab-reports .db-read .rv-legend { display: flex; gap: 16px; flex-wrap: wrap; font-size: 12.5px; color: var(--td-text2); }
  #tab-reports .db-read .rv-legend b { font-weight: 500; color: var(--td-text); font-variant-numeric: tabular-nums; }
  #tab-reports .db-read .rv-legend .k { display: inline-block; width: 9px; height: 9px; border-radius: 50%; border: 1.5px solid var(--td-border); box-sizing: border-box; margin-right: 5px; }
  #tab-reports .db-read .rv-legend .k.on { background: var(--td-sage); border-color: var(--td-sage); }
  #tab-reports .db-read .rv-note { font-size: 12.5px; color: var(--td-text2); margin-top: 8px; }
  #tab-reports .db-read .rv-segname { font-size: 12.5px; font-weight: 500; color: var(--td-text); margin-top: 10px; }
  #tab-reports .db-read .rv-segbar { height: 12px; border-radius: 6px; background: var(--td-raised); overflow: hidden; display: flex; margin: 4px 0 2px; }
  #tab-reports .db-read .rv-segbar i { display: block; height: 100%; background: var(--td-text); }
  #tab-reports .db-read .rv-segbar i.rest { background: var(--td-border); }
  #tab-reports .db-read .rv-seglbl { display: flex; justify-content: space-between; gap: 10px; font-size: 12px; color: var(--td-text2); font-variant-numeric: tabular-nums; }
  #tab-reports .db-read .rv-card.rv-act { background: var(--td-dusk-tint); border-color: transparent; }
  #tab-reports .db-read .rv-act .rv-cap .t { font-size: 15px; }
  #tab-reports .db-read .rv-tl { position: relative; height: 46px; margin: 18px 6px 0; }
  #tab-reports .db-read .rv-tl .rail { position: absolute; top: 22px; left: 0; right: 0; height: 2px; background: var(--td-border); }
  #tab-reports .db-read .rv-tl .mark { position: absolute; top: 17px; left: 0; width: 12px; height: 12px; border-radius: 50%; background: var(--td-text); }
  #tab-reports .db-read .rv-tl .mlbl { position: absolute; top: -2px; left: 0; font-size: 11.5px; color: var(--td-text2); white-space: nowrap; }
  #tab-reports .db-read .rv-tl .win { position: absolute; top: 16px; height: 14px; border-radius: 7px; background: var(--td-dusk); opacity: .85; }
  #tab-reports .db-read .rv-tl .wlbl { position: absolute; top: -2px; text-align: center; font-size: 11.5px; font-weight: 500; color: var(--td-dusk); white-space: nowrap; }
  #tab-reports .db-read .rv-tl .tick { position: absolute; top: 30px; transform: translateX(-50%); font-size: 11px; color: var(--td-text2); font-variant-numeric: tabular-nums; }
  #tab-reports .db-read .rv-words { background: transparent; border: 0; padding: 0 0 1px; margin-top: 14px; font-family: inherit; font-size: 12.5px; color: var(--td-text2); cursor: pointer; border-bottom: 1px dotted var(--td-border); }
  #tab-reports .db-read .rv-words:hover { color: var(--td-text); }
  #tab-reports .db-read .rv-prose { margin-top: 10px; }

  /* Per-chapter one-line note. */
  #tab-reports .db-ai-note {
    display: flex; gap: 12px; align-items: baseline;
    background: var(--td-dusk-tint); border-radius: 14px;
    padding: 14px 20px; margin-bottom: 16px;
    font-size: 14px; line-height: 1.7; color: var(--td-text);
  }
  #tab-reports .db-ai-note .td-ai-dot { position: relative; top: -1px; }
  #tab-reports .db-ai-note p { margin: 0; max-width: 78ch; }

  /* Facts bar — computed plain-words rows atop each chapter (no AI). */
  #tab-reports .db-factsbar { padding: 8px 24px; margin-bottom: 16px; }
  #tab-reports .db-fact { display: flex; align-items: baseline; gap: 12px; padding: 10px 0; border-top: 1px solid var(--td-raised); font-size: 14px; }
  #tab-reports .db-fact:first-child { border-top: none; }
  #tab-reports .db-fact .t { flex: 1; min-width: 0; color: var(--td-text); }
  #tab-reports .db-fact .v { font-weight: 500; color: var(--td-text); font-variant-numeric: tabular-nums; white-space: nowrap; }

  /* The short version — Overview headline rows, click → chapter. */
  #tab-reports .db-hl { padding: 8px 24px; }
  #tab-reports .db-hl-row { display: flex; align-items: baseline; gap: 14px; padding: 12px 0; border-top: 1px solid var(--td-raised); cursor: pointer; }
  #tab-reports .db-hl-row:first-child { border-top: none; }
  #tab-reports .db-hl-row .k { flex: none; width: 170px; font-size: 12px; font-weight: 500; letter-spacing: .08em; text-transform: uppercase; color: var(--td-text2); }
  #tab-reports .db-hl-row .t { flex: 1; font-size: 14px; color: var(--td-text); }
  #tab-reports .db-hl-row .go { color: var(--td-text2); transition: transform .2s cubic-bezier(.16,1,.3,1); }
  #tab-reports .db-hl-row:hover .go { transform: translateX(3px); color: var(--td-text); }
  @media (max-width: 720px) { #tab-reports .db-hl-row { flex-wrap: wrap; } #tab-reports .db-hl-row .k { width: 100%; } }

  /* Ask about your numbers. */
  #tab-reports .db-ask .db-ask-head { display: flex; align-items: center; gap: 9px; margin-bottom: 12px; }
  #tab-reports .db-ask .db-ask-head h3 { margin: 0; font-size: 15px; font-weight: 500; color: var(--td-text); }
  #tab-reports .db-ask-box { display: flex; gap: 10px; }
  #tab-reports .db-ask-box input {
    flex: 1; min-width: 0; background: var(--td-surface); border: 1px solid var(--td-border);
    border-radius: 8px; padding: 9px 14px; font-family: inherit; font-size: 14px;
    color: var(--td-text); outline: none;
  }
  #tab-reports .db-ask-box input:focus { border-color: var(--td-dusk); }
  #tab-reports .db-ask-send {
    background: var(--td-dusk); border: 1px solid var(--td-dusk); color: #FFFFFF;
    border-radius: 8px; padding: 9px 20px; font-family: inherit; font-size: 13.5px;
    font-weight: 500; cursor: pointer; white-space: nowrap;
  }
  #tab-reports .db-ask-send:hover { background: var(--td-dusk-hi); border-color: var(--td-dusk-hi); }
  #tab-reports .db-ask-send[disabled] { opacity: .55; cursor: default; }
  #tab-reports .db-ask-chips { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 10px; }
  #tab-reports .db-ask-chip {
    background: transparent; border: 1px solid var(--td-border); border-radius: 999px;
    padding: 3px 13px; font-family: inherit; font-size: 12.5px; color: var(--td-text2); cursor: pointer;
    transition: color .2s cubic-bezier(.16,1,.3,1), border-color .2s cubic-bezier(.16,1,.3,1);
  }
  #tab-reports .db-ask-chip:hover { color: var(--td-text); border-color: var(--td-text2); }
  #tab-reports .db-ask-qa { margin-top: 16px; padding-top: 16px; border-top: 1px solid var(--td-raised); }
  #tab-reports .db-ask-qa .q { font-size: 13.5px; font-weight: 500; color: var(--td-text); margin-bottom: 6px; }
  #tab-reports .db-ask-qa .a { display: flex; gap: 12px; align-items: baseline; font-size: 14px; line-height: 1.7; color: var(--td-text); }
  #tab-reports .db-ask-qa .a .td-ai-dot { position: relative; top: -1px; }
  #tab-reports .db-ask-qa .a p { margin: 0; max-width: 74ch; }
  #tab-reports .db-ask-qa .a.muted { color: var(--td-text2); font-size: 13px; }

  /* Panels — every chart card is the Tacet card at 24px padding; heads carry a
     15px title with the window caption on the right. 16px gaps everywhere. */
  #tab-reports .db-grid-2 { gap: 16px; }
  #tab-reports .card.db-panel { padding: 24px; margin-bottom: 16px; }
  #tab-reports .db-grid-2 .card.db-panel { margin-bottom: 0; }
  #tab-reports .db-card-head { align-items: baseline; justify-content: flex-start; gap: 12px; margin-bottom: 16px; }
  #tab-reports .db-card-head h3 { font-size: 15px; font-weight: 500; color: var(--td-text); }
  #tab-reports .db-card-head .td-meta { margin-left: auto; font-size: 12.5px; text-align: right; }
  #tab-reports .db-card-head .db-metric-btns { margin-left: auto; }
  #tab-reports #db-trend .db-chart { height: 260px; }

  @media (max-width: 720px) {
    /* Reports has no phone variant this pass — stack what's there. */
    #tab-reports .db-grid-2 { grid-template-columns: 1fr; }
    #tab-reports .db-range { flex-wrap: wrap; }
  }

  /* ════════════════════════════════════════════════════════════════════
     TACET SALES CHANNELS SKIN
     Third Tacet surface (after Dashboard + Reports); spec:
     reference/sales-redesign-mockup.html. Scoped to #tab-sales; the base
     .sales-* classes keep their old look for the Delivery hub, which
     reuses them. Rules kept honest here:
       - Sage is status ("connected"), never a control colour: the on/off
         toggles are Ink, not green.
       - Channel icons are monochrome line-work on a raised tile — no
         per-brand colour chips.
       - Dusk appears at most twice: the not-set-up card's "Set up" CTA
         (salesWireSetup gives it .btn-primary) and nothing else.
       - A closed location is amber (attention), never red.
     ════════════════════════════════════════════════════════════════════ */
  /* Centered 1120px column like Dashboard/Reports. */
  #tab-sales .td-head, #tab-sales .tds-body {
    max-width: 1120px; margin-left: auto; margin-right: auto;
  }
  #tab-sales .td-head { align-items: center; flex-wrap: wrap; }
  .tds-actions { margin-left: auto; display: inline-flex; gap: 8px; }

  /* Buttons inside the tab wear Tacet: quiet outline, ink text; the hub's
     "Set up" (.btn-primary) is the page's one Dusk element. */
  #tab-sales .btn {
    background: transparent; border-color: var(--td-border); color: var(--td-text);
    border-radius: 8px; font-weight: 500; box-shadow: none;
    transition: border-color .2s cubic-bezier(.16,1,.3,1), background .2s cubic-bezier(.16,1,.3,1);
  }
  #tab-sales .btn:hover { border-color: var(--td-text2); background: transparent; }
  #tab-sales .btn-ghost { border-color: transparent; color: var(--td-text2); }
  #tab-sales .btn-ghost:hover { border-color: transparent; color: var(--td-text); background: var(--td-raised); }
  #tab-sales .btn-primary {
    background: var(--td-dusk); border-color: var(--td-dusk); color: #FFFFFF;
  }
  #tab-sales .btn-primary:hover { background: var(--td-dusk-hi); border-color: var(--td-dusk-hi); }

  #tab-sales .sales-of { font-size: 15px; color: var(--td-text2); font-weight: 400; }

  /* Location close bar (injected by csRenderLocations) — a flat raised band,
     no border, like the mockup's cream strip. */
  #tab-sales .sales-locbar {
    background: var(--td-raised); border: none; box-shadow: none;
    border-radius: 14px; padding: 14px 20px; margin: 0 0 16px;
  }
  #tab-sales .sales-locbar-lead { font-weight: 500; font-size: 14px; color: var(--td-text); }
  #tab-sales .sales-locbar-lead svg { width: 16px; height: 16px; color: var(--td-text2); }
  #tab-sales .sales-locbar-hint { font-size: 12.5px; color: var(--td-text2); }
  @media (max-width: 720px) { #tab-sales .sales-locbar-hint { display: none; } }
  #tab-sales .sales-locpill {
    background: var(--td-surface); border: 1px solid var(--td-border);
    color: var(--td-text); font-weight: 500; padding: 6px 14px;
    transition: border-color .2s cubic-bezier(.16,1,.3,1);
  }
  #tab-sales .sales-locpill:hover { border-color: var(--td-text2); }
  #tab-sales .sales-locpill .act { color: var(--td-text2); font-weight: 400; }
  /* Closed = attention, so amber — never red, never a brand colour. */
  #tab-sales .sales-locpill.closed {
    background: var(--td-amber-tint); border-color: transparent; color: var(--td-amber-ink);
  }
  #tab-sales .sales-locpill.closed .act { color: var(--td-amber-ink); }

  /* Channel cards. */
  #tab-sales .sales-grid {
    grid-template-columns: repeat(auto-fill, minmax(330px, 1fr)); gap: 16px;
    /* Equalize EVERY row to the tallest so all channel cards match — cards flex
       to fill, content stays top-packed, any slack sits at the card bottom. */
    grid-auto-rows: 1fr;
  }
  #tab-sales .sales-ch {
    background: var(--td-surface); border: 1px solid var(--td-border);
    border-radius: 14px; padding: 22px 24px 18px;
    box-shadow: 0 1px 2px rgba(17,26,43,.05), 0 4px 16px rgba(17,26,43,.04);
    display: flex; flex-direction: column; gap: 16px;
    /* grid-auto-rows:1fr makes the row track definite; height:100% forces every
       card to fill it so a light card (e.g. Eleme "Not set up") matches its
       row-mate instead of collapsing to its own content height. */
    height: 100%;
  }
  #tab-sales .sales-ch:hover { transform: none; border-color: var(--td-border); box-shadow: 0 1px 2px rgba(17,26,43,.05), 0 4px 16px rgba(17,26,43,.04); }
  #tab-sales .sales-ch[data-on="0"] { opacity: 1; background: transparent; box-shadow: none; }

  /* Monochrome icon tile — overrides the coloured .wc/.wb/.mt/.gy/.el chips. */
  #tab-sales .sales-ch-ico {
    width: 40px; height: 40px; border-radius: 12px;
    background: var(--td-raised); color: var(--td-text);
  }
  #tab-sales .sales-ch-ico svg { width: 18px; height: 18px; }
  #tab-sales .sales-ch[data-on="0"] .sales-ch-ico {
    background: transparent; border: 1px dashed var(--td-border); color: var(--td-text2);
  }
  #tab-sales .sales-ch-name { font-size: 15px; font-weight: 500; color: var(--td-text); }
  #tab-sales .sales-ch-stat { font-size: 12.5px; color: var(--td-text2); gap: 7px; margin-top: 2px; }
  #tab-sales .sales-dot { width: 7px; height: 7px; }
  #tab-sales .sales-dot.ok   { background: var(--td-sage); }
  #tab-sales .sales-dot.off  { background: var(--td-border); }
  #tab-sales .sales-dot.warn { background: var(--td-amber); }
  #tab-sales .sales-dot.err  { background: var(--td-brick-ink); }

  /* Ink toggle — on is Ink (Paper in dark), half-fill = open at some
     locations only. Green is not a control colour in Tacet. */
  #tab-sales .sales-toggle {
    width: 40px; height: 22px; background: var(--td-border); border: none;
    transition: background .2s cubic-bezier(.16,1,.3,1);
  }
  #tab-sales .sales-toggle .knob {
    width: 16px; height: 16px; top: 3px; left: 3px;
    background: var(--td-surface); box-shadow: 0 1px 2px rgba(17,26,43,.25);
    transition: left .2s cubic-bezier(.16,1,.3,1);
  }
  #tab-sales .sales-toggle.on { background: var(--td-text); }
  #tab-sales .sales-toggle.on .knob { left: 21px; }
  #tab-sales .sales-toggle.partial { background: linear-gradient(90deg, var(--td-text) 50%, var(--td-border) 50%); }
  #tab-sales .sales-toggle.partial .knob { left: 12px; }
  #tab-sales .sales-toggle:focus-visible { outline: 2px solid var(--td-dusk); outline-offset: 2px; }

  #tab-sales .sales-ch-metrics { gap: 12px 16px; }
  #tab-sales .sales-ch-metrics .m-l { font-size: 12px; color: var(--td-text2); }
  #tab-sales .sales-ch-metrics .m-n {
    font-size: 19px; font-weight: 500; letter-spacing: -.01em; color: var(--td-text);
  }
  #tab-sales .sales-ch[data-on="0"] .sales-ch-metrics .m-n { color: var(--td-text2); }
  #tab-sales .sales-ch-foot { border-top: 1px solid var(--td-border); padding-top: 13px; }
  /* Connected cards empty their footer (Configure is now the gear) — hide it. */
  #tab-sales .sales-ch-foot:empty { display: none; }

  /* Gear (Configure) sits just left of the master toggle, top-right of a card. */
  #tab-sales .sales-ch-gear {
    margin-left: auto; border: none; background: transparent; color: var(--td-text2);
    cursor: pointer; padding: 6px; border-radius: 8px; display: grid; place-items: center;
    transition: background .2s cubic-bezier(.16,1,.3,1), color .2s cubic-bezier(.16,1,.3,1);
  }
  #tab-sales .sales-ch-gear:hover { background: var(--td-raised); color: var(--td-text); }
  #tab-sales .sales-ch-gear svg { width: 16px; height: 16px; }
  /* With a gear present the toggle no longer needs to claim the free space. */
  #tab-sales .sales-ch-gear + .sales-toggle { margin-left: 6px; }

  /* Small inline toggle for the per-location rows. */
  #tab-sales .sales-toggle.sm { width: 34px; height: 19px; }
  #tab-sales .sales-toggle.sm .knob { width: 13px; height: 13px; top: 3px; left: 3px; }
  #tab-sales .sales-toggle.sm.on .knob { left: 18px; }

  /* Per-location rows inside a channel card. */
  #tab-sales .sales-ch-locs { border-top: 1px solid var(--td-border); padding-top: 12px; }
  #tab-sales .sales-ch-locs-hd {
    display: flex; align-items: center; gap: 8px; font-size: 11px; font-weight: 500;
    letter-spacing: .08em; text-transform: uppercase; color: var(--td-text2); margin-bottom: 4px;
  }
  #tab-sales .sales-ch-locs-note { margin-left: auto; font-size: 11px; letter-spacing: normal; text-transform: none; }
  #tab-sales .sales-ch-loc-row { display: flex; align-items: center; gap: 10px; padding: 8px 0; }
  #tab-sales .sales-ch-loc-row + .sales-ch-loc-row { border-top: 1px solid var(--td-raised); }
  #tab-sales .sales-ch-loc-nm { font-size: 13.5px; font-weight: 500; color: var(--td-text); }
  #tab-sales .sales-ch-loc-row.closed .sales-ch-loc-nm { color: var(--td-text2); }
  #tab-sales .sales-ch-loc-st { margin-left: auto; font-size: 12px; color: var(--td-text2); }
  #tab-sales .sales-ch-loc-note { font-size: 11px; color: var(--td-amber-ink); }

  /* Locations section: kicker with the Add-location button + a chip row card. */
  #tab-sales .sales-loc-kicker { display: flex; align-items: center; gap: 12px; }
  #tab-sales .sales-loc-kicker .btn {
    margin-left: auto; text-transform: none; letter-spacing: normal;
    display: inline-flex; align-items: center; gap: 6px;
  }
  #tab-sales .sales-loc-grid { margin-bottom: 8px; }
  /* Location cards re-render on every toggle, so don't re-run the settle rise. */
  #tab-sales .sales-loc-grid .sales-ch { animation: none; }
  #tab-sales .sales-loc-chips {
    display: flex; flex-wrap: wrap; align-content: flex-start; gap: 7px;
    border-top: 1px solid var(--td-border); padding-top: 14px;
    /* Reserve two chip rows so a 3-channel store is the same height as a
       4/5-channel one — every location card matches regardless of chip count.
       border-box, so this spans the 14px divider padding + two ~23px chip rows
       + the 7px gap. */
    min-height: 68px;
  }
  #tab-sales .sales-chip {
    display: inline-flex; align-items: center; gap: 6px; font-size: 12px; color: var(--td-text);
    background: var(--td-raised); border-radius: 999px; padding: 4px 11px;
  }
  #tab-sales .sales-chip-dot { width: 6px; height: 6px; border-radius: 50%; background: var(--td-sage); flex: none; }
  #tab-sales .sales-chip.off { color: var(--td-text2); background: transparent; border: 1px solid var(--td-border); }
  #tab-sales .sales-chip.off .sales-chip-dot { background: var(--td-border); }

  /* ── Full-screen Configure overlay (opened by a channel's gear) ──
     A 16px-inset sheet (.cfg-shell) over a dim backdrop. Its own top bar
     (.cfg-bar: icon + "Configuration" + ✕) replaces each panel's header, which
     is hidden below. The panel renders into #cfg-host and gets the same Tacet
     .mc-wiz skin as in-tab (the :is(#tab-sales,.cfg-host) selectors above). Body
     level so it clears the rail/topbar; z-index below toasts (400) so a panel's
     "Saved" confirmation stays visible. */
  .cfg-ov {
    position: fixed; inset: 0; z-index: 390; background: rgba(17,26,43,.42);
    opacity: 0; transition: opacity .25s cubic-bezier(.16,1,.3,1);
    font-family: 'Inter', 'IBM Plex Sans SC', 'PingFang SC', 'Hiragino Sans GB', system-ui, sans-serif;
    color: var(--td-text);
  }
  .cfg-ov.in { opacity: 1; }
  .cfg-shell {
    position: absolute; inset: 16px; display: flex; flex-direction: column; overflow: hidden;
    background: var(--td-paper); border: 1px solid var(--td-border); border-radius: 16px;
    box-shadow: 0 8px 24px rgba(17,26,43,.12), 0 40px 90px rgba(17,26,43,.28);
    transform: translateY(12px) scale(.995); opacity: 0;
    transition: transform .3s cubic-bezier(.16,1,.3,1), opacity .3s cubic-bezier(.16,1,.3,1);
  }
  .cfg-ov.in .cfg-shell { transform: none; opacity: 1; }
  .cfg-bar {
    flex: none; display: flex; align-items: center; gap: 13px;
    padding: 13px 16px; border-bottom: 1px solid var(--td-border); background: var(--td-surface);
  }
  .cfg-bar-ico {
    width: 34px; height: 34px; border-radius: 10px; flex: none;
    background: var(--td-raised); color: var(--td-text); display: grid; place-items: center;
  }
  .cfg-bar-ico svg { width: 17px; height: 17px; }
  .cfg-bar-t { font-size: 15px; font-weight: 500; color: var(--td-text); }
  .cfg-bar-sp { margin-left: auto; }
  .cfg-bar-x {
    border: none; background: transparent; color: var(--td-text2); cursor: pointer;
    font-size: 16px; line-height: 1; padding: 8px; border-radius: 8px;
  }
  .cfg-bar-x:hover { background: var(--td-raised); color: var(--td-text); }
  .cfg-host {
    flex: 1; overflow: auto; -webkit-overflow-scrolling: touch;
    padding: 22px clamp(16px, 4vw, 44px);
  }
  /* Center the rendered config on a comfortable column. */
  .cfg-host > * { max-width: 860px; margin-left: auto; margin-right: auto; }
  /* The overlay's top bar owns the title + ✕ — hide each panel's own header. */
  .cfg-host .mc-head { display: none; }
  .cfg-host .ig-wiz:not(.mc-wiz) > div:first-child { display: none; }
  @media (max-width: 640px) { .cfg-shell { inset: 0; border-radius: 0; } .cfg-host { padding: 16px; } }
  @media (prefers-reduced-motion: reduce) { .cfg-ov, .cfg-shell { transition: none; } }

  /* Cards settle in once — same 8px rise as the other Tacet screens. */
  #tab-sales .db-kpi, #tab-sales .sales-locbar, #tab-sales .sales-ch {
    animation: td-settle .6s cubic-bezier(.16,1,.3,1) both;
  }
  #tab-sales .db-kpi:nth-child(2), #tab-sales .sales-ch:nth-child(2) { animation-delay: .06s; }
  #tab-sales .db-kpi:nth-child(3), #tab-sales .sales-ch:nth-child(3) { animation-delay: .12s; }
  #tab-sales .db-kpi:nth-child(4), #tab-sales .sales-ch:nth-child(4) { animation-delay: .18s; }
  #tab-sales .sales-ch:nth-child(5) { animation-delay: .24s; }
  #tab-sales .sales-locbar { animation-delay: .08s; }
  @media (prefers-reduced-motion: reduce) {
    #tab-sales .db-kpi, #tab-sales .sales-locbar, #tab-sales .sales-ch { animation: none; }
  }

  /* By-location popup (csPopup renders it at body level, so these are not
     #tab-sales-scoped — the popup only ever opens from the Sales page). */
  .cs-ov {
    position: fixed; inset: 0; z-index: 9999;
    background: rgba(17,26,43,.4);
    display: flex; align-items: center; justify-content: center; padding: 20px;
  }
  .cs-modal {
    background: var(--td-surface); border: 1px solid var(--td-border);
    border-radius: 14px; max-width: 420px; width: 100%; padding: 20px 24px 22px;
    box-shadow: 0 4px 12px rgba(17,26,43,.08), 0 24px 64px rgba(17,26,43,.16);
    font-family: 'Inter', 'IBM Plex Sans SC', 'PingFang SC', 'Hiragino Sans GB', system-ui, sans-serif;
    color: var(--td-text);
    animation: td-settle .35s cubic-bezier(.16,1,.3,1) both;
  }
  .cs-modal-head { display: flex; align-items: center; gap: 10px; margin-bottom: 2px; }
  .cs-modal-head b { font-size: 16px; font-weight: 500; color: var(--td-text); }
  .cs-modal-x {
    margin-left: auto; border: none; background: transparent; cursor: pointer;
    color: var(--td-text2); font-size: 15px; line-height: 1; padding: 6px; border-radius: 8px;
  }
  .cs-modal-x:hover { background: var(--td-raised); color: var(--td-text); }
  .cs-sub { font-size: 12.5px; color: var(--td-text2); margin-bottom: 10px; }
  .cs-row { display: flex; align-items: center; gap: 12px; padding: 12px 0; border-top: 1px solid var(--td-border); }
  .cs-row .cs-nm { flex: 1; min-width: 0; font-size: 14px; font-weight: 500; }
  .cs-row .muted { color: var(--td-text2); }
  .cs-dot { width: 7px; height: 7px; border-radius: 50%; flex: none; display: inline-block; }
  .cs-dot.open { background: var(--td-sage); }
  .cs-dot.closed { background: var(--td-border); }
  .cs-foot { display: flex; gap: 8px; justify-content: flex-end; margin-top: 14px; }
  .cs-modal .btn {
    background: transparent; border: 1px solid var(--td-border); color: var(--td-text);
    border-radius: 8px; font-weight: 500; box-shadow: none;
  }
  .cs-modal .btn:hover { border-color: var(--td-text2); background: transparent; }
  /* "Open" is the popup's strong action — Ink, not Dusk (nothing automated). */
  .cs-modal .btn-primary { background: var(--td-text); border-color: var(--td-text); color: var(--td-paper); }
  .cs-modal .btn-primary:hover { background: var(--td-text); border-color: var(--td-text); opacity: .88; }

  /* ── Mini App configuration (.mc-wiz) — Tacet skin ──
     Opened by Configure on the Mini app card; renders in #sales-setup-host
     inside #tab-sales. Scoped to .mc-wiz so the other credential pages that
     reuse the base .mc- and .ig-wiz classes aren't half-restyled. */
  :is(#tab-sales, .cfg-host) .mc-wiz {
    background: var(--td-surface); border: 1px solid var(--td-border);
    border-radius: 14px; padding: 24px; margin: 4px 0 16px; color: var(--td-text);
    box-shadow: 0 1px 2px rgba(17,26,43,.05), 0 4px 16px rgba(17,26,43,.04);
    /* Remap the admin's base tokens to Tacet inside the card. The web-config
       (wc-*) and setup-wizard (mw-*) inline rules read these vars, so they
       recolour themselves to Tacet without a per-class override each. Literal
       rgba() spots + semantic bits (toggles, the one Dusk CTA) are fixed below. */
    --bg-pane: var(--td-surface);
    --bg-elev-1: var(--td-raised);
    --bg-elev-2: var(--td-raised);
    --line: var(--td-border);
    --line-soft: var(--td-border);
    --line-strong: var(--td-border);
    --text-1: var(--td-text);
    --text-2: var(--td-text);
    --text-3: var(--td-text2);
    --text-4: var(--td-text2);
    --accent: var(--td-sage-ink);       /* "ready / live / ok" cues → sage */
    --brass: var(--td-amber);           /* "now / waiting" cues → amber */
    --brass-hi: var(--td-amber);
    --danger: var(--td-brick-ink);
  }
  :is(#tab-sales, .cfg-host) .mc-wiz .mc-head { align-items: center; }
  :is(#tab-sales, .cfg-host) .mc-wiz .mc-title { font-size: 18px; font-weight: 500; letter-spacing: -.01em; color: var(--td-text); }
  :is(#tab-sales, .cfg-host) .mc-wiz .mc-chips { margin: 16px 0 4px; }
  :is(#tab-sales, .cfg-host) .mc-wiz .mc-chip {
    border-color: var(--td-border); background: var(--td-raised); color: var(--td-text2);
    font-weight: 500;
  }
  /* "ready" chips = all-clear = sage (status colour, never decoration). */
  :is(#tab-sales, .cfg-host) .mc-wiz .mc-chip.on {
    background: var(--td-sage-tint); color: var(--td-sage-ink); border-color: transparent;
  }
  :is(#tab-sales, .cfg-host) .mc-wiz .mc-sec { border-top-color: var(--td-border); margin-top: 20px; padding-top: 20px; }
  :is(#tab-sales, .cfg-host) .mc-wiz .mc-sec-head b { color: var(--td-text); font-size: 14px; font-weight: 500; }
  /* Section icon: monochrome line-work on a raised tile — no brand-colour chip. */
  :is(#tab-sales, .cfg-host) .mc-wiz .mc-ico { background: var(--td-raised); color: var(--td-text); border-radius: 10px; }
  :is(#tab-sales, .cfg-host) .mc-wiz .field-label {
    color: var(--td-text); font-weight: 500; font-size: 13px;
    display: inline-flex; align-items: center;
    margin-bottom: 6px;
  }
  :is(#tab-sales, .cfg-host) .mc-wiz .mc-opt { border-color: var(--td-border); color: var(--td-text2); font-weight: 500; }
  :is(#tab-sales, .cfg-host) .mc-wiz input, :is(#tab-sales, .cfg-host) .mc-wiz textarea {
    background: var(--td-surface); border: 1px solid var(--td-border); color: var(--td-text);
    border-radius: 8px; padding: 9px 12px; font-size: 14px; width: 100%;
    transition: border-color .2s cubic-bezier(.16,1,.3,1);
  }
  :is(#tab-sales, .cfg-host) .mc-wiz input::placeholder, :is(#tab-sales, .cfg-host) .mc-wiz textarea::placeholder { color: var(--td-text2); opacity: .7; }
  :is(#tab-sales, .cfg-host) .mc-wiz input:focus, :is(#tab-sales, .cfg-host) .mc-wiz textarea:focus { outline: none; border-color: var(--td-dusk); }
  :is(#tab-sales, .cfg-host) .mc-wiz input[readonly] { background: var(--td-raised); color: var(--td-text2); }
  :is(#tab-sales, .cfg-host) .mc-wiz .mc-saved { background: var(--td-raised); border-color: var(--td-border); }
  :is(#tab-sales, .cfg-host) .mc-wiz .mc-saved > svg { color: var(--td-sage-ink); }
  :is(#tab-sales, .cfg-host) .mc-wiz .mc-saved .tx { color: var(--td-text); }
  :is(#tab-sales, .cfg-host) .mc-wiz .mc-saved .dots { color: var(--td-text2); }
  :is(#tab-sales, .cfg-host) .mc-wiz .mc-keep { color: var(--td-text2); }
  :is(#tab-sales, .cfg-host) .mc-wiz .mc-keep:hover { color: var(--td-text); }
  :is(#tab-sales, .cfg-host) .mc-wiz .mc-foot { border-top-color: var(--td-border); margin-top: 20px; padding-top: 16px; }
  :is(#tab-sales, .cfg-host) .mc-wiz .mc-note { color: var(--td-text2); }
  :is(#tab-sales, .cfg-host) .mc-wiz .btn-icon svg { width: 15px; height: 15px; }
  :is(#tab-sales, .cfg-host) .mc-wiz .helplink {
    display: inline-flex; align-items: center; gap: 6px;
    background: none; border: 0; cursor: pointer; padding: 0;
    color: var(--td-text2); font: 500 12.5px var(--f-sans);
  }
  :is(#tab-sales, .cfg-host) .mc-wiz .helplink:hover { color: var(--td-text); }
  :is(#tab-sales, .cfg-host) .mc-wiz .helplink svg { width: 14px; height: 14px; }
  /* The footer's main action is the page's one Dusk CTA (Save & verify / Save
     changes / Next). Inline strong actions (Add domain, Check DNS) stay Ink so
     Dusk keeps its "this is THE action" meaning. */
  :is(#tab-sales, .cfg-host) .mc-wiz .btn-primary { background: var(--td-text); border-color: var(--td-text); color: var(--td-paper); }
  :is(#tab-sales, .cfg-host) .mc-wiz .btn-primary:hover { background: var(--td-text); border-color: var(--td-text); opacity: .9; }
  :is(#tab-sales, .cfg-host) .mc-wiz #mc_save,
  :is(#tab-sales, .cfg-host) .mc-wiz #wc_save,
  :is(#tab-sales, .cfg-host) .mc-wiz #mw_next { background: var(--td-dusk); border-color: var(--td-dusk); color: #fff; }
  :is(#tab-sales, .cfg-host) .mc-wiz #mc_save:hover,
  :is(#tab-sales, .cfg-host) .mc-wiz #wc_save:hover,
  :is(#tab-sales, .cfg-host) .mc-wiz #mw_next:hover { background: var(--td-dusk-hi); border-color: var(--td-dusk-hi); opacity: 1; }
  :is(#tab-sales, .cfg-host) .mc-wiz #mc_save.mc-ok { background: var(--td-sage); border-color: var(--td-sage); color: #fff; }

  /* ── Setup wizard (mw-*) — Tacet, beyond the token remap ──
     The step instructions (mc-purpose + mw-help lists) stay: this is an
     onboarding flow and the how-to IS the feature — just reskinned. */
  :is(#tab-sales, .cfg-host) .mc-wiz .mc-subtitle { color: var(--td-text2); }
  :is(#tab-sales, .cfg-host) .mc-wiz .mc-purpose { color: var(--td-text2); }
  :is(#tab-sales, .cfg-host) .mc-wiz .mw-step.now { border-color: var(--td-border); color: var(--td-text); }
  :is(#tab-sales, .cfg-host) .mc-wiz .mw-step.now .n { background: var(--td-text); color: var(--td-paper); }
  :is(#tab-sales, .cfg-host) .mc-wiz .mw-step.done { color: var(--td-sage-ink); border-color: transparent; }
  :is(#tab-sales, .cfg-host) .mc-wiz .mw-step.done .n { background: var(--td-sage); color: #fff; }
  :is(#tab-sales, .cfg-host) .mc-wiz .mw-help li { color: var(--td-text); }
  :is(#tab-sales, .cfg-host) .mc-wiz .mw-done .big { background: var(--td-sage-tint); color: var(--td-sage-ink); }
  :is(#tab-sales, .cfg-host) .mc-wiz .mw-done .big.later { background: var(--td-amber-tint); color: var(--td-amber-ink); }
  :is(#tab-sales, .cfg-host) .mc-wiz .mc-chip.later { background: var(--td-amber-tint); color: var(--td-amber-ink); border-color: transparent; }
  :is(#tab-sales, .cfg-host) .mc-wiz .mw-todo .t > svg { color: var(--td-amber-ink); }
  :is(#tab-sales, .cfg-host) .mc-wiz .mw-later:hover { color: var(--td-amber-ink); }

  /* ── Web store config (wc-*) — Tacet, beyond the token remap ── */
  /* Live address dot = sage; reserved = muted. */
  :is(#tab-sales, .cfg-host) .mc-wiz .wc-addr .live { background: var(--td-sage); box-shadow: 0 0 0 4px var(--td-sage-tint); }
  :is(#tab-sales, .cfg-host) .mc-wiz .wc-addr .live.off { background: var(--td-text2); box-shadow: 0 0 0 4px var(--td-raised); }
  /* Method / payment icons: monochrome line-work on a raised tile. */
  :is(#tab-sales, .cfg-host) .mc-wiz .wc-mico { background: var(--td-raised); border-color: var(--td-border); color: var(--td-text); }
  :is(#tab-sales, .cfg-host) .mc-wiz .wc-mstat.ok { color: var(--td-sage-ink); }
  /* Sign-in toggles are Ink, matching the channel-card toggles (green is not a
     control colour in Tacet). */
  :is(#tab-sales, .cfg-host) .mc-wiz .wc-tog { background: var(--td-border); }
  :is(#tab-sales, .cfg-host) .mc-wiz .wc-tog .knob { background: var(--td-surface); }
  :is(#tab-sales, .cfg-host) .mc-wiz .wc-tog.on { background: var(--td-text); }
  /* Domain DNS stepper: done = sage, waiting = amber. */
  :is(#tab-sales, .cfg-host) .mc-wiz .wc-dstep.done .num { background: var(--td-sage); border-color: var(--td-sage); color: #fff; }
  :is(#tab-sales, .cfg-host) .mc-wiz .wc-dstep.now .num { background: var(--td-amber); border-color: var(--td-amber); color: #fff; }
  :is(#tab-sales, .cfg-host) .mc-wiz .wc-dstep.done .stat { color: var(--td-sage-ink); }
  :is(#tab-sales, .cfg-host) .mc-wiz .wc-dstep.now .stat { color: var(--td-amber-ink); }
  /* Inputs focus Dusk (the Tacet input accent), not brass. */
  :is(#tab-sales, .cfg-host) .mc-wiz .wc-di input:focus { border-color: var(--td-dusk); box-shadow: 0 0 0 3px var(--td-dusk-tint); }

  /* ── Two-column, no-scroll layout for config panels in the overlay ──
     The sections (.mc-sec) flow into two balanced columns (multicol); chips +
     footer span full width; each section becomes its own card. :has(> .mc-sec)
     targets the three config panels (mini/web/meituan — mini/web are .ig-wiz
     .mc-wiz, meituan is .ig-wiz) but NOT the Add-location form (.ig-wiz-grid, no
     .mc-sec). #cfg-host (id) keeps these above the :is() skin; overlay-only so
     the inline in-tab config keeps its single stacked column. Falls back to one
     column where :has()/multicol isn't supported. */
  #cfg-host > .ig-wiz:has(> .mc-sec) {
    max-width: 1120px; margin: 0 auto;
    background: transparent; border: none; box-shadow: none; padding: 0;
  }
  /* Two balanced columns only when there are 2+ sections (mini/web). A single-
     section panel (meituan) stays full width and fills it with its own grid. */
  #cfg-host > .ig-wiz:has(> .mc-sec ~ .mc-sec) { columns: 2; column-gap: 18px; }
  #cfg-host > .ig-wiz:has(> .mc-sec) > .mc-chips,
  #cfg-host > .ig-wiz:has(> .mc-sec) > .mc-foot { column-span: all; }
  #cfg-host > .ig-wiz:has(> .mc-sec) > .mc-chips { margin: 0 0 8px; }
  #cfg-host > .ig-wiz:has(> .mc-sec) > .mc-sec {
    break-inside: avoid;
    margin: 0 0 16px; padding: 16px 18px;
    border: 1px solid var(--td-border); border-radius: 14px; background: var(--td-surface);
  }
  @media (max-width: 760px) { #cfg-host > .ig-wiz:has(> .mc-sec ~ .mc-sec) { columns: 1; } }
  /* Meituan renders each store as its own .mc-sec, so the store cards get the
     same white-surface section-card treatment as the mini-app / web configs. */


  /* ═══════════════════════════════════════════════════════════════════
     TACET ACTIVITY SKIN (act-*) — Inventory activity: read-first digest
     cards + quiet ledger. Spec: reference/inventory-activity-tacet-mockup
     .html (option C). Brand rules kept honest here:
       - a sale is normal, so an outbound delta stays ink; stock-in is
         sage; a zero balance is the only amber on the table.
       - channels have no colours — a channel is a filter, not a
         traffic light. Active tab = ink underline.
     Reuses the shared --td-* tokens (dark default / light via
     [data-theme="light"]), same as dashboard/reports/sales.
     ═══════════════════════════════════════════════════════════════════ */
  #prod-view-activity .num { font-variant-numeric: tabular-nums; font-feature-settings: "tnum"; }

  /* ── Products sub-views (Catalogue ↔ Stock activity) + the four stock cards ──
     The switcher speaks the same quiet-tab language as #act-tabs below (ink
     underline, no colour — a view is a place, not a status). The cards echo
     the pa-recap surface one size down; amber appears ONLY on the hit-zero
     card while its count is above zero, matching the ledger's one-amber
     rule. Everything rides the shared --td-* tokens so both themes work. */
  .stk-views { display: flex; flex-wrap: wrap; gap: 2px;
    border-bottom: 1px solid var(--td-border, #E4E0D5); margin: 0 0 var(--s4, 16px); }
  .stk-view { appearance: none; background: none; border: none;
    border-bottom: 2px solid transparent; margin-bottom: -1px;
    font: 500 13.5px var(--f-sans); color: var(--td-text2, #58606E);
    padding: 8px 12px 10px; cursor: pointer; white-space: nowrap;
    display: inline-flex; align-items: center; gap: 7px;
    transition: color .2s cubic-bezier(.16,1,.3,1); }
  .stk-view:hover { color: var(--td-text, #111A2B); }
  .stk-view.on { color: var(--td-text, #111A2B); font-weight: 600;
    border-bottom-color: var(--td-text, #111A2B); }
  .stk-view-dot { width: 7px; height: 7px; border-radius: 50%; flex: none;
    background: var(--td-amber); }

  .stk-minis { display: grid; grid-template-columns: repeat(4, 1fr);
    gap: 10px; margin: 0 0 var(--s4, 16px); }
  .stk-mini { appearance: none; text-align: left; cursor: pointer;
    display: flex; flex-direction: column; gap: 1px; min-width: 0;
    background: var(--td-surface, #fff); border: 1px solid var(--td-border, #E4E0D5);
    border-radius: 12px; padding: 10px 14px;
    font-family: var(--f-sans); color: var(--td-text, #111A2B);
    transition: border-color .2s cubic-bezier(.16,1,.3,1); }
  .stk-mini:hover { border-color: var(--td-text2, #58606E); }
  .stk-mini .lbl { font-size: 11.5px; color: var(--td-text2, #58606E);
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 100%; }
  .stk-mini .val { font-size: 19px; font-weight: 600; line-height: 1.3;
    font-variant-numeric: tabular-nums; font-feature-settings: "tnum"; }
  .stk-mini .val.is-in { color: var(--td-sage-ink, #4A6249); }
  .stk-mini .hint { font-size: 11px; color: var(--td-text2, #58606E); min-height: 14px;
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 100%; }
  .stk-mini.warn { background: var(--td-amber-tint, #F7EEDE); }
  .stk-mini.warn .val,
  .stk-mini.warn .hint { color: var(--td-amber-ink, #8A6425); }
  @media (max-width: 760px) { .stk-minis { grid-template-columns: 1fr 1fr; } }

  /* ══ Categories sub-view — "the shelf" ═══════════════════════════════════
     Built to the Tacet canon, not adapted from the old tree editor: paper,
     one hairline border, 14px card radius, no shadow, no gradient. Names sit
     as plain text until clicked (a wall of input boxes shouts); grips and row
     actions only surface on hover. Colour is status, never decoration — amber
     marks a category the shop is hiding, and the single Dusk dot appears only
     on the rename card, where Tacet re-tags products for you.
     ═════════════════════════════════════════════════════════════════════ */
  .cat2-status .num { font-variant-numeric: tabular-nums; font-feature-settings: "tnum"; }

  /* status line — replaces three KPI tiles with one sentence */
  .cat2-status { display: flex; align-items: center; gap: 16px; flex-wrap: wrap;
    background: var(--td-surface, #fff); border: 1px solid var(--td-border, #E4E0D5);
    border-radius: 14px; padding: 13px 18px; margin: 0 0 var(--s4, 16px);
    font-family: var(--f-sans); }
  .cat2-facts { display: flex; align-items: center; gap: 16px; flex-wrap: wrap;
    font-size: 13.5px; color: var(--td-text, #111A2B); min-width: 0; }
  .cat2-fact { display: inline-flex; align-items: center; gap: 7px;
    font-variant-numeric: tabular-nums; font-feature-settings: "tnum"; }
  .cat2-fact b { font-weight: 600; }
  .cat2-fact .k { color: var(--td-text2, #58606E); }
  .cat2-fact-dot { width: 8px; height: 8px; border-radius: 50%; flex: none;
    background: var(--td-sage); }
  .cat2-fact.warn .cat2-fact-dot { background: var(--td-amber); }
  .cat2-fact-sep { width: 1px; height: 15px; background: var(--td-border, #E4E0D5); flex: none; }
  .cat2-status-end { margin-left: auto; display: flex; align-items: center; gap: 8px; }
  .cat2-save { font-size: 12.5px; color: var(--td-text2, #58606E); white-space: nowrap; }
  .cat2-save.wait { color: var(--td-amber-ink, #8A6425); }

  /* buttons — secondary by default, ghost variant, quiet destructive */
  .cat2-btn { appearance: none; cursor: pointer; white-space: nowrap;
    display: inline-flex; align-items: center; justify-content: center; gap: 7px;
    font: 500 13.5px var(--f-sans); color: var(--td-text, #111A2B);
    background: var(--td-surface, #fff); border: 1px solid var(--td-border, #E4E0D5);
    border-radius: 8px; padding: 8px 14px;
    transition: border-color .2s cubic-bezier(.16,1,.3,1), color .2s cubic-bezier(.16,1,.3,1),
                background .2s cubic-bezier(.16,1,.3,1); }
  .cat2-btn:hover { border-color: var(--td-text2, #58606E); }
  .cat2-btn svg { width: 15px; height: 15px; flex: none; }
  .cat2-btn-ghost { background: none; border-color: transparent; color: var(--td-text2, #58606E); }
  .cat2-btn-ghost:hover { background: var(--td-raised, #F6F3EC); color: var(--td-text, #111A2B); border-color: transparent; }
  .cat2-btn-wide { width: 100%; }
  .cat2-btn-danger { background: none; border-color: transparent; color: var(--td-text2, #58606E); }
  .cat2-btn-danger:hover { background: var(--td-brick-tint, #F6E7E3); color: var(--td-brick-ink, #B4533E); border-color: transparent; }

  /* pills — a category the shop hides, or one it shows */
  .cat2-pill { font: 500 12px var(--f-sans); padding: 2px 10px; border-radius: 999px;
    display: inline-flex; align-items: center; gap: 6px; white-space: nowrap; }
  .cat2-pill-hidden { background: var(--td-amber-tint, #F7EEDE); color: var(--td-amber-ink, #8A6425); }
  .cat2-pill-live { background: var(--td-sage-tint, #EDF1EC); color: var(--td-sage-ink, #4A6249); }
  .cat2-pill-new { background: var(--td-raised, #F6F3EC); color: var(--td-text2, #58606E); }

  /* grips + row actions — invisible until the row is hovered */
  .cat2-grip { display: inline-flex; padding: 4px; border-radius: 6px; flex: none;
    color: var(--td-text2, #58606E); opacity: 0; cursor: grab; user-select: none;
    transition: opacity .15s cubic-bezier(.16,1,.3,1), background .15s cubic-bezier(.16,1,.3,1); }
  .cat2-grip svg { width: 13px; height: 13px; }
  .cat2-grip:hover { background: var(--td-raised, #F6F3EC); }
  .cat2-grip:active { cursor: grabbing; }
  .cat2-acts { display: inline-flex; align-items: center; gap: 2px; opacity: 0; flex: none;
    transition: opacity .15s cubic-bezier(.16,1,.3,1); }
  .cat2-icon { appearance: none; background: none; border: 0; cursor: pointer;
    display: inline-flex; padding: 6px; border-radius: 6px; color: var(--td-text2, #58606E);
    transition: background .15s cubic-bezier(.16,1,.3,1), color .15s cubic-bezier(.16,1,.3,1); }
  .cat2-icon svg { width: 15px; height: 15px; }
  .cat2-icon:hover { background: var(--td-raised, #F6F3EC); color: var(--td-text, #111A2B); }
  .cat2-icon.danger { color: var(--td-brick-ink, #B4533E); }
  .cat2-icon.danger:hover { background: var(--td-brick-tint, #F6E7E3); color: var(--td-brick-ink, #B4533E); }
  .cat2-row:hover .cat2-grip,
  .cat2-row:hover .cat2-acts,
  .cat2-row:hover .cat2-pen-inline,
  .cat2-row:focus-within .cat2-grip,
  .cat2-row:focus-within .cat2-acts,
  .cat2-row:focus-within .cat2-pen-inline { opacity: 1; }

  /* rename pencil sitting right next to the name it edits, not bundled with
     delete at the far end of the row; reveals with the rest of the row's
     actions rather than only when the name itself is hovered */
  .cat2-pen-inline { opacity: 0; padding: 3px; vertical-align: middle; }

  /* drag cues: one insertion line on the edge the row will land against */
  .cat2-dragging { opacity: .45; }
  .cat2-drop-before { box-shadow: 0 -2px 0 var(--td-text, #111A2B) inset; }
  .cat2-drop-after  { box-shadow: 0  2px 0 var(--td-text, #111A2B) inset; }

  /* shelf layout: the shop's rail, then the pane for the one category picked */
  .cat2-shelf { display: grid; grid-template-columns: 280px 1fr; gap: 20px; align-items: start; }

  .cat2-rail { background: var(--td-surface, #fff); border: 1px solid var(--td-border, #E4E0D5);
    border-radius: 14px; overflow: hidden; }
  .cat2-railcap { font: 600 11px var(--f-sans); letter-spacing: .08em; text-transform: uppercase;
    color: var(--td-text2, #58606E); padding: 16px 18px 8px; }
  .cat2-railrow { appearance: none; width: 100%; text-align: left; cursor: pointer;
    display: flex; align-items: center; gap: 8px; background: none; border: 0;
    border-left: 3px solid transparent; padding: 10px 14px 10px 8px;
    font-family: var(--f-sans); color: var(--td-text, #111A2B);
    transition: background .15s cubic-bezier(.16,1,.3,1); }
  .cat2-railrow:hover { background: var(--td-raised, #F6F3EC); }
  .cat2-railrow.on { background: var(--td-raised, #F6F3EC); border-left-color: var(--td-text, #111A2B); }
  .cat2-railrow .nm { font-size: 14.5px; font-weight: 500; flex: 1; min-width: 0;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .cat2-railrow .n { font-size: 12.5px; color: var(--td-text2, #58606E);
    font-variant-numeric: tabular-nums; font-feature-settings: "tnum"; }
  .cat2-railrow .cat2-fact-dot { width: 6px; height: 6px; background: var(--td-amber); }
  .cat2-railfoot { padding: 10px; border-top: 1px solid var(--td-border, #E4E0D5); }
  .cat2-railhint { font: 400 12px var(--f-sans); color: var(--td-text2, #58606E);
    line-height: 1.5; margin: 0; padding: 10px 18px 14px; }

  .cat2-pane { background: var(--td-surface, #fff); border: 1px solid var(--td-border, #E4E0D5);
    border-radius: 14px; padding: 24px 26px; min-height: 420px; font-family: var(--f-sans); }
  .cat2-panehead { display: flex; align-items: flex-start; gap: 14px; }
  .cat2-panehead h3 { margin: 0 0 0 -8px; font: 600 19px var(--f-sans); letter-spacing: -.01em;
    color: var(--td-text, #111A2B); cursor: text; border-radius: 6px; padding: 2px 8px; }
  .cat2-panehead h3:hover { background: var(--td-raised, #F6F3EC); }
  .cat2-panemeta { display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
    font-size: 13px; color: var(--td-text2, #58606E); margin-top: 3px;
    font-variant-numeric: tabular-nums; font-feature-settings: "tnum"; }
  .cat2-panehead .end { margin-left: auto; display: flex; align-items: center; gap: 6px; }
  .cat2-sec { font: 600 11px var(--f-sans); letter-spacing: .08em; text-transform: uppercase;
    color: var(--td-text2, #58606E); margin: 26px 0 6px; }

  .cat2-sub { display: flex; align-items: center; gap: 8px; padding: 8px 0;
    border-bottom: 1px solid var(--td-border, #E4E0D5); }
  .cat2-sub:last-of-type { border-bottom: 0; }
  .cat2-subname { font-size: 14.5px; color: var(--td-text, #111A2B); cursor: text;
    border-radius: 6px; padding: 1px 6px; margin-left: -6px; }
  .cat2-subname:hover { background: var(--td-raised, #F6F3EC); }
  .cat2-sub .n { margin-left: auto; font-size: 13px; color: var(--td-text2, #58606E);
    white-space: nowrap; font-variant-numeric: tabular-nums; font-feature-settings: "tnum"; }
  .cat2-empty { font-size: 13px; color: var(--td-text2, #58606E); padding: 10px 0; }

  /* inline rename card — the only place Dusk appears on this page */
  .cat2-rename { background: var(--td-raised, #F6F3EC); border: 1px solid var(--td-border, #E4E0D5);
    border-radius: 10px; padding: 16px; margin: 8px 0; }
  .cat2-rename-fields { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
  .cat2-rename label { display: block; font: 500 12px var(--f-sans);
    color: var(--td-text, #111A2B); margin-bottom: 4px; }
  .cat2-rename input { width: 100%; font: 400 14px var(--f-sans); color: var(--td-text, #111A2B);
    background: var(--td-surface, #fff); border: 1px solid var(--td-border, #E4E0D5);
    border-radius: 8px; padding: 9px 12px;
    transition: border-color .2s cubic-bezier(.16,1,.3,1); }
  .cat2-rename input:focus { outline: none; border-color: var(--td-dusk, #6E67AF); }
  .cat2-rename-foot { display: flex; align-items: center; gap: 10px; margin-top: 12px; flex-wrap: wrap; }
  .cat2-retag { margin-left: auto; display: inline-flex; align-items: center; gap: 8px;
    font-size: 12.5px; color: var(--td-text2, #58606E); }
  .cat2-retag-dot { width: 7px; height: 7px; border-radius: 50%; flex: none;
    background: var(--td-dusk, #6E67AF); animation: cat2-breathe 2.4s cubic-bezier(.16,1,.3,1) infinite; }
  @keyframes cat2-breathe { 0%, 100% { opacity: 1; } 50% { opacity: .45; } }

  /* products standing on this shelf */
  .cat2-prod { display: flex; align-items: center; gap: 12px; padding: 8px 0;
    border-bottom: 1px solid var(--td-border, #E4E0D5); }
  .cat2-prod:last-of-type { border-bottom: 0; }
  .cat2-thumb { width: 36px; height: 36px; border-radius: 8px; flex: none; object-fit: cover;
    background: var(--td-raised, #F6F3EC); border: 1px solid var(--td-border, #E4E0D5);
    display: flex; align-items: center; justify-content: center; color: var(--td-text2, #58606E); }
  .cat2-thumb svg { width: 16px; height: 16px; opacity: .5; }
  .cat2-prod .pn { flex: 1; min-width: 0; font-size: 14px; color: var(--td-text, #111A2B);
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .cat2-prod .psub { font-size: 12.5px; color: var(--td-text2, #58606E); white-space: nowrap; }
  /* background-COLOR only, never the `background` shorthand: the global
     `select` rule paints the chevron via background-image with no-repeat +
     right-position, and `:root[data-theme="light"] select` re-sets that image
     at higher specificity. A shorthand here would reset repeat/position and
     tile the chevron across the control. Leave room for it on the right. */
  .cat2-prod select { font: 400 13px var(--f-sans); color: var(--td-text, #111A2B);
    background-color: var(--td-surface, #fff); border: 1px solid var(--td-border, #E4E0D5);
    border-radius: 8px; padding: 6px 28px 6px 10px; cursor: pointer; max-width: 200px; }
  /* invisible trigger row: the IntersectionObserver reveals the next page
     when this scrolls into view, so the list just keeps growing */
  .cat2-more { height: 1px; }

  @media (max-width: 880px) {
    .cat2-shelf { grid-template-columns: 1fr; }
    .cat2-rename-fields { grid-template-columns: 1fr; }
    .cat2-grip, .cat2-acts, .cat2-pen-inline { opacity: 1; }
  }

  /* ═══════════════════════════════════════════════════════════════════════
     PURCHASING — the buying department, folded into Products 2026-07-11 as the
     #prod-view-purchasing sub-view. Rebuilt in the Tacet "quiet ledger" look
     (locked mockup Option A). Everything is a .pu2-* class scoped under the
     pane so it never leaks into the catalogue. Colour is status only; the sole
     Dusk on any screen is the two automations (.pu2-autonote). Rides the shared
     --td-* tokens so both themes work.
     ═══════════════════════════════════════════════════════════════════════ */
  #prod-view-purchasing { font-family: var(--f-sans); color: var(--td-text); }

  /* ── FULL-CIRCLE RESET ──────────────────────────────────────────────────────
     The base admin still styles bare h1–h4 as a Full Circle label: mono font,
     UPPERCASE, letter-spaced, brass. Any heading in the pane that isn't fully
     restyled (e.g. the record-a-bill modal's <h2>) inherits that. Neutralise
     every heading to plain Tacet type so ONLY Tacet shows — then the specific
     .pu2-* rules layer size/margins on top. Covers the modal host too, which
     mounts inside the pane. */
  #prod-view-purchasing h1, #prod-view-purchasing h2,
  #prod-view-purchasing h3, #prod-view-purchasing h4 {
    font-family: var(--f-sans); font-weight: 500; color: var(--td-text);
    text-transform: none; letter-spacing: -.01em; margin: 0;
  }
  /* Tables ride the card's own surface — no leaked base header tint. */
  #prod-view-purchasing .pu2-tbl th { background: transparent; }

  /* Tacet page-head + sub-nav — the pane's own header, replacing the old
     hardcoded Full Circle .pa-pagetitle brass title in the HTML wrapper. */
  #prod-view-purchasing .pu2-pagehead { margin: 2px 0 0; }
  #prod-view-purchasing .pu2-eyebrow { font: 500 11px var(--f-sans); letter-spacing: .14em; text-transform: uppercase;
    color: var(--td-text2); margin: 0 0 6px; }
  #prod-view-purchasing .pu2-title { font: 500 24px var(--f-sans); letter-spacing: -.01em; color: var(--td-text);
    text-transform: none; margin: 0 0 5px; }
  #prod-view-purchasing .pu2-sub { font-size: 13.5px; color: var(--td-text2); max-width: 64ch; margin: 0; }
  #prod-view-purchasing .pu2-subnav { display: flex; gap: 2px; flex-wrap: wrap;
    border-bottom: 1px solid var(--td-border); margin: 20px 0 22px; }
  #prod-view-purchasing .pu2-tab { appearance: none; background: none; border: none; cursor: pointer;
    font: 400 13.5px var(--f-sans); color: var(--td-text2); padding: 9px 14px 11px; border-bottom: 2px solid transparent;
    margin-bottom: -1px; display: inline-flex; align-items: center; gap: 7px; white-space: nowrap;
    transition: color .2s cubic-bezier(.16,1,.3,1); }
  #prod-view-purchasing .pu2-tab:hover { color: var(--td-text); }
  #prod-view-purchasing .pu2-tab.on { color: var(--td-text); font-weight: 500; border-bottom-color: var(--td-text); }

  #prod-view-purchasing .pu2-num { font-variant-numeric: tabular-nums; font-feature-settings: "tnum"; }
  #prod-view-purchasing .pu2-mono { font-family: var(--f-mono, ui-monospace, "SF Mono", Menlo, Consolas, monospace); letter-spacing: .02em; font-weight: 500; }
  #prod-view-purchasing .pu2-loading { color: var(--td-text2); padding: 40px 0; text-align: center; }
  #prod-view-purchasing .pu2-lede { font-size: 13.5px; color: var(--td-text2); max-width: 64ch; margin: 0 0 4px; }
  #prod-view-purchasing .spacer { margin-left: auto; }
  #prod-view-purchasing .sub { font-size: 12.5px; color: var(--td-text2); margin-top: 2px; }

  /* icons */
  #prod-view-purchasing .pu2-ic { display: inline-flex; align-items: center; }
  #prod-view-purchasing .pu2-ic svg { width: 14px; height: 14px; display: block; }
  #prod-view-purchasing .pu2-ic.big svg { width: 26px; height: 26px; }

  /* head row (suppliers) */
  #prod-view-purchasing .pu2-head { display: flex; align-items: flex-start; gap: 12px; flex-wrap: wrap; margin-bottom: 4px; }

  /* stat line — the four KPI cards, collapsed to one line of clickable numbers */
  #prod-view-purchasing .pu2-statline { display: flex; align-items: baseline; gap: 22px; flex-wrap: wrap; margin: 18px 0 4px; }
  #prod-view-purchasing .pu2-stat { appearance: none; background: none; border: none; padding: 0;
    font: 400 13.5px var(--f-sans); color: var(--td-text2); display: inline-flex; align-items: baseline; gap: 6px; }
  #prod-view-purchasing .pu2-stat b { font-size: 19px; font-weight: 600; color: var(--td-text); }
  #prod-view-purchasing .pu2-stat.link { cursor: pointer; }
  #prod-view-purchasing .pu2-stat.link:hover { color: var(--td-text); }
  #prod-view-purchasing .pu2-stat.link.on b { text-decoration: underline; text-underline-offset: 4px; }
  #prod-view-purchasing .pu2-stat.warn b { color: var(--td-amber-ink); }

  /* toolbar: segbar + search + action */
  #prod-view-purchasing .pu2-toolbar { display: flex; align-items: center; gap: 12px; margin: 20px 0 16px; flex-wrap: wrap; }
  #prod-view-purchasing .pu2-segbar { display: inline-flex; border: 1px solid var(--td-border); border-radius: 999px; overflow: hidden; background: var(--td-surface, #fff); }
  #prod-view-purchasing .pu2-seg { padding: 6px 15px; font-size: 13px; color: var(--td-text2); cursor: pointer; white-space: nowrap;
    transition: background .2s cubic-bezier(.16,1,.3,1); }
  #prod-view-purchasing .pu2-seg.on { background: var(--td-text); color: var(--td-paper); }
  #prod-view-purchasing .pu2-seg .n { opacity: .55; margin-left: 5px; font-size: 12px; }
  #prod-view-purchasing .pu2-search { display: flex; align-items: center; gap: 8px; margin-left: auto;
    border: 1px solid var(--td-border); border-radius: 8px; background: var(--td-surface, #fff);
    padding: 7px 12px; min-width: 240px; color: var(--td-text2); }
  #prod-view-purchasing .pu2-search.full { margin-left: 0; width: 100%; }
  #prod-view-purchasing .pu2-search input { border: none; outline: none; background: none; width: 100%;
    font: 400 13.5px var(--f-sans); color: var(--td-text); padding: 0; }
  #prod-view-purchasing .pu2-search input::placeholder { color: var(--td-text2); opacity: .7; }
  #prod-view-purchasing .pu2-search:focus-within { border-color: var(--td-dusk); }

  /* buttons */
  #prod-view-purchasing .pu2-btn { display: inline-flex; align-items: center; gap: 7px;
    font: 500 13.5px var(--f-sans); color: var(--td-text); background: transparent;
    border: 1px solid var(--td-border); border-radius: 8px; padding: 8px 15px; cursor: pointer; white-space: nowrap;
    transition: border-color .2s cubic-bezier(.16,1,.3,1), background .2s cubic-bezier(.16,1,.3,1); }
  #prod-view-purchasing .pu2-btn:hover { border-color: var(--td-text2); }
  #prod-view-purchasing .pu2-btn.primary { background: var(--td-text); border-color: var(--td-text); color: var(--td-paper); }
  #prod-view-purchasing .pu2-btn.primary:hover { background: #232f47; }
  :root[data-theme="light"] #prod-view-purchasing .pu2-btn.primary:hover { background: #232f47; }
  #prod-view-purchasing .pu2-btn.ghost { border-color: transparent; color: var(--td-text2); }
  #prod-view-purchasing .pu2-btn.ghost:hover { color: var(--td-text); background: var(--td-raised); }
  #prod-view-purchasing .pu2-btn.danger { border-color: transparent; color: var(--td-brick); }
  #prod-view-purchasing .pu2-btn.danger:hover { background: var(--td-brick-tint); }
  #prod-view-purchasing .pu2-btn.sm { padding: 5px 11px; font-size: 13px; }
  #prod-view-purchasing .pu2-btn:disabled { opacity: .45; cursor: not-allowed; }
  #prod-view-purchasing .pu2-link { appearance: none; background: none; border: none; cursor: pointer;
    color: var(--td-text2); font: 400 13px var(--f-sans); text-decoration: underline; text-underline-offset: 3px; margin-left: auto; }
  #prod-view-purchasing .pu2-link:hover { color: var(--td-text); }

  /* pills */
  #prod-view-purchasing .pu2-pill { display: inline-flex; align-items: center; gap: 6px; font-size: 12px; font-weight: 500;
    line-height: 1; padding: 4px 10px; border-radius: 999px; white-space: nowrap; }
  #prod-view-purchasing .pu2-pill .dot { width: 6px; height: 6px; border-radius: 50%; background: currentColor; }
  #prod-view-purchasing .pu2-pill.sage  { background: var(--td-sage-tint);  color: var(--td-sage-ink); }
  #prod-view-purchasing .pu2-pill.grey  { background: var(--td-raised);      color: var(--td-text2); }
  #prod-view-purchasing .pu2-pill.amber { background: var(--td-amber-tint); color: var(--td-amber-ink); }
  #prod-view-purchasing .pu2-pill.brick { background: var(--td-brick-tint); color: var(--td-brick-ink); }
  #prod-view-purchasing .pu2-pill.dusk  { background: var(--td-dusk-tint);  color: var(--td-dusk); }

  /* cards + table */
  #prod-view-purchasing .pu2-card { background: var(--td-surface, #fff); border: 1px solid var(--td-border);
    border-radius: 14px; box-shadow: 0 1px 2px rgba(17,26,43,.05), 0 4px 16px rgba(17,26,43,.04); }
  #prod-view-purchasing .pu2-tbl { width: 100%; border-collapse: collapse; font-size: 14px; }
  #prod-view-purchasing .pu2-tbl th { text-align: left; font: 500 11px var(--f-sans); letter-spacing: .08em;
    text-transform: uppercase; color: var(--td-text2); padding: 14px 16px 10px; border-bottom: 1px solid var(--td-border); }
  #prod-view-purchasing .pu2-tbl td { padding: 13px 16px; border-bottom: 1px solid var(--td-border); vertical-align: middle; }
  #prod-view-purchasing .pu2-tbl tr:last-child td { border-bottom: none; }
  #prod-view-purchasing .pu2-tbl tbody tr[onclick] { cursor: pointer; transition: background .2s cubic-bezier(.16,1,.3,1); }
  #prod-view-purchasing .pu2-tbl tbody tr[onclick]:hover { background: var(--td-raised); }
  #prod-view-purchasing .pu2-tbl .right { text-align: right; }
  #prod-view-purchasing .pu2-kicker { font: 500 11px var(--f-sans); letter-spacing: .1em; text-transform: uppercase;
    color: var(--td-text2); margin: 30px 0 12px; }

  /* empty states */
  #prod-view-purchasing .pu2-emptyline { text-align: center; color: var(--td-text2); font-size: 13.5px; padding: 22px 8px; }
  #prod-view-purchasing .pu2-empty { text-align: center; padding: 54px 24px 58px; }
  #prod-view-purchasing .pu2-empty-ico { color: var(--td-text2); margin: 0 auto 6px; }
  #prod-view-purchasing .pu2-empty-ico svg { width: 34px; height: 34px; margin: 0 auto; }
  #prod-view-purchasing .pu2-empty h3 { font-size: 17px; font-weight: 500; margin: 8px 0 6px; }
  #prod-view-purchasing .pu2-empty p { font-size: 13.5px; color: var(--td-text2); max-width: 46ch; margin: 0 auto 20px; }
  #prod-view-purchasing .pu2-empty-steps { display: flex; justify-content: center; gap: 28px; margin-top: 30px; flex-wrap: wrap; }
  #prod-view-purchasing .pu2-empty-step { max-width: 190px; font-size: 12.5px; color: var(--td-text2); line-height: 1.5; }
  #prod-view-purchasing .pu2-empty-step b { display: block; color: var(--td-text); font-weight: 500; font-size: 13px; margin: 8px 0 2px; }
  #prod-view-purchasing .pu2-empty-step .ico { width: 38px; height: 38px; margin: 0 auto; border-radius: 50%;
    background: var(--td-raised); display: flex; align-items: center; justify-content: center; color: var(--td-text2); }
  #prod-view-purchasing .pu2-empty-step .ico svg { width: 17px; height: 17px; }

  /* wizard / detail header */
  #prod-view-purchasing .pu2-wizhead { display: flex; align-items: center; gap: 13px; margin-bottom: 22px; flex-wrap: wrap; }
  #prod-view-purchasing .pu2-wizhead h1 { font-size: 21px; font-weight: 500; letter-spacing: -.01em; }
  #prod-view-purchasing .pu2-wsteps { display: flex; align-items: center; gap: 9px; margin-left: auto; font-size: 13px; color: var(--td-text2); flex-wrap: wrap; }
  #prod-view-purchasing .pu2-wsteps .st { display: inline-flex; align-items: center; gap: 7px; }
  #prod-view-purchasing .pu2-wsteps .n { width: 22px; height: 22px; border-radius: 50%; border: 1px solid var(--td-border);
    display: inline-flex; align-items: center; justify-content: center; font-size: 12px; }
  #prod-view-purchasing .pu2-wsteps .n svg { width: 11px; height: 11px; }
  #prod-view-purchasing .pu2-wsteps .st.done .n { background: var(--td-text); border-color: var(--td-text); color: var(--td-paper); }
  #prod-view-purchasing .pu2-wsteps .st.on { color: var(--td-text); font-weight: 500; }
  #prod-view-purchasing .pu2-wsteps .st.on .n { border-color: var(--td-text); }
  #prod-view-purchasing .pu2-wsteps .dash { width: 20px; height: 1px; background: var(--td-border); }

  /* wizard cards + forms */
  #prod-view-purchasing .pu2-wizcard { padding: 24px 28px; max-width: 880px; }
  #prod-view-purchasing .pu2-wizcard + .pu2-wizcard, #prod-view-purchasing .pu2-card + .pu2-card { margin-top: 16px; }
  #prod-view-purchasing .pu2-recap { display: flex; gap: 24px; flex-wrap: wrap; align-items: center; padding: 16px 28px; font-size: 13.5px; color: var(--td-text2); }
  #prod-view-purchasing .pu2-recap b { color: var(--td-text); font-weight: 500; }
  #prod-view-purchasing .pu2-fgrid { display: grid; grid-template-columns: 1fr 1fr; gap: 18px 24px; }
  #prod-view-purchasing .pu2-f { display: flex; flex-direction: column; font-size: 13px; color: var(--td-text2); min-width: 0; }
  #prod-view-purchasing .pu2-f .lbl { font-weight: 500; color: var(--td-text); font-size: 13px; margin-bottom: 6px; }
  #prod-view-purchasing .pu2-f .hint { font-size: 12px; color: var(--td-text2); margin-top: 5px; }
  #prod-view-purchasing .pu2-input { border: 1px solid var(--td-border); border-radius: 8px; background: var(--td-surface, #fff);
    padding: 9px 12px; font: 400 14px var(--f-sans); color: var(--td-text); width: 100%; }
  #prod-view-purchasing textarea.pu2-input { resize: vertical; }
  #prod-view-purchasing .pu2-input:focus { outline: none; border-color: var(--td-dusk); }
  #prod-view-purchasing select.pu2-input { color-scheme: dark; }
  :root[data-theme="light"] #prod-view-purchasing select.pu2-input,
  :root[data-theme="light"] #prod-view-purchasing input[type=date].pu2-input { color-scheme: light; }
  #prod-view-purchasing .pu2-wizfoot { display: flex; align-items: center; gap: 10px; margin-top: 26px; }
  #prod-view-purchasing .pu2-formcard { padding: 26px 28px; max-width: 760px; }
  #prod-view-purchasing .pu2-fgroup { margin-bottom: 24px; }
  #prod-view-purchasing .pu2-fgroup:last-of-type { margin-bottom: 0; }
  #prod-view-purchasing .pu2-fgroup .pu2-kicker { margin: 0 0 12px; }

  /* product picker */
  #prod-view-purchasing .pu2-picker-list { position: absolute; z-index: 30; left: 0; right: 0; top: 100%;
    max-height: 280px; overflow: auto; background: var(--td-surface, #fff); border: 1px solid var(--td-border);
    border-radius: 10px; box-shadow: 0 8px 24px rgba(17,26,43,.14); margin-top: 4px; }
  #prod-view-purchasing .pu2-pick { padding: 9px 13px; cursor: pointer; font-size: 13.5px; border-bottom: 1px solid var(--td-border); }
  #prod-view-purchasing .pu2-pick:last-child { border-bottom: none; }
  #prod-view-purchasing .pu2-pick:hover { background: var(--td-raised); }
  #prod-view-purchasing .pu2-pick.muted { color: var(--td-text2); cursor: default; }
  #prod-view-purchasing .pu2-pick .sub { display: inline; margin-left: 6px; }

  /* product → supplier price popover (who sells it, what we paid last time) */
  #prod-view-purchasing .pu2-suppop { margin-top: 14px; }
  #prod-view-purchasing .pu2-pop { border: 1px solid var(--td-border); border-radius: 14px; background: var(--td-surface, #fff);
    box-shadow: 0 8px 24px rgba(17,26,43,.14); overflow: hidden; max-width: 660px; }
  #prod-view-purchasing .pu2-pop .pop-head { display: flex; align-items: baseline; gap: 10px; padding: 13px 18px;
    border-bottom: 1px solid var(--td-border); }
  #prod-view-purchasing .pu2-pop .pop-head b { font-size: 14px; font-weight: 500; }
  #prod-view-purchasing .pu2-pop .pop-head .sub { margin-top: 0; }
  #prod-view-purchasing .pu2-pop .pop-head .pu2-link { margin-left: auto; }
  #prod-view-purchasing .pu2-suprow { display: grid; grid-template-columns: 1.5fr 1fr 1fr 1.1fr auto; align-items: center;
    gap: 12px; padding: 11px 18px; border-bottom: 1px solid var(--td-border); font-size: 13.5px; }
  #prod-view-purchasing .pu2-suprow:last-child { border-bottom: none; }
  #prod-view-purchasing .pu2-suprow.best { background: var(--td-sage-tint); }
  #prod-view-purchasing .pu2-suprow .sname { font-weight: 500; display: flex; align-items: center; gap: 7px; flex-wrap: wrap; }
  #prod-view-purchasing .pu2-suprow .lab { font-size: 10.5px; text-transform: uppercase; letter-spacing: .06em; color: var(--td-text2); }
  #prod-view-purchasing .pu2-suprow .val { font-size: 13.5px; color: var(--td-text); margin-top: 2px; }
  #prod-view-purchasing .pu2-suprow .val.cheap { color: var(--td-sage-ink); }
  #prod-view-purchasing .pu2-suprow.manual { color: var(--td-text2); }
  #prod-view-purchasing .pu2-tagstar { display: inline-flex; align-items: center; gap: 4px; font-size: 11.5px; font-weight: 500;
    color: var(--td-amber-ink); }
  #prod-view-purchasing .pu2-tagstar svg { width: 11px; height: 11px; }

  /* per-line supplier chip */
  #prod-view-purchasing .pu2-supline { margin-top: 6px; }
  #prod-view-purchasing .pu2-supchip { display: inline-flex; align-items: center; gap: 6px; font-size: 12.5px; color: var(--td-text);
    border: 1px solid var(--td-border); border-radius: 999px; padding: 3px 10px; background: var(--td-surface, #fff);
    cursor: pointer; white-space: nowrap; transition: border-color .2s cubic-bezier(.16,1,.3,1); }
  #prod-view-purchasing .pu2-supchip:hover { border-color: var(--td-text2); }
  #prod-view-purchasing .pu2-supchip .swap { color: var(--td-text2); }
  #prod-view-purchasing .pu2-lasthint.pricewarn { color: var(--td-amber-ink); }

  /* Bills — KPI tiles + outstanding-by-supplier + recorded ledger */
  #prod-view-purchasing .pu2-metricrow { display: grid; grid-template-columns: repeat(4, 1fr); gap: 14px; margin: 20px 0 8px; }
  #prod-view-purchasing .pu2-metric { padding: 15px 18px; }
  #prod-view-purchasing .pu2-metric .lab { font-size: 11px; text-transform: uppercase; letter-spacing: .07em; color: var(--td-text2); }
  #prod-view-purchasing .pu2-metric .big { font-size: 22px; font-weight: 500; margin-top: 6px; }
  #prod-view-purchasing .pu2-metric .big.bad { color: var(--td-brick-ink); }
  #prod-view-purchasing .pu2-metric .foot { font-size: 12px; color: var(--td-text2); margin-top: 3px; }
  #prod-view-purchasing .pu2-billcols { display: grid; grid-template-columns: 1fr 1fr; gap: 22px; margin-top: 8px; align-items: start; }
  #prod-view-purchasing .pu2-osrow { display: flex; align-items: center; gap: 10px; padding: 13px 16px; border-bottom: 1px solid var(--td-border); }
  #prod-view-purchasing .pu2-osrow:last-child { border-bottom: none; }
  #prod-view-purchasing .pu2-osrow .pu2-btn { margin-left: auto; }
  #prod-view-purchasing .pu2-mut { color: var(--td-text2); }

  /* record-a-bill modal (mounted inside the pane, tokens at :root) */
  #prod-view-purchasing .pu2-scrim { position: fixed; inset: 0; background: rgba(17,26,43,.42); z-index: 60; }
  #prod-view-purchasing .pu2-modal { position: fixed; z-index: 61; top: 50%; left: 50%; transform: translate(-50%, -50%);
    width: min(680px, calc(100vw - 32px)); max-height: calc(100vh - 48px); overflow: auto;
    background: var(--td-surface, #fff); border: 1px solid var(--td-border); border-radius: 14px;
    box-shadow: 0 24px 64px rgba(17,26,43,.28); }
  #prod-view-purchasing .pu2-modal-head { display: flex; align-items: flex-start; gap: 12px; padding: 18px 22px 14px; border-bottom: 1px solid var(--td-border); }
  #prod-view-purchasing .pu2-modal-head h2 { font-size: 17px; font-weight: 500; }
  #prod-view-purchasing .pu2-modal-body { padding: 18px 22px; }
  #prod-view-purchasing .pu2-modal-foot { display: flex; align-items: center; gap: 10px; padding: 14px 22px; border-top: 1px solid var(--td-border); }
  #prod-view-purchasing .pu2-fgrid.three { grid-template-columns: 1fr 1fr 1fr; }
  #prod-view-purchasing .pu2-alloc-head { display: flex; align-items: center; margin: 20px 0 8px; }
  #prod-view-purchasing .pu2-alloclist { border: 1px solid var(--td-border); border-radius: 10px; overflow: hidden; }
  #prod-view-purchasing .pu2-pogrp { font-size: 11px; text-transform: uppercase; letter-spacing: .06em; color: var(--td-text2);
    padding: 10px 14px 4px; background: var(--td-raised); }
  #prod-view-purchasing .pu2-allocrow { display: grid; grid-template-columns: 26px 1fr auto 110px; align-items: center; gap: 12px;
    padding: 11px 14px; border-top: 1px solid var(--td-border); }
  #prod-view-purchasing .pu2-allocrow:first-child { border-top: none; }
  #prod-view-purchasing .pu2-allocrow .pu2-input.pu2-allocin { width: 110px; padding: 6px 10px; text-align: right; }
  #prod-view-purchasing .pu2-chk { width: 18px; height: 18px; border: 1.5px solid var(--td-border); border-radius: 5px;
    display: inline-flex; align-items: center; justify-content: center; color: var(--td-paper); cursor: pointer; }
  #prod-view-purchasing .pu2-chk svg { width: 11px; height: 11px; }
  #prod-view-purchasing .pu2-chk.on { background: var(--td-text); border-color: var(--td-text); }
  @media (max-width: 820px) {
    #prod-view-purchasing .pu2-metricrow { grid-template-columns: 1fr 1fr; }
    #prod-view-purchasing .pu2-billcols { grid-template-columns: 1fr; }
    #prod-view-purchasing .pu2-fgrid.three { grid-template-columns: 1fr; }
  }

  /* Reorder — low-stock cards grouped by preferred supplier + store */
  #prod-view-purchasing .pu2-reo { display: grid; grid-template-columns: repeat(auto-fill, minmax(340px, 1fr)); gap: 16px; margin-top: 6px; }
  #prod-view-purchasing .pu2-reocard { padding: 18px 20px 16px; }
  #prod-view-purchasing .pu2-reocard .top { display: flex; align-items: center; gap: 10px; }
  #prod-view-purchasing .pu2-reocard .sup { font-size: 15px; font-weight: 500; }
  #prod-view-purchasing .pu2-reocard .sub { margin: 2px 0 0; }
  #prod-view-purchasing .pu2-reocard .lines2 { margin: 12px 0 4px; }
  #prod-view-purchasing .pu2-reocard .l2 { display: flex; align-items: baseline; gap: 8px; font-size: 13px; padding: 6px 0;
    border-bottom: 1px solid var(--td-border); }
  #prod-view-purchasing .pu2-reocard .l2:last-child { border-bottom: none; }
  #prod-view-purchasing .pu2-reocard .l2 .cover { margin-left: auto; color: var(--td-text2); font-size: 12px; white-space: nowrap; }
  #prod-view-purchasing .pu2-reocard .foot { display: flex; align-items: center; gap: 8px; margin-top: 12px; padding-top: 12px;
    border-top: 1px solid var(--td-border); }
  #prod-view-purchasing .pu2-reocard .foot .amt { font-weight: 500; }
  #prod-view-purchasing .pu2-reocard .foot .pu2-btn { margin-left: auto; }

  /* PO lifecycle stepper — Draft → Sent → Part received → Received */
  #prod-view-purchasing .pu2-life { display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
    font-size: 12.5px; color: var(--td-text2); margin: 4px 0 18px; }
  #prod-view-purchasing .pu2-life .s { display: inline-flex; align-items: center; gap: 6px; }
  #prod-view-purchasing .pu2-life .s .d { width: 8px; height: 8px; border-radius: 50%; background: var(--td-border); }
  #prod-view-purchasing .pu2-life .s.done { color: var(--td-text); }
  #prod-view-purchasing .pu2-life .s.done .d { background: var(--td-text); }
  #prod-view-purchasing .pu2-life .s.now { color: var(--td-text); font-weight: 500; }
  #prod-view-purchasing .pu2-life .s.now .d { background: var(--td-amber); }
  #prod-view-purchasing .pu2-life .conn { width: 20px; height: 1px; background: var(--td-border); }

  /* Landed cost breakdown */
  #prod-view-purchasing .pu2-landed .pu2-lc-grid { max-width: 360px; margin-top: 10px; }
  #prod-view-purchasing .pu2-landed .lc-row { display: flex; justify-content: space-between; align-items: center;
    padding: 7px 0; border-bottom: 1px dashed var(--td-border); font-size: 13.5px; }
  #prod-view-purchasing .pu2-landed .lc-row:last-of-type { border-bottom: none; }
  #prod-view-purchasing .pu2-landed .lc-row .g { color: var(--td-text2); }
  #prod-view-purchasing .pu2-landed .lc-row.tot { font-weight: 500; border-top: 1px solid var(--td-border);
    border-bottom: none; margin-top: 4px; padding-top: 10px; }

  /* Price-history sparkline */
  #prod-view-purchasing .pu2-spark { width: 220px; height: 46px; display: block; }
  #prod-view-purchasing .pu2-spark .line { fill: none; stroke: var(--td-text); stroke-width: 1.6; }
  #prod-view-purchasing .pu2-spark .area { fill: var(--td-raised); stroke: none; }
  #prod-view-purchasing .pu2-spark .dot { fill: var(--td-text); }

  /* low-stock suggestions */
  #prod-view-purchasing .pu2-sugrow { display: flex; align-items: center; gap: 12px; padding: 10px 14px;
    border: 1px dashed var(--td-border); border-radius: 10px; margin-bottom: 8px; font-size: 13.5px; }
  #prod-view-purchasing .pu2-sugrow .low { color: var(--td-amber-ink); font-size: 12.5px; }
  #prod-view-purchasing .pu2-sugrow .pu2-btn { margin-left: auto; }

  /* line editor */
  #prod-view-purchasing .pu2-lines td { vertical-align: middle; }
  #prod-view-purchasing .pu2-noline td { text-align: center; color: var(--td-text2); padding: 18px; font-size: 13.5px; }
  #prod-view-purchasing .pu2-qty { display: inline-flex; align-items: center; border: 1px solid var(--td-border);
    border-radius: 8px; overflow: hidden; background: var(--td-surface, #fff); }
  #prod-view-purchasing .pu2-qty .pm { appearance: none; border: none; background: none; color: var(--td-text2);
    padding: 6px 11px; font-size: 15px; cursor: pointer; }
  #prod-view-purchasing .pu2-qty .pm:hover { background: var(--td-raised); color: var(--td-text); }
  #prod-view-purchasing .pu2-qty input { width: 46px; border: none; outline: none; background: none; text-align: center;
    font: 400 14px var(--f-sans); color: var(--td-text); padding: 6px 0; -moz-appearance: textfield; }
  #prod-view-purchasing .pu2-qty input::-webkit-outer-spin-button,
  #prod-view-purchasing .pu2-qty input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
  #prod-view-purchasing .pu2-qty.warn { border-color: var(--td-amber); }
  #prod-view-purchasing .pu2-costwrap { display: inline-flex; align-items: center; gap: 3px; border: 1px solid var(--td-border);
    border-radius: 8px; padding: 5px 10px; background: var(--td-surface, #fff); color: var(--td-text2); }
  #prod-view-purchasing .pu2-cost { width: 68px; border: none; outline: none; background: none; text-align: right;
    font: 400 14px var(--f-sans); color: var(--td-text); }
  #prod-view-purchasing .pu2-costwrap:focus-within { border-color: var(--td-dusk); }
  #prod-view-purchasing .pu2-lasthint { font-size: 11.5px; color: var(--td-text2); margin-top: 3px; }
  #prod-view-purchasing .pu2-totrow td { background: var(--td-raised); font-weight: 500; }

  /* review / automation notes */
  #prod-view-purchasing .pu2-reviewline { font-size: 14px; margin-bottom: 6px; }
  #prod-view-purchasing .pu2-warnline { font-size: 13px; color: var(--td-amber-ink); margin: 10px 0 0; }
  #prod-view-purchasing .pu2-autonote { display: flex; gap: 10px; align-items: flex-start; font-size: 13px; color: var(--td-text2);
    background: var(--td-dusk-tint); border-radius: 10px; padding: 12px 16px; margin: 18px 0; max-width: 640px; }
  #prod-view-purchasing .pu2-autonote .dot { flex: none; width: 7px; height: 7px; border-radius: 50%; background: var(--td-dusk);
    margin-top: 7px; animation: pu2breathe 2.4s cubic-bezier(.16,1,.3,1) infinite; }
  #prod-view-purchasing .pu2-autonote b { color: var(--td-dusk); font-weight: 500; }
  @keyframes pu2breathe { 0%,100% { opacity: 1; } 50% { opacity: .4; } }
  @media (prefers-reduced-motion: reduce) { #prod-view-purchasing .pu2-autonote .dot { animation: none; } }

  /* receive extras */
  #prod-view-purchasing .pu2-rcvsub { font-size: 13.5px; color: var(--td-text2); margin-bottom: 18px; }
  #prod-view-purchasing .pu2-notes { font-size: 13.5px; color: var(--td-text2); margin: -8px 0 18px; }
  #prod-view-purchasing .pu2-actions { display: flex; gap: 10px; flex-wrap: wrap; align-items: center; margin-top: 18px; }
  #prod-view-purchasing .pu2-fin { margin-top: 22px; border: 1px solid var(--td-border); border-radius: 10px;
    padding: 12px 16px; max-width: 560px; font-size: 13.5px; }
  #prod-view-purchasing .pu2-fin b { font-size: 13px; }
  #prod-view-purchasing .pu2-fin .sub { margin-top: 4px; }

  /* supplier rating dots */
  #prod-view-purchasing .pu2-rate { display: inline-flex; gap: 3px; }
  #prod-view-purchasing .pu2-rate i { width: 7px; height: 7px; border-radius: 50%; background: var(--td-text); opacity: .85; }
  #prod-view-purchasing .pu2-rate i.off { background: var(--td-border); opacity: 1; }

  /* supplier detail blocks */
  #prod-view-purchasing .pu2-block { padding: 16px 20px; max-width: 640px; }
  #prod-view-purchasing .pu2-block > b { font-size: 13px; }
  #prod-view-purchasing .pu2-docs, #prod-view-purchasing .pu2-recentpos { margin-top: 8px; font-size: 13.5px; }
  #prod-view-purchasing .pu2-docrow { display: flex; gap: 8px; align-items: center; padding: 4px 0; }
  #prod-view-purchasing .pu2-docadd { display: flex; gap: 8px; align-items: center; margin-top: 10px; flex-wrap: wrap; }
  #prod-view-purchasing .pu2-docadd .pu2-input { width: auto; }
  #prod-view-purchasing .pu2-recentpo { display: flex; gap: 8px; align-items: center; padding: 6px 0; cursor: pointer; }
  #prod-view-purchasing .pu2-recentpo:hover { color: var(--td-text); }

  /* CSV import */
  #prod-view-purchasing .pu2-impstep { display: flex; gap: 18px; padding: 22px 0; border-bottom: 1px solid var(--td-border); }
  #prod-view-purchasing .pu2-impstep:last-child { border-bottom: none; }
  #prod-view-purchasing .pu2-impstep .n { flex: none; width: 30px; height: 30px; border-radius: 50%; background: var(--td-text);
    color: var(--td-paper); display: flex; align-items: center; justify-content: center; font-size: 14px; font-weight: 500; }
  #prod-view-purchasing .pu2-impstep h3 { font-size: 15px; font-weight: 500; margin-bottom: 4px; }
  #prod-view-purchasing .pu2-impstep p { font-size: 13.5px; color: var(--td-text2); max-width: 58ch; }
  #prod-view-purchasing .pu2-implist { margin: 10px 0 0; list-style: none; padding: 0; }
  #prod-view-purchasing .pu2-implist li { font-size: 13.5px; color: var(--td-text2); display: flex; gap: 9px; padding: 3px 0; }
  #prod-view-purchasing .pu2-implist li::before { content: "·"; color: var(--td-text); }
  #prod-view-purchasing .pu2-csvpeek { font-family: var(--f-mono, ui-monospace, Menlo, Consolas, monospace); font-size: 12px;
    line-height: 1.8; background: var(--td-raised); border-radius: 10px; padding: 12px 16px; margin-top: 12px;
    overflow-x: auto; white-space: nowrap; color: var(--td-text2); }
  #prod-view-purchasing .pu2-csvpeek b { color: var(--td-text); font-weight: 500; }
  #prod-view-purchasing .pu2-drop { display: block; margin-top: 14px; border: 1.5px dashed var(--td-border); border-radius: 14px;
    padding: 30px 20px; text-align: center; color: var(--td-text2); font-size: 13.5px; cursor: pointer;
    transition: border-color .2s cubic-bezier(.16,1,.3,1); }
  #prod-view-purchasing .pu2-drop:hover { border-color: var(--td-text2); }
  #prod-view-purchasing .pu2-drop .pu2-ic { justify-content: center; }
  #prod-view-purchasing .pu2-drop b { display: block; color: var(--td-text); font-weight: 500; font-size: 14px; margin: 10px 0 2px; }
  #prod-view-purchasing .pu2-prevsum { display: flex; gap: 10px; flex-wrap: wrap; margin: 14px 0; }
  #prod-view-purchasing .pu2-tbl tr.prev-ok td:first-child { box-shadow: inset 3px 0 0 var(--td-sage); }
  #prod-view-purchasing .pu2-tbl tr.prev-upd td:first-child { box-shadow: inset 3px 0 0 var(--td-amber); }
  #prod-view-purchasing .pu2-tbl tr.prev-skip td { color: var(--td-text2); }
  #prod-view-purchasing .pu2-tbl tr.prev-skip td:first-child { box-shadow: inset 3px 0 0 var(--td-brick); }

  @media (max-width: 760px) {
    #prod-view-purchasing .pu2-fgrid { grid-template-columns: 1fr; }
    #prod-view-purchasing .pu2-search { margin-left: 0; width: 100%; }
  }

  /* channel tabs */
  #act-tabs { display: flex; flex-wrap: wrap; gap: 2px;
    border-bottom: 1px solid var(--td-border, #E4E0D5); margin: 0 0 16px; }
  .act-tab { appearance: none; background: none; border: none;
    border-bottom: 2px solid transparent; margin-bottom: -1px;
    font: 400 13.5px var(--f-sans); color: var(--td-text2, #58606E);
    padding: 8px 12px 10px; cursor: pointer; white-space: nowrap;
    display: inline-flex; align-items: baseline; gap: 7px;
    transition: color .2s cubic-bezier(.16,1,.3,1); }
  .act-tab:hover { color: var(--td-text, #111A2B); }
  .act-tab.on { color: var(--td-text, #111A2B); font-weight: 500;
    border-bottom-color: var(--td-text, #111A2B); }
  .act-tab-n { font-size: 12px; opacity: .7; font-variant-numeric: tabular-nums; }
  .act-tab.quiet .act-tab-n { opacity: .35; }

  /* toolbar */
  .act-toolbar { display: flex; align-items: center; flex-wrap: wrap;
    gap: 10px; margin: 0 0 16px; }
  .act-date { font: 400 13.5px var(--f-sans); color: var(--td-text);
    background: var(--td-surface, #fff); border: 1px solid var(--td-border);
    border-radius: 8px; padding: 7px 10px; color-scheme: dark; }
  :root[data-theme="light"] .act-date { color-scheme: light; }
  .act-arrow { color: var(--td-text2); font-size: 13px; }
  .act-quick { display: inline-flex; gap: 2px; }
  .act-quick button { appearance: none; background: none; border: none;
    cursor: pointer; border-radius: 8px; font: 400 13px var(--f-sans);
    color: var(--td-text2); padding: 7px 10px;
    transition: all .2s cubic-bezier(.16,1,.3,1); }
  .act-quick button:hover { background: var(--td-raised); color: var(--td-text); }
  .act-search { display: inline-flex; align-items: center; gap: 8px;
    background: var(--td-surface, #fff); border: 1px solid var(--td-border);
    border-radius: 8px; padding: 7px 11px; min-width: 230px;
    color: var(--td-text2); }
  .act-search svg { flex: none; }
  .act-search input { border: none; outline: none; background: none;
    width: 100%; font: 400 13.5px var(--f-sans); color: var(--td-text); padding: 0; }
  .act-search input::placeholder { color: var(--td-text2); opacity: .7; }
  .act-search:focus-within { border-color: var(--td-text2); }
  .act-export { appearance: none; cursor: pointer;
    display: inline-flex; align-items: center; gap: 8px;
    font: 500 13.5px var(--f-sans); color: var(--td-text);
    background: transparent; border: 1px solid var(--td-border);
    border-radius: 8px; padding: 8px 15px;
    transition: border-color .2s cubic-bezier(.16,1,.3,1); }
  .act-export:hover { border-color: var(--td-text2); }

  /* read-first digest cards */
  .act-digest { display: grid; grid-template-columns: repeat(3, 1fr);
    gap: 14px; margin: 0 0 20px; }
  .act-dcard { background: var(--td-surface, #fff);
    border: 1px solid var(--td-border); border-radius: 14px; padding: 15px 18px; }
  .act-dcard-calm { background: var(--td-raised); border-color: transparent; }
  .act-dcard h3 { font: 500 11px var(--f-sans); letter-spacing: .08em;
    text-transform: uppercase; color: var(--td-text2); margin: 0 0 7px; }
  .act-drow { display: flex; justify-content: space-between;
    align-items: baseline; gap: 12px; padding: 4px 0;
    font-size: 13.5px; color: var(--td-text); }
  .act-drow + .act-drow { border-top: 1px dashed var(--td-border); }
  .act-dmain { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .act-dmeta { color: var(--td-text2); font-size: 12.5px; white-space: nowrap; }
  .act-dempty { color: var(--td-text2); font-size: 13px; padding: 4px 0; }
  .act-quiet-txt { opacity: .7; }

  /* scope line */
  .act-scope { font-size: 13px; color: var(--td-text2); margin: 0 0 10px; }
  .act-scope b { font-weight: 500; color: var(--td-text); }

  /* quiet ledger table */
  .act-tablewrap { overflow-x: auto; background: var(--td-surface, #fff);
    border: 1px solid var(--td-border); border-radius: 14px; }
  .act-table { width: 100%; border-collapse: collapse; }
  .act-table thead th { font: 500 11px var(--f-sans); letter-spacing: .08em;
    text-transform: uppercase; color: var(--td-text2); text-align: left;
    padding: 11px 14px; background: var(--td-raised);
    border-bottom: 1px solid var(--td-border); white-space: nowrap; }
  .act-table thead th.r { text-align: right; }
  .act-table tbody td { padding: 10px 14px; font-size: 13.5px;
    color: var(--td-text); border-bottom: 1px solid var(--td-border);
    vertical-align: top; }
  .act-table tbody tr:last-child td { border-bottom: none; }
  .act-time { white-space: nowrap; color: var(--td-text2); font-size: 12.5px; }
  .act-ch { white-space: nowrap; font-size: 12.5px; color: var(--td-text2); }
  .act-ch-manual { color: var(--td-text); }
  .act-store { white-space: nowrap; font-size: 12.5px; color: var(--td-text2); }
  .act-sku { display: block; font: 400 11.5px ui-monospace, 'SF Mono', Consolas, monospace;
    color: var(--td-text2); margin-top: 1px; }
  .act-delta, .act-bal { text-align: right; white-space: nowrap;
    font-variant-numeric: tabular-nums; }
  .act-out { color: var(--td-text); }
  .act-in { color: var(--td-sage-ink, #4A6249); }
  .act-zero { display: inline-block; padding: 0 8px; border-radius: 999px;
    background: var(--td-amber-tint); color: var(--td-amber-ink);
    font-size: 12px; font-weight: 500; line-height: 19px; }
  .act-ctx { font-size: 12px; color: var(--td-text2); max-width: 260px;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .act-empty { padding: 18px; text-align: center; color: var(--td-text2); }
  .act-foot { display: flex; justify-content: center; margin: 10px 0; }
  .act-ghost { appearance: none; background: none; border: none;
    cursor: pointer; border-radius: 8px; font: 400 13.5px var(--f-sans);
    color: var(--td-text2); padding: 9px 15px;
    transition: all .2s cubic-bezier(.16,1,.3,1); }
  .act-ghost:hover { color: var(--td-text); background: var(--td-raised); }

  @media (max-width: 900px) { .act-digest { grid-template-columns: 1fr; } }

/* ════════════════════════════════════════════════════════════════
   DASHBOARDS BY POSITION — body.pos-<position> is set at boot from
   the operator's role (02-settings-ops bootstrap).
   · owner (pos-default) is the 4-card glance: Recap · Needs you ·
     Today · Customers. The operational cards are hidden for it.
   · warehouse / customer-service / dispatch see NO revenue: the
     Recap, the Orders + Customers blocks are removed — the "Needs you"
     card (whose sub-cards are cap-gated in 03-dashboard dashCardWants /
     tdAttnRender) IS their dashboard. They keep the block that matches
     their job: warehouse gets Inventory + On-the-way/Drafts, dispatch
     gets On-the-road/Ready + Delivery, customer-service gets Delivery.
   · On phones those positions keep this same card surface instead of
     the revenue-led mobile variant (#rm-dash).
   · store-manager keeps the full pulse but locked to their store.
   ════════════════════════════════════════════════════════════════ */
/* The Recap is now shown to every role (role-filtered + money-free in
   tdRecapRender for warehouse / dispatch / customer-service), so it is not in
   this hide list. The money blocks (Today, Customers) stay hidden for them. */
body.pos-warehouse .td-block[data-block="orders"], body.pos-customer-service .td-block[data-block="orders"], body.pos-dispatch .td-block[data-block="orders"],
body.pos-warehouse .td-block[data-block="customers"], body.pos-customer-service .td-block[data-block="customers"], body.pos-dispatch .td-block[data-block="customers"],
body.pos-warehouse #tab-dashboard .td-foot, body.pos-customer-service #tab-dashboard .td-foot, body.pos-dispatch #tab-dashboard .td-foot,
/* role-restricted views are fixed — the Arrange control is hidden for them */
body.pos-warehouse .td-arrange-btn, body.pos-customer-service .td-arrange-btn, body.pos-dispatch .td-arrange-btn, body.pos-store-manager .td-arrange-btn {
  display: none;
}
/* Owner (pos-default) is the 4-card glance — recap · needs · today · customers.
   The operational cards belong to the warehouse & dispatch views, not the
   owner's morning, so they're hidden here even though the owner isn't a
   restricted role. */
body.pos-default .td-block[data-block="unhandled"],
body.pos-default .td-block[data-block="inventory"],
body.pos-default .td-block[data-block="incoming"],
body.pos-default .td-block[data-block="onroad"],
body.pos-default .td-block[data-block="ready"],
body.pos-default .td-block[data-block="delivery"] { display: none; }
/* "On the way / Drafts" (incoming) is warehouse-native — hidden for every non-
   warehouse role. Unhandled orders now shows for warehouse + dispatch +
   customer-service; only store-manager (not part of the redesign) keeps it hidden. */
body.pos-dispatch .td-block[data-block="incoming"],
body.pos-customer-service .td-block[data-block="incoming"],
body.pos-store-manager .td-block[data-block="incoming"], body.pos-store-manager .td-block[data-block="unhandled"] { display: none; }
/* "On the road" + "Ready to hand off" (delivery board) are delivery-native —
   shown to dispatch + customer-service, hidden for warehouse / store-manager
   (and for owner, via the pos-default rule above). */
body.pos-warehouse .td-block[data-block="onroad"], body.pos-warehouse .td-block[data-block="ready"],
body.pos-store-manager .td-block[data-block="onroad"], body.pos-store-manager .td-block[data-block="ready"] { display: none; }
/* Each restricted position keeps only the blocks it works in. Dispatch / CS now
   run on the On-the-road + Ready board, so the old Delivery section block is
   hidden for them too; only store-manager still uses Delivery. */
body.pos-dispatch .td-block[data-block="inventory"], body.pos-dispatch .td-block[data-block="delivery"],
body.pos-customer-service .td-block[data-block="inventory"], body.pos-customer-service .td-block[data-block="delivery"],
body.pos-warehouse .td-block[data-block="delivery"] { display: none; }
@media (max-width: 720px) {
  body.pos-warehouse #tab-dashboard .td-wrap,
  body.pos-customer-service #tab-dashboard .td-wrap,
  body.pos-dispatch #tab-dashboard .td-wrap { display: block; }
  body.pos-warehouse #rm-dash,
  body.pos-customer-service #rm-dash,
  body.pos-dispatch #rm-dash { display: none; }
}

/* ════════════════════════════════════════════════════════════════════
   SHARED PAGER — Orders · Customers  (markup: rmRenderPager, 01-core.js)
   Built to the Tacet system on the existing --td-* tokens, so it swaps
   with the light/dark theme like every other Tacet surface.

   The one rule worth stating out loud: there is NO Dusk in this bar.
   Dusk (#6E67AF) means "Tacet did this for you" — automation. Turning a
   page is the operator's own hand, so every control is a Secondary
   button: transparent fill, 1px border, ink text. The only Dusk allowed
   near a pager is the focus ring, which is a system affordance.
   ════════════════════════════════════════════════════════════════════ */
.t-pager {
  display: flex; align-items: center; justify-content: space-between;
  /* Wrap rather than crush. Flex items shrink by default, and a <select>
     shrinks its CONTENT box while keeping its 40px of padding — so in a narrow
     pane the digits vanish under the chevron before anything visibly "breaks".
     Given a row-gap the right-hand controls drop to a second line instead. */
  flex-wrap: wrap;
  gap: 12px 24px; padding: 16px;
  background: var(--td-raised);
  border-top: 1px solid var(--td-border);
  border-radius: 0 0 8px 8px;
  /* Inter, stated outright rather than inherited. The Tacet skin scope
     (#tacet-orders-skin) covers #tab-orders but NOT #tab-customers, so a
     pager that leaned on the ambient --f-sans would render in Inter under
     Orders and IBM Plex under Customers — the same component, two faces.
     Tacet is Inter for all product UI; the component carries it itself. */
  font-family: 'Inter', 'IBM Plex Sans SC', 'PingFang SC', 'Hiragino Sans GB', system-ui, sans-serif;
}
/* The count is the only thing allowed to give ground: it may shrink and, at
   the very end, wrap. Everything on the right is a control — controls keep
   their size or the operator can't hit them. */
.t-pager-count {
  font-size: 13px; color: var(--td-text2);
  flex: 1 1 auto; min-width: 0;
}
.t-pager-count b {
  color: var(--td-text); font-weight: 500;
  font-variant-numeric: tabular-nums; font-feature-settings: "tnum";
  white-space: nowrap;   /* never split "1,251–1,284" across two lines */
}
.t-pager-right {
  display: flex; align-items: center; gap: 12px 24px;
  margin-left: auto;
  /* In a cramped pane "Per page" and the nav together outgrow the bar. Let them
     stack, right-aligned, rather than push each other out past its edge.
     `flex: 0 1 auto` + min-width:0 is what makes that wrap possible: a
     non-shrinkable item is sized to its max-content, so its children would
     never be handed a narrower box to wrap inside, and the group would spill. */
  flex: 0 1 auto; min-width: 0;
  flex-wrap: wrap; justify-content: flex-end;
}
.t-pager-nav { display: flex; align-items: center; gap: 8px; flex: 0 0 auto; }
.t-pager-pos {
  font-size: 13px; color: var(--td-text2);
  font-variant-numeric: tabular-nums; font-feature-settings: "tnum";
  min-width: 58px; text-align: center; white-space: nowrap;
}

.t-pager-btn {
  font-family: inherit; font-size: 13px; font-weight: 500;
  color: var(--td-text); background: transparent;
  border: 1px solid var(--td-border); border-radius: 8px;
  padding: 7px 14px; cursor: pointer;
  display: inline-flex; align-items: center; gap: 6px;
  flex: 0 0 auto; white-space: nowrap;   /* phone rule below re-flexes these */
  transition: border-color 200ms cubic-bezier(.16, 1, .3, 1);
}
.t-pager-btn:hover:not(:disabled) { border-color: var(--td-text2); }
.t-pager-btn:focus-visible { outline: 2px solid var(--td-dusk); outline-offset: 2px; }
.t-pager-btn:disabled { opacity: .35; cursor: not-allowed; }
.t-pager-btn svg {
  width: 13px; height: 13px; flex: none;
  stroke: currentColor; fill: none;
}

.t-pager-size {
  display: inline-flex; align-items: center; gap: 8px;
  font-size: 13px; color: var(--td-text2); white-space: nowrap;
  flex: 0 0 auto;
}
.t-pager-size select {
  font-family: inherit; font-size: 13px; color: var(--td-text);
  /* background-COLOR only, never the `background` shorthand: the app paints the
     dropdown chevron as a background-image on `select`, and the shorthand would
     wipe it out. The right padding is that chevron's gutter — remove it (as a
     flat `padding: 6px 8px` did) and the chevron lands on top of the number.
     `:root[data-theme=light] select` outranks this rule anyway, so the image
     returns whatever we do here; give it room rather than a fight. */
  background-color: var(--td-surface);
  border: 1px solid var(--td-border);
  border-radius: 8px; padding: 6px 30px 6px 10px; cursor: pointer;
  /* Never shrink. A flex <select> collapses its content box first and keeps
     its padding, so under pressure the digits slide under the chevron and the
     control reads as a clipped blob. min-width holds 3 digits + both gutters. */
  flex: 0 0 auto; min-width: 72px;
  transition: border-color 200ms cubic-bezier(.16, 1, .3, 1);
}
.t-pager-size select:focus { outline: none; border-color: var(--td-dusk); }

/* Phone (≤720px) — the pager STAYS. It stacks: count on its own line, then
   full-width Prev/Next targets. The page-size selector drops away, because
   50 is right for a card list and a choice nobody makes is clutter.
   (Before this, #cu-pager was display:none here — which stranded phone
   operators on page 1 of the customer list forever.) */
@media (max-width: 720px) {
  .t-pager { flex-direction: column; align-items: stretch; gap: 12px; }
  .t-pager-count { text-align: center; }
  /* margin-left:auto is the desktop right-align; in this column it would stop
     the row stretching and shrink the tap targets back to content width. */
  .t-pager-right { justify-content: space-between; gap: 12px; margin-left: 0; }
  .t-pager-size  { display: none; }
  .t-pager-nav   { flex: 1 1 auto; justify-content: space-between; }
  .t-pager-btn   { flex: 1 1 auto; justify-content: center; padding: 10px 16px; }
}

@media (prefers-reduced-motion: reduce) {
  .t-pager-btn, .t-pager-size select { transition: none; }
}

/* ══ THE LIST CARD — Orders · Customers ═════════════════════════════════
   ONE surface holds the table, the phone card list, and the pager footer.
   The card owns the border, the radius and the lift; `overflow: hidden`
   clips every child to the corner, so the footer's corners are the card's
   corners by construction. There is no seam to align.

   This replaces an earlier attempt that left the pager a SIBLING of the
   table and tried to make two boxes agree: square the card's bottom, round
   the pager's. It could never hold. `#tab-orders .table-wrap` is declared
   in an inline <style> further down the document at the same specificity as
   any rule here, so it re-rounded the card's bottom corners on source order
   and the notch came back. Nesting removes the argument instead of winning it.

   The inner .table-wrap keeps overflow-x:auto: a wide table scrolls inside
   the card, and the footer stays put rather than sliding off sideways.   */
.list-card {
  background: var(--bg-pane);
  border: 1px solid var(--line);
  border-radius: 14px;
  overflow: hidden;
  box-shadow: 0 1px 2px rgba(18, 22, 26, .05), 0 8px 22px rgba(18, 22, 26, .07);
}

/* Strip the card chrome off the nested table wrapper — the .list-card is the
   card now. The id prefix outranks the inline `#tab-orders .table-wrap` /
   `#tab-customers .table-wrap` rules (1,2,0) vs (1,1,0), so this holds no
   matter where those rules sit in the document. */
#tab-orders    .list-card > .table-wrap,
#tab-customers .list-card > .table-wrap {
  background: transparent;
  border: 0;
  border-radius: 0;
  box-shadow: none;
  overflow: visible;
  overflow-x: auto;
}

/* The footer. A hairline above it, nothing else — the card draws the rest. */
#ord-pager .t-pager,
#cu-pager  .t-pager {
  border: 0;
  border-top: 1px solid var(--line);
  border-radius: 0;
  box-shadow: none;
  margin: 0;
}

@media (max-width: 720px) {
  /* Phones show the card list, not the table. The mobile rows carry their own
     surfaces, so the list card steps back to a plain container and the pager
     becomes its own rounded card beneath them. */
  .list-card {
    background: transparent;
    border: 0;
    border-radius: 0;
    box-shadow: none;
    overflow: visible;
  }
  #ord-pager .t-pager,
  #cu-pager  .t-pager {
    border: 1px solid var(--line);
    border-radius: 14px;
    margin-top: 12px;
  }
}

/* ════════════════════════════════════════════════════════════════════
   TACET COUPONS (vch-*) — Option A rebuild: one door, quiet rows.
   #tab-vouchers sits in the tacet-orders-skin token scope (inline style
   in retail-admin.html), so --bg-pane / --line / --text-* / --tacet-*
   already resolve to Tacet values here, light and dark. Dusk budget on
   this screen: the New-coupon CTA + the auto-apply pill/switch ("the
   system applies it for them") — nothing else.
   ════════════════════════════════════════════════════════════════════ */
/* Paper ground while the tab is active, like the other Tacet pages. */
body:has(#tab-vouchers.active) { background: var(--td-paper); }

/* Centered 1120px column like Dashboard/Reports/Sales. */
#tab-vouchers .td-head, #tab-vouchers .vch-body {
  max-width: 1120px; margin-left: auto; margin-right: auto;
}
#tab-vouchers .td-head { align-items: flex-start; flex-wrap: wrap; margin-bottom: 20px; }
.vch-spacer { flex: 1; }

/* Creation CTA — dark-ink primary, matches #tab-flash .fsm-new. */
.vch-new { background: var(--text-1); border-color: var(--text-1); color: var(--bg-app); gap: 7px; font-weight: 500; white-space: nowrap; }
.vch-new:hover { background: #24304A; border-color: #24304A; color: var(--paper, #FBF9F4); }
[data-theme="dark"] .vch-new:hover { background: #EAE7DF; border-color: #EAE7DF; color: var(--bg-app); }
.vch-new svg { flex: none; }

/* ── toolbar: segment + search ── */
.vch-toolbar { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; margin: 4px 0 14px; }
.vch-seg { display: inline-flex; border: 1px solid var(--line); border-radius: 999px; overflow: hidden; background: var(--bg-pane); }
.vch-segbtn {
  border: 0; background: transparent; font: 500 13px var(--f-sans);
  color: var(--text-2); padding: 6px 14px; cursor: pointer;
  transition: background var(--t-med, .2s) var(--ease-out, ease);
}
.vch-segbtn:hover { color: var(--text-1); }
.vch-segbtn.on { background: var(--text-1); color: var(--bg-pane); }
.vch-segbtn .n { opacity: .55; font-variant-numeric: tabular-nums; }
.vch-search {
  display: flex; align-items: center; gap: 8px; margin-left: auto;
  background: var(--bg-pane); border: 1px solid var(--line); border-radius: 8px;
  padding: 0 10px; color: var(--text-2); min-width: 230px;
}
.vch-search:focus-within { border-color: var(--tacet-dusk); }
.vch-search svg { width: 14px; height: 14px; flex: none; }
.vch-search input {
  border: 0; background: transparent; padding: 8px 0; width: 100%;
  font: 400 13.5px var(--f-sans); color: var(--text-1); outline: none;
}

/* ── the table ── */
.vch-card {
  background: var(--bg-pane); border: 1px solid var(--line);
  border-radius: 14px; overflow: hidden; overflow-x: auto;
}
#vouchers-table { width: 100%; border-collapse: collapse; }
#vouchers-table thead th {
  text-align: left; background: var(--bg-pane); color: var(--text-2);
  font-family: var(--f-sans); font-weight: 500; font-size: 11.5px;
  letter-spacing: .02em; text-transform: none; white-space: nowrap;
  padding: 12px 14px 9px; border-bottom: 1px solid var(--line);
}
#vouchers-table tbody td {
  padding: 13px 14px; border-bottom: 1px solid var(--line);
  font-size: 13.5px; color: var(--text-1); vertical-align: middle;
}
#vouchers-table tbody tr:last-child td { border-bottom: none; }
tr.vch-row { cursor: pointer; transition: background var(--t-med, .2s) var(--ease-out, ease); }
tr.vch-row:hover td { background: var(--bg-elev-1); }
tr.vch-row.is-off .vch-code, tr.vch-row.is-off .vch-gives { color: var(--text-2); }
.vch-code {
  font-family: var(--f-mono); font-weight: 500; font-size: 13px;
  letter-spacing: .03em; color: var(--text-1);
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
}
.vch-lbl { font-size: 12.5px; color: var(--text-2); margin-top: 2px; }
.vch-gives { font-weight: 500; color: var(--text-1); white-space: nowrap; }
.vch-pills { display: flex; gap: 6px; flex-wrap: wrap; margin-top: 5px; }
.vch-pill {
  display: inline-flex; align-items: center; gap: 6px;
  font: 500 11.5px var(--f-sans); line-height: 1; padding: 4px 9px;
  border-radius: 999px; background: var(--bg-elev-1); color: var(--text-2);
  white-space: nowrap; font-variant-numeric: tabular-nums;
}
.vch-pill .dot { width: 6px; height: 6px; border-radius: 50%; background: currentColor; }
.vch-pill.sage { background: var(--tacet-sage-tint); color: var(--tacet-sage-ink); }
.vch-pill.dusk { background: var(--tacet-dusk-tint, #EEEDF6); color: var(--tacet-dusk); }
.vch-pill.dusk .dot { animation: vchBreathe 2.4s cubic-bezier(.16, 1, .3, 1) infinite; }
@keyframes vchBreathe { 0%, 100% { opacity: 1; } 50% { opacity: .45; } }
@media (prefers-reduced-motion: reduce) { .vch-pill.dusk .dot { animation: none; } }
.vch-rules, .vch-end { font-size: 13px; color: var(--text-2); white-space: nowrap; }
.vch-used { font-variant-numeric: tabular-nums; white-space: nowrap; }
.vch-end { font-variant-numeric: tabular-nums; }
.vch-end.amber { color: var(--tacet-amber-ink); }
.vch-go { text-align: right; color: var(--text-3); white-space: nowrap; }
.vch-go svg { display: inline-block; vertical-align: middle; }
#vch-offnote { margin-top: 14px; font-size: 13px; color: var(--text-2); }
#vch-offnote:empty { display: none; }
#vch-offnote button {
  border: 0; background: none; padding: 0; cursor: pointer;
  font: inherit; color: var(--text-1); text-decoration: underline; text-underline-offset: 3px;
}

/* ── the coupon drawer ── */
#vch-drawer .drawer-body { padding-top: 26px; }
.vch-kicker {
  font-size: 11px; font-weight: 500; letter-spacing: .1em; text-transform: uppercase;
  color: var(--text-2); margin: 24px 0 4px;
}
.vch-dw-head { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; padding-right: 40px; }
.vch-dw-code { font-family: var(--f-mono); font-weight: 500; font-size: 17px; letter-spacing: .03em; color: var(--text-1); }
.vch-note { margin-top: 12px; padding: 10px 13px; border-radius: 8px; font-size: 12.5px; line-height: 1.5; }
.vch-note.amber { background: var(--tacet-amber-tint); color: var(--tacet-amber-ink); }
.vch-facts { margin-top: 14px; }
.vch-fact {
  display: flex; justify-content: space-between; align-items: center; gap: 16px;
  padding: 8px 0; border-bottom: 1px solid var(--line); font-size: 13.5px;
}
.vch-fact:last-child { border-bottom: none; }
.vch-fact .k { color: var(--text-2); }
.vch-fact .v { font-weight: 500; color: var(--text-1); text-align: right; font-variant-numeric: tabular-nums; }
.vch-date, .vch-per {
  border: 1px solid var(--line); background: var(--bg-input); color: var(--text-1);
  border-radius: 8px; padding: 5px 8px; font: 400 13px var(--f-sans);
}
.vch-date:focus, .vch-per:focus { outline: none; border-color: var(--tacet-dusk); }
/* Native select arrow is painted over the label at this width, so suppress it
   and draw our own chevron on the wrapper (::after inherits currentColor, which
   a background-image data URI can't). */
.vch-perwrap { position: relative; display: inline-block; }
.vch-perwrap::after {
  content: ""; position: absolute; right: 10px; top: 50%; margin-top: -5px;
  width: 6px; height: 6px; pointer-events: none;
  border-right: 1.5px solid var(--text-2); border-bottom: 1.5px solid var(--text-2);
  transform: rotate(45deg);
}
.vch-per {
  cursor: pointer; appearance: none; -webkit-appearance: none;
  padding-right: 26px; text-align: left;
}
.vch-per::-ms-expand { display: none; }

/* Who holds this coupon — a short head of the grant list, in the drawer. */
.vch-holders:not(:empty) { margin-top: 10px; border-top: 1px solid var(--line); }
.vch-holder {
  display: flex; align-items: center; gap: 10px;
  padding: 7px 0; border-bottom: 1px solid var(--line); font-size: 12.5px;
}
.vch-holder:last-child { border-bottom: none; }
/* Name + phone are one target that opens the customer; Take it back stays its
   own control, so the row can't send you to a page you meant to revoke from. */
.vch-holder .go {
  flex: 1 1 auto; min-width: 0; display: flex; align-items: center; gap: 10px;
  border: 0; background: none; padding: 0; margin: 0;
  font: inherit; text-align: left; cursor: pointer;
}
.vch-holder .go:hover .who { color: var(--tacet-dusk); }
.vch-holder .go:focus-visible { outline: 2px solid var(--tacet-dusk); outline-offset: 3px; border-radius: 4px; }
.vch-holder .who { font-weight: 500; color: var(--text-1); flex: none; }
.vch-holder .ph {
  color: var(--text-2); font-variant-numeric: tabular-nums;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.vch-holders .lnk {
  flex: none; border: 0; background: none; padding: 0; cursor: pointer;
  font: inherit; color: var(--text-2); text-decoration: underline;
  text-underline-offset: 2px;
}
.vch-holders .lnk:hover { color: var(--tacet-brick); }
.vch-holders .lnk.more { display: block; margin-top: 8px; color: var(--text-1); }
.vch-holders .lnk.more:hover { color: var(--tacet-dusk); }
.vch-swrow { display: flex; align-items: flex-start; gap: 12px; padding: 11px 0; border-bottom: 1px solid var(--line); }
.vch-swrow .t { font-size: 13.5px; font-weight: 500; color: var(--text-1); }
.vch-swrow .d { font-size: 12px; color: var(--text-2); line-height: 1.5; }
.vch-sw {
  flex: none; width: 36px; height: 21px; border-radius: 999px; border: 0; padding: 0;
  background: var(--line-strong); position: relative; margin-top: 2px; cursor: pointer;
  transition: background var(--t-med, .2s) var(--ease-out, ease);
}
.vch-sw::after {
  content: ""; position: absolute; top: 2.5px; left: 3px; width: 16px; height: 16px;
  border-radius: 50%; background: #fff; box-shadow: 0 1px 2px rgba(17, 26, 43, .25);
  transition: left .2s cubic-bezier(.16, 1, .3, 1);
}
.vch-sw.on { background: var(--text-1); }
.vch-sw.on.dusk { background: var(--tacet-dusk); }
.vch-sw.on::after { left: 17px; }
.vch-labels { display: flex; flex-direction: column; gap: 8px; margin-top: 8px; }
.vch-labels input {
  width: 100%; border: 1px solid var(--line); background: var(--bg-input);
  color: var(--text-1); border-radius: 8px; padding: 8px 10px; font: 400 13px var(--f-sans);
}
.vch-labels input:focus { outline: none; border-color: var(--tacet-dusk); }
.vch-labels .btn { align-self: flex-start; }
.vch-grants { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-top: 8px; }
.vch-grants .d { font-size: 12.5px; color: var(--text-2); line-height: 1.5; }
.vch-grants .btn { flex: none; }
.vch-dw-foot { display: flex; align-items: center; gap: 12px; margin-top: 28px; padding-top: 16px; border-top: 1px solid var(--line); }
.vch-dw-foot .d { font-size: 12px; color: var(--text-2); }
.vch-off-btn { color: var(--tacet-brick); border-color: var(--tacet-brick); background: transparent; }
.vch-off-btn:hover { background: var(--tacet-brick-tint); color: var(--tacet-brick); border-color: var(--tacet-brick); }

@media (max-width: 720px) {
  .vch-search { min-width: 0; width: 100%; margin-left: 0; }
  #vouchers-table thead th:nth-child(3), #vouchers-table tbody td:nth-child(3) { display: none; }
}

/* ════════════════════════════════════════════════════════════════════
   TACET MEMBERSHIP & POINTS (mp-*) — Option A rebuild.
   #tab-membership joins the #tacet-orders-skin token scope (retail-admin
   .html), so --bg-pane / --line / --text-* / --tacet-* already resolve to
   Tacet values here, light and dark. The points rules read as editable
   sentences; the Roots Club ladder reads as cards with one inline editor
   open at a time (replaces the old 8-column spreadsheet).
   Dusk budget on this screen: ZERO — nothing here is automation. Sage
   marks members present / tiers reached; amber flags a setting worth a
   second look; the only solid buttons are ink (the surface, inverted).
   ════════════════════════════════════════════════════════════════════ */
/* Paper ground while the tab is active, like the other Tacet pages. */
body:has(#tab-membership.active) { background: var(--td-paper); }

#tab-membership .mp { max-width: 1120px; }
#tab-membership .mp-eyebrow {
  font-size: 11px; font-weight: 500; letter-spacing: .14em; text-transform: uppercase;
  color: var(--text-3); margin: 34px 0 6px;
}
#tab-membership .mp-eyebrow:first-child { margin-top: 8px; }
#tab-membership .mp-section-sub {
  font-size: 13px; color: var(--text-2); margin: 0 0 16px; max-width: 70ch; line-height: 1.6;
}

#tab-membership .mp-card {
  background: var(--bg-pane); border: 1px solid var(--line); border-radius: 14px;
  overflow: hidden;
}

/* ── rules as sentences ── */
#tab-membership .mp-rules { padding: 6px 22px; }
#tab-membership .mp-rule {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  padding: 15px 0; border-bottom: 1px solid var(--line-soft);
  font-size: 15px; color: var(--text-1); line-height: 1.5;
}
#tab-membership .mp-rule:last-child { border-bottom: 0; }
#tab-membership .mp-why { font-size: 13px; color: var(--text-3); }
#tab-membership .mp-in {
  font: inherit; font-size: 14px; font-variant-numeric: tabular-nums;
  color: var(--text-1); background: var(--bg-input);
  border: 1px solid var(--line); border-radius: 8px;
  padding: 7px 9px; width: 74px; text-align: center;
}
#tab-membership .mp-rule select {
  font: inherit; font-size: 14px; color: var(--text-1); background: var(--bg-input);
  border: 1px solid var(--line); border-radius: 8px; padding: 7px 28px 7px 10px;
}
#tab-membership .mp-in:focus-visible,
#tab-membership .mp-rule select:focus-visible,
#tab-membership .mp-field input:focus-visible,
#tab-membership .mp-field textarea:focus-visible {
  outline: 2px solid var(--tacet-ink); outline-offset: 1px; border-color: var(--text-2);
}
[data-theme="dark"] #tab-membership .mp-in:focus-visible,
[data-theme="dark"] #tab-membership .mp-rule select:focus-visible,
[data-theme="dark"] #tab-membership .mp-field input:focus-visible,
[data-theme="dark"] #tab-membership .mp-field textarea:focus-visible {
  outline-color: var(--text-1);
}

/* amber inline hint (attention, never alarmist) */
#tab-membership .mp-hint {
  display: inline-flex; align-items: baseline; gap: 6px;
  background: var(--tacet-amber-tint); color: var(--tacet-amber-ink);
  border-radius: 8px; padding: 5px 11px; font-size: 12.5px; line-height: 1.5;
}
#tab-membership .mp-hint[hidden] { display: none; }

/* the shipping switch — a styled checkbox */
#tab-membership .mp-switch {
  position: relative; display: inline-flex; flex: none;
  width: 40px; height: 24px; cursor: pointer;
}
#tab-membership .mp-switch input {
  position: absolute; opacity: 0; width: 100%; height: 100%; margin: 0; cursor: pointer;
}
#tab-membership .mp-knob {
  position: absolute; inset: 0; border-radius: 999px; background: var(--line-strong);
  transition: background 200ms cubic-bezier(.16,1,.3,1);
}
#tab-membership .mp-knob::after {
  content: ""; position: absolute; top: 3px; left: 3px; width: 18px; height: 18px;
  border-radius: 50%; background: #fff; box-shadow: 0 1px 2px rgba(17,26,43,.3);
  transition: left 200ms cubic-bezier(.16,1,.3,1);
}
#tab-membership .mp-switch input:checked + .mp-knob { background: var(--tacet-ink); }
[data-theme="dark"] #tab-membership .mp-switch input:checked + .mp-knob { background: var(--tacet-sage); }
#tab-membership .mp-switch input:checked + .mp-knob::after { left: 19px; }
#tab-membership .mp-switch input:focus-visible + .mp-knob { outline: 2px solid var(--tacet-ink); outline-offset: 2px; }

/* outcome line — the grey sentence that does the math */
#tab-membership .mp-outcome {
  margin: 0 22px 18px; padding: 12px 16px; background: var(--tacet-cream);
  border-radius: 8px; font-size: 13.5px; color: var(--text-2); line-height: 1.6;
  font-variant-numeric: tabular-nums;
}
#tab-membership .mp-outcome b { font-weight: 600; color: var(--text-1); }

/* card foot + save row */
#tab-membership .mp-card-foot {
  display: flex; align-items: center; gap: 16px;
  padding: 13px 22px; border-top: 1px solid var(--line); background: var(--tacet-cream);
}
#tab-membership .mp-foot-note { font-size: 12.5px; color: var(--text-3); }
#tab-membership .mp-foot-sp { margin-left: auto; }

#tab-membership .mp-btn {
  font: 500 14px var(--f-sans); padding: 9px 18px; border-radius: 8px;
  border: 1px solid transparent; cursor: pointer;
  display: inline-flex; align-items: center; gap: 8px; white-space: nowrap;
  transition: all 160ms cubic-bezier(.16,1,.3,1);
}
#tab-membership .mp-btn-primary { background: var(--text-1); color: var(--bg-pane); }
#tab-membership .mp-btn-primary:hover { opacity: .9; }
#tab-membership .mp-btn-secondary { background: transparent; color: var(--text-1); border-color: var(--line-strong); }
#tab-membership .mp-btn-secondary:hover { border-color: var(--text-2); }
#tab-membership .mp-btn-quiet {
  background: none; border: 0; color: var(--text-3); font: 500 13px var(--f-sans);
  padding: 4px 0; cursor: pointer;
}
#tab-membership .mp-btn-quiet:hover { color: var(--text-1); }
#tab-membership .mp-btn-danger {
  background: none; border: 0; color: var(--tacet-brick); font: 500 13px var(--f-sans);
  padding: 4px 0; cursor: pointer;
}
#tab-membership .mp-btn-danger:hover { text-decoration: underline; }
#tab-membership .mp-btn:focus-visible,
#tab-membership .mp-btn-quiet:focus-visible,
#tab-membership .mp-btn-danger:focus-visible { outline: 2px solid var(--tacet-ink); outline-offset: 2px; }
[data-theme="dark"] #tab-membership .mp-btn:focus-visible { outline-color: var(--text-1); }

/* ── tier cards ── */
#tab-membership .mp-tier-cards {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(232px, 1fr)); gap: 14px;
}
#tab-membership .mp-tier {
  background: var(--bg-pane); border: 1px solid var(--line); border-radius: 14px;
  padding: 17px 19px 14px; display: flex; flex-direction: column; gap: 6px;
}
#tab-membership .mp-tier.editing { border-color: var(--text-2); box-shadow: 0 0 0 1px var(--text-2); }
#tab-membership .mp-tier-head { display: flex; align-items: baseline; gap: 8px; }
#tab-membership .mp-tier-name { font-size: 16px; font-weight: 600; color: var(--text-1); }
#tab-membership .mp-tier-name .cn { font-size: 13px; font-weight: 400; color: var(--text-3); margin-left: 4px; }
#tab-membership .mp-tier-mult {
  margin-left: auto; font-size: 12.5px; font-weight: 500; font-variant-numeric: tabular-nums;
  background: var(--tacet-cream); border-radius: 999px; padding: 2px 10px;
  color: var(--text-2); white-space: nowrap;
}
#tab-membership .mp-tier-from { font-size: 13px; color: var(--text-2); font-variant-numeric: tabular-nums; }
#tab-membership .mp-tier-from b { font-weight: 600; color: var(--text-1); }
#tab-membership .mp-tier-members {
  display: inline-flex; align-items: center; gap: 7px;
  font-size: 12.5px; color: var(--text-3); font-variant-numeric: tabular-nums;
}
#tab-membership .mp-tier-members .dot { width: 7px; height: 7px; border-radius: 50%; background: var(--tacet-sage); flex: none; }
#tab-membership .mp-tier-perks { list-style: none; margin: 6px 0 6px; padding: 0; flex: 1; }
#tab-membership .mp-tier-perks li {
  font-size: 13px; color: var(--text-1); padding: 3px 0 3px 15px; position: relative; line-height: 1.5;
}
#tab-membership .mp-tier-perks li::before {
  content: ""; position: absolute; left: 2px; top: 10px; width: 5px; height: 5px;
  border-radius: 50%; background: var(--line-strong);
}
#tab-membership .mp-tier-perks .mp-perks-empty { color: var(--text-4); padding-left: 0; }
#tab-membership .mp-tier-perks .mp-perks-empty::before { display: none; }
#tab-membership .mp-tier-edit { align-self: flex-start; }

/* ── inline tier editor ── */
#tab-membership .mp-editor {
  background: var(--bg-pane); border: 1px solid var(--text-2); border-radius: 14px;
  padding: 19px 22px 16px; margin-top: 14px;
}
#tab-membership .mp-editor-cap {
  font-size: 10.5px; font-weight: 500; letter-spacing: .14em; text-transform: uppercase;
  color: var(--text-3); margin-bottom: 14px;
}
#tab-membership .mp-field-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 14px 16px; }
#tab-membership .mp-field { display: flex; flex-direction: column; gap: 5px; min-width: 0; }
#tab-membership .mp-field.half { grid-column: span 2; }
#tab-membership .mp-field label { font-size: 12px; font-weight: 500; color: var(--text-2); }
#tab-membership .mp-field input, #tab-membership .mp-field textarea {
  font: inherit; font-size: 14px; color: var(--text-1); background: var(--bg-input);
  border: 1px solid var(--line); border-radius: 8px; padding: 7px 10px; width: 100%;
}
#tab-membership .mp-field textarea { resize: vertical; min-height: 88px; line-height: 1.55; }
#tab-membership .mp-field .mp-aff { display: flex; align-items: center; gap: 6px; }
#tab-membership .mp-field .mp-aff .u { font-size: 14px; color: var(--text-3); }
#tab-membership .mp-field .mp-aff input { font-variant-numeric: tabular-nums; }
#tab-membership .mp-field .fh { font-size: 12px; color: var(--text-3); }
#tab-membership .mp-editor-foot {
  display: flex; align-items: center; gap: 16px; margin-top: 16px;
  padding-top: 14px; border-top: 1px solid var(--line);
}
#tab-membership .mp-editor-foot .mp-key { font-size: 12px; color: var(--text-3); }
#tab-membership .mp-editor-foot .mp-key code {
  font-family: var(--f-mono, ui-monospace, Menlo, monospace); font-size: 11.5px;
  background: var(--tacet-cream); border-radius: 4px; padding: 1px 6px; color: var(--text-2);
}
#tab-membership .mp-editor-foot .mp-foot-sp { margin-left: auto; }

/* ── save row under the ladder ── */
#tab-membership .mp-tier-save {
  display: flex; align-items: center; gap: 16px; margin-top: 20px; flex-wrap: wrap;
}
#tab-membership .mp-tier-save .mp-foot-note { flex: 1; min-width: 220px; }
#tab-membership .mp-tier-empty {
  grid-column: 1 / -1; padding: 22px; text-align: center; color: var(--text-3);
  font-size: 13px; border: 1px dashed var(--line-strong); border-radius: 14px;
}

@media (max-width: 720px) {
  #tab-membership .mp-field-grid { grid-template-columns: repeat(2, 1fr); }
  #tab-membership .mp-field.half { grid-column: 1 / -1; }
  #tab-membership .mp-tier-cards { grid-template-columns: 1fr; }
  #tab-membership .mp-editor-foot { flex-wrap: wrap; }
  #tab-membership .mp-editor-foot .mp-foot-sp { margin-left: 0; flex-basis: 100%; }
}

/* ── rules + live shopper preview (side by side) ── */
#tab-membership .mp-split {
  display: grid; grid-template-columns: minmax(0, 1fr) 320px; gap: 20px; align-items: start;
}
#tab-membership .mp-preview {
  position: sticky; top: 16px; display: flex; flex-direction: column; gap: 10px; min-width: 0;
}
#tab-membership .mp-pv-tabs { display: flex; flex-wrap: wrap; gap: 6px; }
#tab-membership .mp-pv-tab {
  font: 500 12px var(--f-sans); padding: 5px 11px; border-radius: 999px;
  border: 1px solid var(--line); background: var(--bg-pane); color: var(--text-2); cursor: pointer;
  transition: all 160ms cubic-bezier(.16, 1, .3, 1);
}
#tab-membership .mp-pv-tab:hover { border-color: var(--text-2); }
#tab-membership .mp-pv-tab.on { background: var(--text-1); color: var(--bg-pane); border-color: var(--text-1); }
#tab-membership .mp-pv-tab:focus-visible { outline: 2px solid var(--tacet-ink); outline-offset: 2px; }
[data-theme="dark"] #tab-membership .mp-pv-tab:focus-visible { outline-color: var(--text-1); }

/* the phone stays its own dark surface in both themes — it is the mini-app card */
#tab-membership .mp-phone {
  background: var(--tacet-ink); border-radius: 24px; padding: 11px;
  box-shadow: 0 14px 40px rgba(17, 26, 43, .20);
}
#tab-membership .mp-pv-scr {
  background: linear-gradient(160deg, #22324f, #141d30); border-radius: 17px;
  padding: 20px 18px 22px; color: #fff; min-height: 386px; display: flex; flex-direction: column;
}
#tab-membership .mp-pv-eyebrow {
  font-size: 10.5px; font-weight: 500; letter-spacing: .14em; text-transform: uppercase;
  color: rgba(255, 255, 255, .5);
}
#tab-membership .mp-pv-tier { font-size: 23px; font-weight: 600; margin: 6px 0 2px; line-height: 1.15; }
#tab-membership .mp-pv-tier small { font-size: 14px; font-weight: 400; color: rgba(255, 255, 255, .6); margin-left: 5px; }
#tab-membership .mp-pv-mult {
  align-self: flex-start; font-size: 12px; font-weight: 500; font-variant-numeric: tabular-nums;
  background: rgba(110, 103, 175, .42); color: #d3cff2; padding: 3px 11px; border-radius: 999px; margin-top: 8px;
}
#tab-membership .mp-pv-bar {
  height: 6px; border-radius: 999px; background: rgba(255, 255, 255, .14);
  margin: 20px 0 8px; overflow: hidden;
}
#tab-membership .mp-pv-bar i { display: block; height: 100%; background: var(--tacet-sage); border-radius: 999px; }
#tab-membership .mp-pv-next { font-size: 12px; color: rgba(255, 255, 255, .6); }
#tab-membership .mp-pv-pts {
  margin-top: 20px; padding-top: 18px; border-top: 1px solid rgba(255, 255, 255, .12);
  display: flex; align-items: baseline; justify-content: space-between; gap: 10px;
}
#tab-membership .mp-pv-pts .n { font-size: 25px; font-weight: 600; font-variant-numeric: tabular-nums; }
#tab-membership .mp-pv-pts .l { font-size: 11.5px; color: rgba(255, 255, 255, .55); text-align: right; }
#tab-membership .mp-pv-perks {
  margin: 16px 0 0; padding: 0; list-style: none;
  font-size: 12.5px; color: rgba(255, 255, 255, .74); line-height: 1.65;
}
#tab-membership .mp-pv-perks li { padding-left: 15px; position: relative; }
#tab-membership .mp-pv-perks li::before {
  content: ""; position: absolute; left: 2px; top: 9px; width: 5px; height: 5px;
  border-radius: 50%; background: var(--tacet-sage);
}
#tab-membership .mp-pv-cap { font-size: 11.5px; color: var(--text-3); text-align: center; }

@media (max-width: 900px) {
  #tab-membership .mp-split { grid-template-columns: 1fr; }
  #tab-membership .mp-preview { position: static; max-width: 360px; }
}

/* ===========================================================================
   PRODUCT EDITOR - Paper / Inter skin (the approved mockup).
   The editor renders as a light "Paper" surface no matter the admin's theme.
   Rather than override every rule, we re-declare the LIGHT Tacet token values
   scoped to #pd-editor so all native styling resolves to light values; then a
   few flourishes (Inter caps, white cards, Dusk accent, sliding publish switch)
   take it the rest of the way. Mockup: reference/product-edit-essentials-mockup.html
   =========================================================================== */
  #pd-editor {
    --bg-app:     #FBF9F4;
    --bg-pane:    #FFFFFF;
    --bg-elev-1:  #F6F3EC;
    --bg-elev-2:  #EFEBE2;
    --bg-input:   #FFFFFF;
    --bg-overlay: rgba(18, 22, 26, .46);
    --line-soft:  rgba(18, 22, 26, .09);
    --line:       rgba(18, 22, 26, .14);
    --line-strong: rgba(18, 22, 26, .22);
    --text-1:     #111A2B;
    --text-2:     #3f4653;
    --text-3:     #58606E;
    --text-4:     #8b9098;
    --brass:      #C9954A;   /* was Aged Brass → Tacet amber */
    --accent:     #4A6249;   /* was Tea Green → Tacet sage-ink */
    --danger:     #B4533E;
    background: #FBF9F4;
    color: #111A2B;
    border: 1px solid #E4E0D5;
    border-radius: 14px;
    font-family: 'Inter', -apple-system, BlinkMacSystemFont, system-ui, sans-serif;
  }
  #pd-editor .pu-editor-head, #pd-editor .pu-editor-foot { background: #FBF9F4; border-color: var(--line); }
  #pd-editor .pu-editor-head #pd-title { font-size: 18px; font-weight: 600; letter-spacing: -.005em; color: #111A2B; }
  #pd-editor .pd-card, #pd-editor .bp-mode { background: #FFFFFF; }
  #pd-editor .pd-card { border: 1px solid #E4E0D5; border-radius: 14px; padding: 18px; }
  /* Quiet rows = ONE continuous list with hairline dividers (mockup), not cards. */
  #pd-editor .pe-rows { gap: 0; }
  #pd-editor .pe-dis, #pd-editor .pd-var-toggle {
    background: transparent; border: none; border-top: 1px solid var(--line-soft); border-radius: 0;
  }
  #pd-editor .pe-dis[open] { background: transparent; }
  #pd-editor .pe-dis > summary { padding: 18px 6px; }
  #pd-editor .pe-dis > summary:hover, #pd-editor .pd-var-toggle:hover { background: #FBF9F4; border-top-color: var(--line-soft); }
  #pd-editor .pe-dis-body { border-top: none; padding: 0 6px 20px; }
  #pd-editor .pd-var-toggle { padding: 18px 6px; }
  /* Small section caps stay uppercase; ROW TITLES are Title-Case ink (mockup). */
  #pd-editor .pd-sec-h, #pd-editor .pd-card-h {
    font-family: 'Inter', sans-serif; font-size: 11px; font-weight: 600; letter-spacing: .09em;
    text-transform: uppercase; color: #58606E;
  }
  #pd-editor .pe-dis-t, #pd-editor .pd-var-head {
    font-family: 'Inter', sans-serif; font-size: 15px; font-weight: 500; letter-spacing: 0;
    text-transform: none; color: #111A2B; margin: 0;
  }
  #pd-editor .pe-dis-s, #pd-editor .pd-var-summary { font-size: 14px; color: #58606E; }
  /* Field labels: Title-Case, not the native uppercase micro-label. */
  #pd-editor .stacked, #pd-editor label.stacked, #pd-editor .field label.stacked {
    text-transform: none; letter-spacing: 0; font-size: 13px; font-weight: 500; color: #111A2B;
  }
  #pd-editor .stacked .muted, #pd-editor label.stacked .muted { color: #58606E; font-weight: 400; }
  #pd-editor input:focus, #pd-editor select:focus, #pd-editor textarea:focus,
  #pd-editor .pd-rte:focus, #pd-editor .field input:focus { outline: none; border-color: #6E67AF; }
  #pd-editor .bp-mode input { accent-color: #6E67AF; }
  #pd-editor .bp-mode.is-on { border-color: #111A2B; background: #F6F3EC; }
  #pd-editor .pa-pill-bundle, #pd-editor .pa-pill.pa-pill-bundle { background: #F6F3EC; color: #58606E; }
  #pd-editor .pd-pub { background: #FFFFFF; border-radius: 12px; }
  #pd-editor .pd-pub > svg { display: none; }
  #pd-editor .pd-pub > span { order: 1; font-weight: 500; color: #111A2B; }
  #pd-editor .pd-pub input[type="checkbox"] {
    order: 2; margin-left: auto; -webkit-appearance: none; appearance: none;
    width: 38px; height: 22px; border-radius: 999px; background: #E4E0D5; position: relative;
    cursor: pointer; transition: background .2s var(--ease-out); flex: none;
  }
  #pd-editor .pd-pub input[type="checkbox"]::after {
    content: ""; position: absolute; top: 3px; left: 3px; width: 16px; height: 16px;
    border-radius: 50%; background: #fff; box-shadow: 0 1px 2px rgba(0,0,0,.18);
    transition: transform .2s var(--ease-out);
  }
  #pd-editor .pd-pub input[type="checkbox"]:checked { background: #111A2B; }
  #pd-editor .pd-pub input[type="checkbox"]:checked::after { transform: translateX(16px); }
  /* Photos: drop the "PHOTOS" cap (mockup omits it) + tidy the native file input. */
  #pd-editor .pe-ess-main .pd-sec > .pd-sec-h { display: none; }
  #pd-editor #pd-img-file { color: transparent; font-size: 12px; }
  #pd-editor #pd-img-file::file-selector-button {
    font: 500 12px 'Inter', sans-serif; color: #111A2B; background: #fff; border: 1px solid #E4E0D5;
    border-radius: 8px; padding: 8px 14px; margin-right: 8px; cursor: pointer;
  }
  #pd-editor #pd-img-file::file-selector-button:hover { border-color: #58606E; }
  #pd-editor .pd-gallery .pd-thumb { border-radius: 10px; }
  /* Header identity strip (mockup): thumbnail + title + Bundle pill / SKU subtitle. */
  #pd-editor .pu-editor-head { display: flex; align-items: center; gap: 14px; }
  #pd-editor .pd-head-id { display: flex; align-items: center; gap: 13px; min-width: 0; }
  #pd-editor .pd-head-thumb { width: 44px; height: 44px; border-radius: 10px; border: 1px solid #E4E0D5;
    background: #E7E0EE no-repeat center / cover; flex: none; }
  #pd-editor .pd-head-txt { min-width: 0; }
  #pd-editor .pd-head-titlerow { display: flex; align-items: center; gap: 10px; }
  #pd-editor .pu-editor-head #pd-title { font-size: 18px; font-weight: 600; letter-spacing: -.005em; color: #111A2B; }
  #pd-editor .pd-head-sub { font-size: 12.5px; color: #58606E; margin-top: 2px; }
  #pd-editor .pd-head-actions { margin-left: auto; display: flex; align-items: center; gap: 10px; }
  #pd-editor .pd-head-actions .pd-save-status { margin-right: 4px; }
  /* Dashed "+ Add" photo tile (mockup): the label triggers the hidden file input. */
  #pd-editor #pd-img-file { display: none; }
  #pd-editor .pe-photos-strip { display: flex; flex-wrap: wrap; gap: 10px; align-items: flex-start; }
  #pd-editor .pd-gallery { display: contents; }
  #pd-editor .pe-add-tile {
    width: 84px; height: 84px; border: 1px dashed #C9C3B4; border-radius: 10px;
    display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 4px;
    color: #58606E; font: 500 11px 'Inter', sans-serif; cursor: pointer; flex: none;
    transition: border-color .15s var(--ease-out), color .15s var(--ease-out);
  }
  #pd-editor .pe-add-tile:hover { border-color: #58606E; color: #111A2B; }
  /* Essentials stock area: loose section like the mockup, not a bordered card.
     (The publish pill card keeps its border.) */
  #pd-editor .pe-ess-side .pd-card:not(.pd-pub) { border: none; border-radius: 0; background: transparent; padding: 0; }
  /* Assembled-bundle computed stock panel in the editor (Dusk = Tacet worked it out). */
  #pd-editor .pe-bstock { background: #EEEDF6; border-radius: 12px; padding: 14px 15px; }
  #pd-editor .pe-bstock-cap { display: flex; align-items: center; gap: 8px; font: 600 12px 'Inter', sans-serif; letter-spacing: .05em; text-transform: uppercase; color: #6E67AF; }
  #pd-editor .pe-bstock-dot { width: 7px; height: 7px; border-radius: 50%; background: #6E67AF; flex: none; }
  #pd-editor .pe-bstock-big { font-size: 24px; font-weight: 600; margin: 8px 0 2px; color: #111A2B; font-variant-numeric: tabular-nums; }
  #pd-editor .pe-bstock-lim { font-size: 13px; color: #58606E; }
  #pd-editor .pe-bstock-split { display: flex; flex-wrap: wrap; gap: 14px; margin-top: 12px; padding-top: 12px; border-top: 1px solid rgba(110,103,175,.18); font-size: 13px; color: #111A2B; }
  #pd-editor .pe-bstock-split b { font-variant-numeric: tabular-nums; }
  #pd-editor .pe-bstock-note { font-size: 12px; color: #58606E; margin: 10px 2px 0; }

  /* Sales card: units/revenue/rank for a chosen window + a daily spark line. */
  #pd-editor .pd-sales-head { display: flex; align-items: center; justify-content: space-between; }
  #pd-editor .pd-sales-chips { display: flex; gap: 6px; }
  #pd-editor .pd-chip {
    font: 500 11px 'Inter', sans-serif; text-transform: none; letter-spacing: 0;
    padding: 3px 10px; border-radius: 999px; border: 1px solid #E4E0D5; background: #FFFFFF;
    color: #58606E; cursor: pointer; transition: all .15s var(--ease-out);
  }
  #pd-editor .pd-chip:hover { border-color: #58606E; color: #111A2B; }
  #pd-editor .pd-chip.pd-chip-active { background: #111A2B; border-color: #111A2B; color: #FFFFFF; }
  #pd-editor .pd-sales-kpis { display: grid; grid-template-columns: repeat(3, 1fr); gap: 14px; margin-top: 2px; }
  #pd-editor .pd-sales-kpi .v { font-size: 20px; font-weight: 600; color: #111A2B; font-variant-numeric: tabular-nums; }
  #pd-editor .pd-sales-kpi .l { font-size: 11.5px; color: #58606E; margin-top: 2px; }
  /* .db-spark-wrap gives position:relative + the dot marker's absolute
     placement; override its height/color here for this card's bigger chart. */
  #pd-editor .pd-sales-spark { width: 100%; height: 44px; margin-top: 10px; color: #111A2B; }
  #pd-editor .pd-sales-spark .db-spark-dot { background: #111A2B; }

  /* ── Focused panes (Basics / Stock / Page / Details) ───────────────────────
     Panes are plain show/hide; every field inside is the same node (same id,
     same wiring) as the old one-scroll form. pdTab() swaps the active pane.
     The tab bar is the same raised-pill segmented control used for the sales
     range and description-mode switches elsewhere in this editor (not a new
     pattern) — a cream track with a white, shadowed pill on the active tab,
     instead of a thin underline that was easy to miss. ─────────────────── */
  #pd-editor .pd-tabs {
    display: flex; gap: 3px; margin: 4px 0 20px; padding: 4px;
    background: #F6F3EC; border: 1px solid #E4E0D5; border-radius: 12px;
    overflow-x: auto; scrollbar-width: none;
  }
  #pd-editor .pd-tabs::-webkit-scrollbar { display: none; }
  #pd-editor .pd-tab {
    flex: 1; border: 0; background: none; cursor: pointer; white-space: nowrap;
    font: 500 13px 'Inter', sans-serif; letter-spacing: 0; text-transform: none;
    color: #58606E; padding: 9px 10px; border-radius: 9px;
    display: inline-flex; align-items: center; justify-content: center; gap: 6px;
    transition: background .15s var(--ease-out), color .15s var(--ease-out);
  }
  #pd-editor .pd-tab svg { width: 15px; height: 15px; flex: none; opacity: .75; }
  #pd-editor .pd-tab:hover { color: #111A2B; }
  #pd-editor .pd-tab.is-active {
    background: #FFFFFF; color: #111A2B;
    box-shadow: 0 1px 3px rgba(17,26,43,.1), 0 0 0 1px rgba(17,26,43,.03);
  }
  #pd-editor .pd-tab.is-active svg { opacity: 1; }
  @media (max-width: 560px) { #pd-editor .pd-tab-txt { display: none; } }
  #pd-editor .pd-pane { display: none; }
  #pd-editor .pd-pane.is-active { display: block; animation: pdPaneIn .28s var(--ease-out) both; }
  @keyframes pdPaneIn { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: none; } }
  /* Even breathing room between a pane's top-level blocks; tighter between the
     paired Name and Price rows. */
  #pd-editor .pd-pane > * + * { margin-top: 20px; }
  #pd-editor .pd-pane > .pd-row2 + .pd-row2 { margin-top: 14px; }
  #pd-editor .pd-fieldstack > .field + .field { margin-top: 12px; }
  /* Availability: the publish switch and Status sit side by side up top. */
  #pd-editor .pd-avail { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; align-items: stretch; }
  #pd-editor .pd-avail-status { justify-content: center; }
  @media (max-width: 880px) { #pd-editor .pd-avail { grid-template-columns: 1fr; } }

  /* Sourcing choice (Stock pane): two clickable cards standing in for the
     single pd-ship-from-supplier checkbox, so the choice and its effect
     (stock matrix vs supplier note below) sit on the same screen. */
  #pd-editor .pd-source-choice { display: flex; flex-direction: column; gap: 9px; }
  #pd-editor .pd-radio-card {
    display: flex; gap: 11px; align-items: flex-start; text-align: left;
    padding: 12px 14px; border: 1px solid #E4E0D5; border-radius: 10px;
    background: #FFFFFF; cursor: pointer; font: inherit; width: 100%;
    transition: border-color .15s var(--ease-out), background .15s var(--ease-out);
  }
  #pd-editor .pd-radio-card:hover { border-color: #58606E; }
  #pd-editor .pd-radio-card.is-on { border-color: #111A2B; background: #F6F3EC; }
  #pd-editor .pd-radio-dot {
    width: 17px; height: 17px; border-radius: 50%; border: 1.5px solid #E4E0D5;
    flex: none; margin-top: 1px; position: relative; transition: border-color .15s var(--ease-out);
  }
  #pd-editor .pd-radio-card.is-on .pd-radio-dot { border-color: #111A2B; }
  #pd-editor .pd-radio-card.is-on .pd-radio-dot::after {
    content: ""; position: absolute; inset: 3px; border-radius: 50%; background: #111A2B;
  }
  #pd-editor .pd-radio-txt b { display: block; font-weight: 500; font-size: 13.5px; color: #111A2B; }
  #pd-editor .pd-radio-txt span { display: block; font-size: 12px; color: #58606E; margin-top: 2px; line-height: 1.45; }

  /* Tab glance chips — a live hint of what's inside a pane without opening it. */
  #pd-editor .pd-tab-cnt {
    font-size: 11px; font-weight: 600; color: #58606E;
    font-variant-numeric: tabular-nums;
  }
  #pd-editor .pd-tab.is-active .pd-tab-cnt { color: #111A2B; }
  #pd-editor .pd-tab-chip {
    font-size: 10px; font-weight: 600; letter-spacing: .04em; text-transform: uppercase;
    color: #4C468A; background: #EEEDF6; border-radius: 999px; padding: 2px 7px;
  }

  /* ── Editor bento: every tab's cards reflow with the editor's width ─────────
     Direction A (reference/product-editor-all-tabs-bento-mockup.html). A pane is
     a size container, so its cards react to their OWN rendered width — which
     changes with Expand and with the app sidebar folding — no JS. Cards flow
     1 col → 2 cols → a tailored 12-col grid; fields inside subdivide via auto-fit
     so nothing stretches into a long line. All classes are pdb-* to dodge the
     base retail-admin class collisions.

     PAGE is the exception: its rich-text editor goes position:fixed for
     fullscreen, and container-type would make the pane the containing block for
     that fixed element (trapping fullscreen inside the card). So Page skips the
     container and uses an intrinsic auto-fit grid instead. */
  #pd-editor .pd-pane[data-pane="basics"],
  #pd-editor .pd-pane[data-pane="stock"],
  #pd-editor .pd-pane[data-pane="details"] { container-type: inline-size; }

  #pd-editor .pdb-grid { display: grid; gap: 14px; align-items: start; grid-template-columns: 1fr; }
  @container (min-width: 600px) {
    #pd-editor .pdb-grid { grid-template-columns: 1fr 1fr; }
    #pd-editor .pdb-c12 { grid-column: 1 / -1; }
  }
  @container (min-width: 980px) {
    #pd-editor .pdb-grid { grid-template-columns: repeat(12, 1fr); }
    #pd-editor .pdb-c4 { grid-column: span 4; }
    #pd-editor .pdb-c5 { grid-column: span 5; }
    #pd-editor .pdb-c6 { grid-column: span 6; }
    #pd-editor .pdb-c7 { grid-column: span 7; }
    #pd-editor .pdb-c8 { grid-column: span 8; }
    #pd-editor .pdb-c12 { grid-column: 1 / -1; }
  }
  /* Page — intrinsic reflow, no container (keeps RTE fullscreen viewport-fixed). */
  #pd-editor .pdb-grid-auto {
    display: grid; gap: 14px; align-items: start;
    grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
  }

  #pd-editor .pdb-card {
    background: #FFFFFF; border: 1px solid #E4E0D5; border-radius: 14px;
    padding: 16px 17px; display: flex; flex-direction: column; gap: 14px; min-width: 0;
  }
  #pd-editor .pdb-card > .pd-sec-h, #pd-editor .pdb-card > .pd-card-h { margin: 0; }
  /* Fields subdivide with the card via auto-fit (intrinsic — no container needed).
     min 250px keeps the semantic pairs (Name ↔ translation, Price ↔ Was) two-up
     across the common width range and collapses to one column when narrow. */
  #pd-editor .pdb-fields {
    display: grid; gap: 13px; align-items: start;
    grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
  }
  #pd-editor .pdb-fields .field { margin: 0; min-width: 0; }
  /* Publish switch sits as a plain row inside the card, not a nested white tile. */
  #pd-editor .pdb-avail .pd-pub { background: transparent; border-radius: 0; padding: 0; font-weight: 500; }
  #pd-editor .pdb-photos .pdb-photo-actions { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
  /* Variant / bundle / option rows sit below the Stock grid, full width. */
  #pd-editor .pdb-belowgrid { margin-top: 14px; }

  /* Stock tab — Variants / Bundle / Add-ons / Options as a 2-up card grid rather
     than full-bleed rows. As full-page rows their summary flew to the far edge
     (the original wasted-space complaint). Two columns when the editor is wide;
     an OPEN section spans the full width so its matrix / radios get the room they
     need (open state is the toggle's aria-expanded — same for all four). Each
     .pd-variants box is JS-rendered; :empty hides any that render nothing. */
  #pd-editor .pdb-vgrid { display: grid; gap: 12px; grid-template-columns: 1fr; align-items: start; }
  @container (min-width: 760px) { #pd-editor .pdb-vgrid { grid-template-columns: 1fr 1fr; } }
  #pd-editor .pdb-vgrid > .pd-variants {
    border: 1px solid #E4E0D5; border-radius: 14px; background: #FFFFFF;
    padding: 0 14px; align-self: start; min-width: 0; transition: border-color .12s ease;
  }
  #pd-editor .pdb-vgrid > .pd-variants:empty { display: none; }
  #pd-editor .pdb-vgrid > .pd-variants:hover { border-color: #C9C3B4; }
  @container (min-width: 760px) {
    #pd-editor .pdb-vgrid > .pd-variants:has(.pd-var-toggle[aria-expanded="true"]) {
      grid-column: 1 / -1; padding-bottom: 14px;
    }
  }
  /* Toggle becomes the card header — drop the full-bleed hairline-row look. */
  #pd-editor .pdb-vgrid .pd-var-toggle { border-top: none; padding: 15px 3px; }
  #pd-editor .pdb-vgrid .pd-var-toggle:hover { background: transparent; border-top-color: transparent; }

  /* ═══════════════════════════════════════════════════════════════════
     TOOLTIP — the shared one
     ═══════════════════════════════════════════════════════════════════
     One bubble for the whole product, driven by data-tt on the trigger.
     Controller lives in 01-core.js; it appends a single .tt-pop to <body>
     and positions it. Everything here is appearance only — placement,
     flipping and viewport clamping are all done in JS, which is why there
     is no side-placement rule to be found: there is no side placement.

     The bubble INVERTS against the page. --td-text and --td-paper both flip
     with the theme, so this reads Ink-on-Paper in light and Paper-on-Ink in
     dark — the same pairing the spark tips already use. Do NOT hardcode
     #111A2B here: on a dark page the bubble would go invisible. */
  .tt-pop {
    position: fixed; top: 0; left: 0;
    z-index: 100002;                 /* above the mobile full-screen overlay */
    max-width: 250px;
    padding: 9px 12px;
    background: var(--td-text, #111A2B);
    color: var(--td-paper, #FBF9F4);
    font: 400 12px/1.45 var(--f-sans);
    letter-spacing: 0; text-transform: none; text-align: left;
    border: 0;
    border-radius: var(--r-lg, 10px);
    box-shadow: 0 4px 12px rgba(17,26,43,.08), 0 12px 32px rgba(17,26,43,.18);
    white-space: normal; overflow-wrap: break-word;
    pointer-events: none;
    visibility: hidden; opacity: 0;
    transform: translateY(4px);
    transition: opacity .16s ease, transform .16s ease, visibility 0s linear .16s;
  }
  /* Quiet fade and a small rise — it settles into place rather than popping. */
  .tt-pop.on {
    visibility: visible; opacity: 1;
    transform: translateY(0);
    transition: opacity .16s ease, transform .16s ease, visibility 0s;
  }
  /* Arrow on the edge nearest the element, flipping with the placement. Its
     horizontal position is --tt-ax, set by JS off the ELEMENT's centre — so
     when the bubble shifts sideways to stay on screen the arrow stays put,
     still pointing at the thing being explained. */
  .tt-pop::after {
    content: ''; position: absolute;
    left: var(--tt-ax, 50%); margin-left: -5px;
    width: 0; height: 0;
    border: 5px solid transparent;
  }
  .tt-pop[data-place="above"]::after {
    top: 100%;
    border-bottom-width: 0;
    border-top-color: var(--td-text, #111A2B);
  }
  .tt-pop[data-place="below"]::after {
    bottom: 100%;
    border-top-width: 0;
    border-bottom-color: var(--td-text, #111A2B);
  }
  @media (prefers-reduced-motion: reduce) {
    .tt-pop, .tt-pop.on { transform: none; transition: opacity .01s linear, visibility 0s; }
  }
  /* Touch tap target — OPT IN, on the triggers that actually need it.
     A bare icon is smaller than a finger, so it gets a 44px hit area without
     growing on screen. Add .tt-hit alongside data-tt for those.
     Deliberately NOT applied to every [data-tt]: a trigger that is already a
     real control (a filter chip, a button with a label) is comfortably
     tappable, and forcing 44px on it would change layouts product-wide.
     Phone chip sizing is a decision for the mobile pass, made once across the
     whole admin — not something a tooltip settles on its way past. */
  @media (pointer: coarse) {
    .tt-hit { position: relative; }
    .tt-hit::before {
      content: ''; position: absolute;
      top: 50%; left: 50%; transform: translate(-50%, -50%);
      width: max(100%, 44px); height: max(100%, 44px);
    }
  }

  /* ══════════════════════════════════════════════════════════════════════
     DELIVERY POINT — the confirm surface in the order drawer.
     docs/tacet-delivery-point-confirmation-spec.md §5. Shown only on an
     order whose push is parked in needs_point.

     The governing idea is in the marker: a coarse match is an AREA, not a
     point, so until an operator places one there is no pin on the map at
     all — only the area — and there is nothing to confirm. That is why the
     primary button starts disabled reading "Place the point" rather than
     offering a one-tap confirm of a district centre.
     ══════════════════════════════════════════════════════════════════════ */
  #order-drawer .qp { margin-top: var(--s3); padding-top: var(--s3); border-top: 1px dashed var(--line-soft); }
  #order-drawer .qp-h { font-size: 17px; font-weight: 600; letter-spacing: -.01em; margin: 0; color: var(--text-1); }
  #order-drawer .qp-sub { font-size: 13.5px; line-height: 1.6; color: var(--text-2); margin: 6px 0 14px; }

  /* Address ABOVE the map on a phone: the operator reads the words first and
     then looks for them on the map, which is the order the task happens in.
     On desktop the map takes the left column and the address sits beside it,
     both in view at once, so neither has to come "first". */
  #order-drawer .qp-grid { display: grid; grid-template-columns: 1fr; gap: 14px; }
  #order-drawer .qp-addr-col { order: 1; }
  #order-drawer .qp-map-col  { order: 2; }
  @media (min-width: 721px) {
    #order-drawer .qp-grid { grid-template-columns: 1.35fr 1fr; align-items: start; }
    #order-drawer .qp-map-col  { order: 1; }
    #order-drawer .qp-addr-col { order: 2; }
  }
  #order-drawer .qp-grid > * { min-width: 0; }

  #order-drawer .qp-addr {
    font-size: 15px; line-height: 1.7; color: var(--text-1);
    user-select: text; -webkit-user-select: text; overflow-wrap: anywhere;
  }
  #order-drawer .qp-grade { font-size: 12.5px; color: var(--text-3); margin-top: 10px; line-height: 1.6; }

  /* Address search — it MOVES THE MAP and does nothing else.
     Above the map, because finding the street is how you get to the point and
     the map is where you finish. Picking a result recentres and zooms; it
     draws no pin, leaves the state line reading "No point placed yet" and
     leaves the primary button disabled. There is no styling here for a
     selected state, because selecting a result is not a step that completes —
     it is a step that repositions. */
  #order-drawer .qp-find { position: relative; margin-bottom: 8px; }
  #order-drawer .qp-find-in { width: 100%; }
  /* Overlays the map rather than pushing it down: a list that reflowed the
     surface would move the map out from under the operator mid-search. */
  #order-drawer .qp-find-out {
    position: absolute; z-index: 30; left: 0; right: 0; top: calc(100% + 4px);
    max-height: 232px; overflow-y: auto;
    background: var(--bg-pane); border: 1px solid var(--line);
    border-radius: var(--r-lg); box-shadow: var(--sh-2);
  }
  #order-drawer .qp-find-out button {
    display: block; width: 100%; text-align: left; cursor: pointer;
    padding: 9px 11px; background: none; border: 0;
    border-bottom: 1px solid var(--line-soft);
  }
  #order-drawer .qp-find-out button:last-child { border-bottom: 0; }
  #order-drawer .qp-find-out button:hover { background: var(--bg-elev-1); }
  #order-drawer .qp-find-out .n { display: block; font-size: 13.5px; color: var(--text-1); }
  #order-drawer .qp-find-out .a {
    display: block; font-size: 12px; line-height: 1.5; color: var(--text-3);
    margin-top: 2px; overflow-wrap: anywhere;
  }
  /* Searching / no match / unavailable — a sentence, never an icon state. */
  #order-drawer .qp-find-note { padding: 10px 11px; font-size: 12.5px; line-height: 1.55; color: var(--text-3); }

  #order-drawer .qp-map {
    position: relative; height: 240px; border-radius: 10px; overflow: hidden;
    border: 1px solid var(--line); background: var(--bg-elev-1);
  }
  #order-drawer .qp-map-fallback {
    display: flex; align-items: center; justify-content: center; text-align: center;
    padding: 18px; font-size: 13px; line-height: 1.6; color: var(--text-2);
  }
  /* Says what the point is, and changes when it becomes one. Sits under the
     map so it reads as a caption on the state, not as a label on the pin. */
  #order-drawer .qp-state { font-size: 12.5px; color: var(--tacet-graphite, var(--text-2)); margin-top: 8px; }

  /* ── PICK A PLACE — the sibling surface ──────────────────────────────────
     Shown when the name search found several places and could not choose. It
     borrows the point surface's frame (.qp, .qp-h, .qp-sub, .qp-grid) on
     purpose: same question shape, same place in the drawer, so an operator who
     has seen one reads the other without relearning it. The list simply takes
     the column the map takes there.

     NOTHING IS PRE-SELECTED, and there is no styling here for a selected
     state — because there is no selected state. Each row IS the action; a
     highlighted default with a Confirm under it would be a guess wearing a
     decision's clothes, which is what the pinless map next door exists to
     prevent. Whatever is styled here has to be reachable by choosing, and
     choosing ends the surface. */
  #order-drawer .qpc-list { display: flex; flex-direction: column; gap: 8px; }
  #order-drawer .qpc-opt {
    display: flex; align-items: flex-start; gap: 10px; width: 100%; text-align: left;
    padding: 11px 12px; cursor: pointer;
    background: var(--bg-pane); border: 1px solid var(--line); border-radius: var(--r-lg);
    transition: background .15s ease, border-color .15s ease;
  }
  #order-drawer .qpc-opt:hover { background: var(--bg-elev-1); border-color: var(--line-strong, var(--line)); }
  #order-drawer .qpc-opt:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; }
  #order-drawer .qpc-opt[disabled] { opacity: .5; cursor: progress; }
  #order-drawer .qpc-opt-main { flex: 1; min-width: 0; }
  #order-drawer .qpc-name {
    display: block; font-size: 14.5px; font-weight: 600; color: var(--text-1);
    line-height: 1.4; overflow-wrap: anywhere;
  }
  #order-drawer .qpc-addr {
    display: block; font-size: 12px; line-height: 1.5; color: var(--text-3);
    margin-top: 3px; overflow-wrap: anywhere;
  }
  /* How far the rider goes. Tabular so three rows compare down the column at a
     glance — the wrong-district hit is spotted by the number being unlike the
     others, which only works if they line up. Never coloured by threshold: the
     server refuses past the limit, and an amber number here would imply this
     surface enforces something it does not. */
  #order-drawer .qpc-km {
    flex: none; font-size: 12.5px; color: var(--text-2);
    font-variant-numeric: tabular-nums; padding-top: 1px;
  }
  #order-drawer .qpc-none { font-size: 12.5px; color: var(--text-3); margin-top: 10px; line-height: 1.6; }
  #order-drawer .qpc-link {
    background: none; border: 0; padding: 0; cursor: pointer;
    font: inherit; color: var(--brand); text-decoration: underline;
  }
  #order-drawer .qpc-link[disabled] { opacity: .5; cursor: progress; }

  #order-drawer .qp-actions { display: flex; gap: 10px; flex-wrap: wrap; align-items: center; margin-top: 14px; }
  #order-drawer .qp-actions .btn[disabled] { opacity: .5; cursor: not-allowed; }
  #order-drawer .qp-hint { font-size: 12px; color: var(--text-3); }

  /* Expiring tomorrow — a deadline in a sentence. Amber, because it needs
     attention; never brick, because nothing has failed yet. No countdown. */
  #order-drawer .qp-urgent {
    display: flex; align-items: flex-start; gap: 9px; margin-top: 12px;
    padding: 10px 12px; border-radius: 8px;
    background: var(--tacet-amber-tint, #F7EEDE); color: var(--tacet-amber-ink, #8A6425);
    border-left: 3px solid var(--tacet-amber, #C9954A);
    font-size: 13px; line-height: 1.55;
  }
  #order-drawer .qp-urgent svg { flex: none; margin-top: 1px; }

  /* Reschedule: a plain radio list, not a new date-picker component. */
  #order-drawer .qp-dates { display: flex; flex-direction: column; gap: 2px; margin: 12px 0 4px; }
  #order-drawer .qp-date {
    display: flex; align-items: center; gap: 10px; padding: 9px 11px;
    border: 1px solid var(--line); border-radius: 8px; background: var(--bg-pane); cursor: pointer;
  }
  #order-drawer .qp-date:has(input:checked) { border-color: var(--text-1); }
  #order-drawer .qp-date .d { font-weight: 700; font-variant-numeric: tabular-nums; }
  /* The blast-radius confirm. Rescheduling changes what the customer was
     promised, so it names both days before it happens — unlike confirming a
     point, where the map itself is the confirmation. */
  #order-drawer .qp-confirm {
    margin-top: 12px; padding: 13px 14px; border-radius: 10px;
    border: 1px solid var(--tacet-amber, #C9954A);
    background: var(--tacet-amber-tint, #F7EEDE); color: var(--tacet-amber-ink, #8A6425);
    font-size: 13.5px; line-height: 1.6;
  }
  #order-drawer .qp-confirm .swap { font-weight: 700; font-variant-numeric: tabular-nums; }

/* ===========================================================================
   ORDERS WAITING FOR A DELIVERY POINT — dashboard worklist (spec section 7)
   A .bn-card at BLOCK level, Variant C at ROW level: plain rows, rules between
   them, no box drawn around each row.
   The first build left the block unboxed on a literal reading of "no card box"
   in section 7. The survey that followed showed Variant C is this sheet's idiom
   for ROWS (.bx-in-row, .td-attn-row both are it) and that every dashboard
   block is a card, so an unboxed block read as broken rather than deliberate —
   the opposite of what the rule was for. Overruled 2026-08-08. Do not strip the
   card back off thinking you are restoring the original intent.
   Spacing matches .td-needs, the other block that sits outside the grid.
   All classes are dp-work-* prefixed — the base sheet owns .opt/.k/.v/.n/.ico
   and a bare child class here would collide with them.
   No Dusk anywhere: Dusk means the system invoking itself, and nothing in this
   block is the system acting. The day-before warning is Amber, which is what
   this sheet reserves for status and attention.
   =========================================================================== */
  .dp-work { margin-bottom: 16px; }
  .dp-work-list { display: grid; }
  /* The row is a button so it is reachable by keyboard and announces itself as
     activatable; stripped back to look like a line of text, because it is one. */
  .dp-work-row {
    display: flex; align-items: baseline; gap: 12px; width: 100%;
    padding: 10px 0; border: 0; background: none; text-align: left;
    font: 400 13.5px var(--f-sans); color: var(--td-text); cursor: pointer;
  }
  .dp-work-row + .dp-work-row { border-top: 1px solid var(--td-border); }
  .dp-work-row:hover .dp-work-no { text-decoration: underline; }
  .dp-work-row:focus-visible { outline: 2px solid var(--td-amber-ink); outline-offset: 2px; }
  /* Figures are 700 and tabular so a column of order numbers lines up. */
  .dp-work-no {
    font-weight: 700; font-variant-numeric: tabular-nums; color: var(--td-text);
    flex: none;
  }
  .dp-work-who {
    min-width: 0; flex: 1 1 auto; color: var(--td-text2); font-size: 13px;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  }
  .dp-work-when {
    margin-left: auto; flex: none; font-size: 12.5px; color: var(--td-text2);
    font-variant-numeric: tabular-nums; white-space: nowrap;
  }
  /* Promised tomorrow. The only marked state, and the only place the section 5.2
     day-before warning appears outside the drawer now that stage 4 is declined. */
  .dp-work-when.is-tomorrow {
    color: var(--td-amber-ink); font-weight: 600;
    background: var(--td-amber-tint); border-radius: 999px; padding: 2px 9px;
  }
  .dp-work-foot {
    margin: 10px 0 0; font-size: 13px; color: var(--td-text2);
  }
