/* GENERATED by scripts/derive-workspace-shell.js from merchant-v2.html's <style> block - DO NOT EDIT. Fix merchant-v2.html and re-derive.
   The SOKONI role-workspace look (U5): every role dashboard wears the merchant-v2 shell. */
:root{
  --bg:#050505;
  --surface:#0d0d0d;
  --surface-2:#141414;
  --line:rgba(255,255,255,.09);
  --txt:#f4f4f4;
  --txt2:#a8a8a8;
  --txt3:#6d6d6d;
  --acc:#71ff00;
  /* ── TOKENS THE MOUNTED MODULES DECLARE ──────────────────────────────────
     sokoni-merchant-sell.js styles its sheet with background:var(--panel) and
     its product cards with var(--card). This shell defined neither — it names
     the same surfaces --surface and --surface-2 — so both resolved to NOTHING
     and rendered TRANSPARENT. That is the reported "payment sheet has no
     background", and a transparent sheet is also why tapping inside it felt
     like nothing happened: there was no visible surface to tap.

     Aliased rather than restyled, so the shell keeps ONE palette and the module
     keeps its own vocabulary. A module is entitled to the tokens it declares —
     the same contract as supplying SokoniCash or SokoniReceiptDoc. */
  --panel: var(--surface);
  --card:  var(--surface-2);
  --acc-dim:rgba(113,255,0,.12);
  --acc-line:rgba(113,255,0,.32);
  --danger:#ff5252;

  --safe-top:env(safe-area-inset-top,0px);
  --safe-bot:env(safe-area-inset-bottom,0px);
  --safe-l:env(safe-area-inset-left,0px);
  --safe-r:env(safe-area-inset-right,0px);

  --header-h:56px;
  --bnav-h:60px;
  --rail-w:232px;
  --rail-collapsed:64px;   /* icon-only rail — matches v1 */
}
*,*::before,*::after{box-sizing:border-box}
html,body{margin:0;padding:0}
html{background:var(--bg);-webkit-text-size-adjust:100%}
body{
  background:var(--bg);color:var(--txt);
  font:400 15px/1.45 -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif;
  /* Never let a module or a wide table scroll the whole application sideways.
     Wide content scrolls inside its own container instead. */
  overflow-x:hidden;
  min-height:100vh;
  min-height:100dvh;   /* dvh so the mobile URL bar cannot crop the bottom nav */
}
button{font:inherit;color:inherit;background:none;border:none;padding:0;cursor:pointer}
a{color:inherit;text-decoration:none}

/* ── APP FRAME ───────────────────────────────────────────────────────────────
   Phone: one column, header fixed on top, bottom nav fixed underneath.
   Desktop (>=900px): a two-column grid, sidebar rail + content. */
.app{
  min-height:100vh;min-height:100dvh;
  padding-top:calc(var(--header-h) + var(--safe-top));
  padding-bottom:calc(var(--bnav-h) + var(--safe-bot));
  padding-left:var(--safe-l);padding-right:var(--safe-r);
}

/* ── HEADER ──────────────────────────────────────────────────────────────────*/
.hdr{
  position:fixed;top:0;left:0;right:0;z-index:40;
  height:calc(var(--header-h) + var(--safe-top));
  padding:var(--safe-top) 12px 0;
  display:flex;align-items:center;gap:10px;
  background:rgba(5,5,5,.92);
  -webkit-backdrop-filter:blur(12px);backdrop-filter:blur(12px);
  border-bottom:1px solid var(--line);
}
.hdr-btn{
  width:38px;height:38px;flex:0 0 auto;border-radius:11px;
  display:flex;align-items:center;justify-content:center;
  border:1px solid var(--line);background:var(--surface);font-size:16px;
}
.hdr-btn:active{transform:scale(.94)}
.hdr-title{
  font-size:16px;font-weight:800;letter-spacing:-.01em;
  /* The title is the flexible element: it truncates so the actions never get
     pushed off-screen on a narrow phone. */
  flex:1 1 auto;min-width:0;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
}
.hdr-actions{display:flex;align-items:center;gap:8px;flex:0 0 auto}
.hdr-shop{
  display:none;align-items:center;gap:8px;height:38px;padding:0 12px;
  border-radius:11px;background:var(--acc-dim);border:1px solid var(--acc-line);
  color:var(--acc);font-size:12.5px;font-weight:800;max-width:190px;
}
.hdr-shop span{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
@media (min-width:560px){ .hdr-shop{display:flex} }

/* HEADER PRINTER CHIP — the shell owns the printer, so its state is global and
   belongs in the top bar. Hidden on the narrowest phones where the title and the
   two icon buttons already fill the row; the sidebar chip still carries it there. */
.hdr-chip{
  display:none;align-items:center;gap:6px;height:38px;padding:0 11px;
  border-radius:11px;border:1px solid var(--line);background:var(--surface);
  font-size:12px;font-weight:700;color:var(--txt2);white-space:nowrap;
}
@media (min-width:420px){ .hdr-chip{display:flex} }

/* ── SIDEBAR ─────────────────────────────────────────────────────────────────
   Phone: off-canvas drawer. Desktop: static rail. ONE markup tree either way. */
.side{
  position:fixed;top:0;bottom:0;left:0;z-index:60;
  width:min(84vw,var(--rail-w));
  background:var(--surface);border-right:1px solid var(--line);
  transform:translateX(-100%);
  transition:transform .22s cubic-bezier(.4,0,.2,1);
  /* THE FIX for v1's clipped row: a flex column whose scrolling child is allowed to
     shrink. Without min-height:0 the child refuses to shrink below its content and
     the overflow is clipped by the parent instead of scrolled. */
  display:flex;flex-direction:column;min-height:0;
  padding-top:var(--safe-top);padding-bottom:var(--safe-bot);
}
.side.open{transform:translateX(0)}
/* BRAND — the real SOKONI mark, and the entry point to the shop/branch switcher.
   A button, not a div: it is the same control v1 exposes as "Switch shop / branch". */
.side-head{
  flex:0 0 auto;width:100%;display:flex;align-items:center;gap:10px;
  padding:13px 12px;border-bottom:1px solid var(--line);text-align:left;
}
.side-head:hover{background:rgba(255,255,255,.04)}
.side-logo{
  width:36px;height:36px;border-radius:10px;flex:0 0 auto;object-fit:contain;
  background:var(--acc-dim);border:1px solid var(--acc-line);padding:3px;
  transition:width .18s,height .18s;
}
.side-shop{min-width:0;flex:1 1 auto;transition:opacity .18s}
.side-shop b{display:block;font-size:13.5px;font-weight:800;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.side-shop small{display:block;font-size:11px;color:var(--txt3)}
.side-caret{flex:0 0 auto;color:var(--txt3);font-size:11px;transition:opacity .18s}

/* STATUS CHIPS — printer / sync / identity. Live state, never decorative:
   the dot colour is driven by paintStatus() below, so a dead printer reads as dead. */
.side-status{
  flex:0 0 auto;border-top:1px solid var(--line);
  padding:10px 14px;display:flex;flex-direction:column;gap:8px;
}
.chip{display:flex;align-items:center;gap:8px;font-size:11.5px;font-weight:700;color:var(--txt2);white-space:nowrap;min-width:0}
.dot{width:8px;height:8px;border-radius:50%;background:#555;flex:0 0 auto}
.dot.ok{background:var(--acc);box-shadow:0 0 7px var(--acc)}
.dot.bad{background:var(--danger)}
.dot.warn{background:#ffb400;box-shadow:0 0 7px rgba(255,180,0,.7)}
.chip .ctxt{min-width:0;overflow:hidden;text-overflow:ellipsis;transition:opacity .18s}

/* The scrolling region. min-height:0 is what makes it scroll instead of clip. */
.side-scroll{flex:1 1 auto;min-height:0;overflow-y:auto;overscroll-behavior:contain;padding:8px 8px 4px}
.side-group{margin:10px 6px 4px;font-size:10.5px;font-weight:800;letter-spacing:.09em;text-transform:uppercase;color:var(--txt3)}
.nav-item{
  width:100%;display:flex;align-items:center;gap:11px;
  padding:10px 11px;border-radius:11px;margin-bottom:2px;
  font-size:14px;font-weight:600;color:var(--txt2);text-align:left;
}
.nav-item .ico{flex:0 0 auto;width:20px;text-align:center;font-size:15px}
.nav-item .lbl{min-width:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.nav-badge{margin-left:auto;min-width:20px;height:20px;padding:0 6px;border-radius:10px;background:var(--acc);color:#050505;font-size:11px;font-weight:800;line-height:20px;text-align:center;flex:none}
.nav-badge[hidden]{display:none}
#bnav button{position:relative}#bnav .nav-badge{position:absolute;top:4px;right:calc(50% - 22px);margin:0}
.nav-item:hover{background:rgba(255,255,255,.05);color:var(--txt)}
.nav-item.on{background:var(--acc-dim);color:var(--acc);border:1px solid var(--acc-line);padding:9px 10px}

/* The exit sits in the footer so it can never be mistaken for an in-shell route. */
.side-foot{flex:0 0 auto;border-top:1px solid var(--line);padding:8px}
.side-foot .nav-item{color:var(--txt3)}
.scrim{
  position:fixed;inset:0;z-index:50;background:rgba(0,0,0,.55);
  opacity:0;pointer-events:none;transition:opacity .22s;
}
.scrim.on{opacity:1;pointer-events:auto}

/* ── CONTENT ─────────────────────────────────────────────────────────────────*/
.main{ padding:14px 14px 24px; max-width:1180px; }
.panel{display:none}
.panel.show{display:block}

/* A NATIVE module that hosts its own scroller needs a definite height for the
   same reason .frame-wrap below does. Without one, `.panel.show` is display:block
   with auto height, so a module's `height:100%` resolves against an auto-height
   ancestor and becomes auto: the module's column grows to its content, its
   internal `overflow-y:auto` body never engages, the whole document scrolls
   instead, and anything the module pins to its own bottom edge — the Quick Sell
   charge bar — ends up below the fold.

   Measured before this rule, at 390x844 AND 412x915: .msl was 2282px tall against
   an 844px viewport, bodyScrollable=false, docScrolls=true, and the charge bar sat
   at y=2908 — about 2000px past the fold. That is the "hunt for Charge after
   scrolling" report, and it is why the module certified green in isolation: a
   height-constrained test host recreates the fix and hides the defect.

   Scoped to hosts that opt in, so panels which legitimately scroll with the page
   are untouched. */
/* The panel ITSELF is the host: renderModule() mounts a module straight into
   #panel-<id>, so there is no inner wrapper to size. An earlier version of this
   rule targeted `.panel.show > #native-sell` — an element the shell never
   creates — so it matched nothing in production while a test harness that built
   that wrapper itself passed. The class is added by renderModule, so only module
   surfaces are constrained and panels that legitimately scroll with the page are
   untouched. */
/* The height below subtracts 14px: #content starts 14px under the header (.frame-wrap cancels that with margin:-14px, this panel does not), so without it the panel's bottom
   lay 14px BEHIND the bottom nav (806 vs 792 at 852px; 621 vs 607 at 667px) and the last rows could not be scrolled into view. */
.panel.show.panel-scroll{
  display:flex; flex-direction:column;
  /* The module is mounted INTO this element, so everything it renders is a flex item —
     and a flex item shrinks. With a definite height and content taller than it, the short
     rows were crushed: the Products filter chips measured 4px tall while the chip inside
     them was 20px, so they spilled out under the next row and read as one control
     overlapping another. Rows keep their natural height; the panel scrolls instead.
     A module that fills the panel (height:100%, or flex:1 with a 0 basis) still fills it:
     this removes SHRINK, not GROW. */
  overflow-y:auto; -webkit-overflow-scrolling:touch;
  height:calc(100vh - var(--header-h) - var(--safe-top) - var(--bnav-h) - var(--safe-bot) - 14px);
  height:calc(100dvh - var(--header-h) - var(--safe-top) - var(--bnav-h) - var(--safe-bot) - 14px);
  min-height:0;
}
.panel.show.panel-scroll > *{ flex-shrink:0; }
/* An iframe module fills the viewport minus the chrome. It is the ONLY thing that
   gets a fixed height, because it hosts its own scrolling document. */
.frame-wrap{
  position:relative;
  height:calc(100vh - var(--header-h) - var(--safe-top) - var(--bnav-h) - var(--safe-bot));
  height:calc(100dvh - var(--header-h) - var(--safe-top) - var(--bnav-h) - var(--safe-bot));
  margin:-14px -14px -24px;
}
.frame-wrap iframe{display:block;width:100%;height:100%;border:0;background:var(--bg)}
.frame-load{
  position:absolute;inset:0;display:flex;align-items:center;justify-content:center;
  color:var(--txt3);font-size:13px;background:var(--bg);
}
.frame-wrap.loaded .frame-load{display:none}
/* While the framed POS shows a dialog or wizard (Retrieve, Refund, payment, shift…), the frame covers the whole window so
   nothing it opens is boxed in by the dashboard (owner 2026-10-05; the POS reports it via {__sokoniPosOverlay, open}). */
.frame-wrap.frame-wrap--overlay{position:fixed;inset:0;margin:0;height:100vh;height:100dvh;z-index:100000}

/* ── AVAILABILITY EDITOR ─────────────────────────────────────────────────────*/
.av-day{padding:12px 0;border-bottom:1px solid var(--line)}
.av-day:last-child{border-bottom:0}
.av-day-h{display:flex;align-items:center;justify-content:space-between;gap:10px}
.av-day-n{display:flex;align-items:center;gap:9px;font-size:13.5px;font-weight:700;cursor:pointer}
.av-day-n input{width:18px;height:18px;accent-color:var(--acc);cursor:pointer}
.av-closed{font-size:11.5px;font-weight:700;color:var(--txt3);text-transform:uppercase;letter-spacing:.06em}
.av-p{display:flex;align-items:center;gap:8px;margin-top:9px;padding-left:27px}
.av-t{min-height:40px;padding:7px 10px;border-radius:10px;font:inherit;font-size:14px;
      background:rgba(255,255,255,.05);border:1px solid var(--line);color:inherit;color-scheme:dark}
.av-to{font-size:12px;color:var(--txt3)}
.av-add{min-height:34px;padding:0 12px;border-radius:9px;cursor:pointer;font:inherit;font-size:12px;
        font-weight:800;background:rgba(113,255,0,.12);border:1px solid rgba(113,255,0,.3);color:var(--acc)}
.av-rm{width:34px;height:34px;flex:0 0 34px;border-radius:9px;cursor:pointer;font:inherit;
       background:transparent;border:1px solid var(--line);color:var(--txt3)}
.av-ov{display:flex;align-items:center;gap:10px;padding:10px 0;border-bottom:1px solid var(--line)}
.av-ov:last-of-type{border-bottom:0}
.av-ov b{font-size:13.5px}
.av-ovs{flex:1;font-size:11.5px;color:var(--txt3)}
.av-addov{display:flex;flex-wrap:wrap;gap:8px;align-items:center;margin-top:12px}
.av-day-h select.av-t,.av-day-h input.av-t{max-width:48%;min-width:0}
@media (max-width:520px){.av-p{padding-left:0;flex-wrap:wrap}.av-t{flex:1 1 40%}}

/* ── DASHBOARD ───────────────────────────────────────────────────────────────*/
.greet{margin:2px 2px 18px}
.greet b{display:block;font-size:21px;font-weight:900;letter-spacing:-.015em}
.greet small{display:block;font-size:12.5px;color:var(--txt2);margin-top:4px}
.sec-t{font-size:10.5px;font-weight:800;letter-spacing:.09em;text-transform:uppercase;color:var(--txt3);margin:0 2px 10px}

/* THE FIX for v1's phone overflow: auto-fit + minmax means cards WRAP to the next
   row instead of running off the right edge. No fixed widths anywhere. */
.stats{display:grid;grid-template-columns:repeat(auto-fit,minmax(140px,1fr));gap:10px;margin-bottom:22px}
.stat{
  position:relative;overflow:hidden;
  background:var(--surface);border:1px solid var(--line);border-radius:14px;padding:14px 13px 12px;
}
.stat::before{content:'';position:absolute;top:0;left:0;right:0;height:2px;background:var(--acc);opacity:.5}
.stat b{display:block;font-size:20px;font-weight:900;color:var(--acc);letter-spacing:-.02em;
        white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.stat small{display:block;font-size:10.5px;font-weight:700;letter-spacing:.07em;text-transform:uppercase;color:var(--txt3);margin-top:5px}
/* A neutral figure is NOT an accent figure — an unknown must never look like a
   result (CLAUDE.md, UI Data Integrity). */
.stat.neutral b{color:var(--txt3)}
.stat.neutral::before{background:var(--line);opacity:1}

.actions{display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:10px}
.act{
  display:flex;align-items:center;gap:9px;padding:13px 14px;border-radius:13px;
  background:var(--acc-dim);border:1px solid var(--acc-line);color:var(--acc);
  font-size:13.5px;font-weight:800;
}
.act.ghost{background:var(--surface-2);border-color:var(--line);color:var(--txt)}
.act:active{transform:scale(.98)}

.note{
  margin-top:22px;padding:13px 14px;border-radius:13px;
  background:var(--surface);border:1px solid var(--line);
  color:var(--txt2);font-size:12.5px;line-height:1.5;
}
.note b{color:var(--txt);font-weight:800}

/* ── SURFACE TOOLBAR — segmented filter + search ─────────────────────────────
   Sticky to the top of the SCROLLER (not the viewport), so filters stay reachable
   while a long order list scrolls under them. */
.toolbar{
  position:sticky;top:-14px;z-index:5;margin:-14px -14px 12px;padding:12px 14px 10px;
  background:linear-gradient(var(--bg) 78%,rgba(5,5,5,0));
}
.segs{display:flex;gap:6px;overflow-x:auto;overscroll-behavior-x:contain;-webkit-overflow-scrolling:touch;
      scrollbar-width:none;padding-bottom:2px}
.segs::-webkit-scrollbar{display:none}
.seg{
  flex:0 0 auto;padding:8px 13px;border-radius:999px;font-size:12.5px;font-weight:700;
  background:var(--surface);border:1px solid var(--line);color:var(--txt2);white-space:nowrap;
}
.seg.on{background:var(--acc-dim);border-color:var(--acc-line);color:var(--acc)}
.seg .n{opacity:.7;margin-left:5px;font-weight:800}
.search{
  margin-top:9px;display:flex;align-items:center;gap:8px;
  background:var(--surface);border:1px solid var(--line);border-radius:11px;padding:0 11px;height:40px;
}
.search input{
  flex:1 1 auto;min-width:0;background:none;border:none;outline:none;color:var(--txt);
  font:inherit;font-size:13.5px;
}
.search input::placeholder{color:var(--txt3)}
/* Orders & Sales: filters (wrap on a phone, 40px+ targets). The Sales Control / Till & Shifts links come with those routes (U-A3), not here. */
.ord-filters{display:flex;gap:8px;flex-wrap:wrap;align-items:center;margin-top:9px}
.ord-filters select{flex:1 1 150px;min-width:0;min-height:40px;border-radius:11px;background:var(--surface);color:var(--txt);border:1px solid var(--line);padding:0 10px;font:inherit;font-size:13px}
.ord-note{margin:8px 0 0;padding:9px 11px;border-radius:10px;font-size:12.5px;line-height:1.45;background:rgba(255,180,0,.08);border:1px solid rgba(255,180,0,.28);color:#ffb400}
.ord-note[hidden]{display:none}
.badge.ord-chan{letter-spacing:.01em;text-transform:none}

/* ── ORDER LIST ──────────────────────────────────────────────────────────────*/
.ord-group{margin-bottom:18px}
.ord-list{display:grid;gap:9px}
.ord{
  width:100%;text-align:left;display:grid;gap:8px;
  background:var(--surface);border:1px solid var(--line);border-radius:14px;padding:13px 14px;
}
.ord:hover{border-color:rgba(255,255,255,.16)}
.ord:active{transform:scale(.995)}
.ord-top{display:flex;align-items:baseline;gap:10px;min-width:0}
.ord-id{font-size:13px;font-weight:800;letter-spacing:-.01em;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.ord-amt{margin-left:auto;flex:0 0 auto;font-size:14.5px;font-weight:900;color:var(--acc)}
.ord-sub{display:flex;align-items:center;gap:8px;min-width:0;color:var(--txt2);font-size:12px}
.ord-sub .who{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.ord-sub .when{margin-left:auto;flex:0 0 auto;color:var(--txt3);font-size:11.5px}
.badges{display:flex;flex-wrap:wrap;gap:6px}
.badge{
  font-size:10.5px;font-weight:800;letter-spacing:.03em;text-transform:uppercase;
  padding:4px 8px;border-radius:7px;border:1px solid var(--line);color:var(--txt2);background:var(--surface-2);
}
.badge.paid{color:var(--acc);border-color:var(--acc-line);background:var(--acc-dim)}
.badge.pending{color:#ffb400;border-color:rgba(255,180,0,.3);background:rgba(255,180,0,.1)}
.badge.failed,.badge.cancelled{color:var(--danger);border-color:rgba(255,82,82,.3);background:rgba(255,82,82,.1)}

/* ── SKELETON — shown INSTANTLY on click, before any data ────────────────────*/
.sk{background:var(--surface);border:1px solid var(--line);border-radius:14px;padding:13px 14px;display:grid;gap:9px}
.sk-line{height:11px;border-radius:6px;background:linear-gradient(90deg,#161616 25%,#1f1f1f 37%,#161616 63%);
         background-size:400% 100%;animation:sh 1.3s ease-in-out infinite}
@keyframes sh{0%{background-position:100% 50%}100%{background-position:0 50%}}
@media (prefers-reduced-motion:reduce){ .sk-line{animation:none} }

/* ── EMPTY / ERROR — honest states, never a fake figure ──────────────────────*/
.state{text-align:center;padding:44px 20px;color:var(--txt2)}
.state .ico{font-size:30px;display:block;margin-bottom:10px;opacity:.75}
.state b{display:block;font-size:14.5px;font-weight:800;color:var(--txt);margin-bottom:5px}
.state small{display:block;font-size:12.5px;line-height:1.5;max-width:340px;margin:0 auto}
.state .act{display:inline-flex;margin-top:14px}

/* ── CHARTS — CSS bars, no library, no network ───────────────────────────────*/
.chart{
  position:relative;height:132px;margin-bottom:6px;padding:8px 10px;
  background:var(--surface);border:1px solid var(--line);border-radius:14px;
}
.bars{position:absolute;inset:8px 10px}
.bar{position:absolute;bottom:0;background:var(--acc);opacity:.85;border-radius:3px 3px 0 0;min-height:2px}
.bar:hover{opacity:1}
.chart-x{display:flex;justify-content:space-between;gap:8px;margin:0 2px 20px;
         font-size:10.5px;color:var(--txt3);font-weight:700}
.chart-x span{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}

.bd{display:grid;gap:8px;margin-bottom:20px}
.bd-row{display:flex;align-items:center;gap:10px;font-size:12.5px;min-width:0}
.bd-k{flex:0 0 34%;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
      color:var(--txt2);font-weight:700;text-transform:capitalize}
.bd-track{flex:1 1 auto;height:8px;border-radius:99px;background:var(--surface-2);overflow:hidden;min-width:0}
.bd-fill{display:block;height:100%;background:var(--acc);opacity:.8;border-radius:99px}
.bd-v{flex:0 0 auto;font-weight:800;font-size:12px;white-space:nowrap}
@media (max-width:420px){ .bd-k{flex-basis:42%} }

/* Custom range row. The display rule lives HERE, not inline: an inline `display:flex`
   beats the [hidden] attribute's display:none, so the date pickers stayed on screen
   for every range. `hidden` must always win. */
#an-custom{margin-top:9px;display:flex;gap:8px}
#an-custom[hidden]{display:none}
input[type=date].search{color:var(--txt);font:inherit;font-size:13px;min-width:0;flex:1 1 0}

/* ── DEVICE / SETTINGS CARDS ─────────────────────────────────────────────────*/
.dev{background:var(--surface);border:1px solid var(--line);border-radius:14px;padding:14px}
.dev-head{display:flex;align-items:center;gap:12px;min-width:0}
.dev-ico{font-size:24px;flex:0 0 auto}
.dev-id{min-width:0}
.dev-id b{display:block;font-size:14.5px;font-weight:800;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.dev-id small{display:flex;align-items:center;gap:7px;font-size:12px;color:var(--txt2);margin-top:3px}
.ta{width:100%;background:var(--surface-2);border:1px solid var(--line);border-radius:11px;
    padding:11px 12px;color:var(--txt);font:inherit;font-size:13.5px;resize:vertical;min-height:96px}
.ta:focus{outline:none;border-color:var(--acc-line)}

.setgrid{display:grid;grid-template-columns:repeat(auto-fit,minmax(230px,1fr));gap:10px}
.np-row{display:flex;align-items:center;gap:12px;justify-content:space-between;padding:12px 14px;background:var(--surface-2);border:1px solid var(--line);border-radius:12px}
.np-t{display:flex;flex-direction:column;gap:3px;min-width:0}
.np-t small{color:var(--mut,#8a8f98);font-size:12px;line-height:1.35}
.np-sw{min-width:92px;min-height:44px;padding:0 14px;border-radius:22px;border:1px solid var(--line);background:var(--surface);color:var(--txt);font-weight:800;cursor:pointer}
.np-sw[aria-checked="true"]{background:var(--acc);color:#000;border-color:var(--acc)}
.np-sw[disabled]{opacity:.55;cursor:default}
.setcard{
  display:flex;align-items:center;gap:12px;text-align:left;
  background:var(--surface);border:1px solid var(--line);border-radius:14px;padding:13px 14px;
}
.setcard:hover:not([disabled]){border-color:rgba(255,255,255,.18)}
.setcard[disabled]{opacity:.45;cursor:not-allowed}
.sc-ico{font-size:20px;flex:0 0 auto}
.sc-t{min-width:0;flex:1 1 auto}
.sc-t b{display:block;font-size:13.5px;font-weight:800}
.sc-t small{display:block;font-size:11.5px;color:var(--txt3);margin-top:2px;
            overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.sc-go{color:var(--txt3);font-size:18px;flex:0 0 auto}


/* ── COMMAND PALETTE ─────────────────────────────────────────────────────────*/
.pal-wrap{
  position:fixed;inset:0;z-index:95;background:rgba(0,0,0,.6);
  display:flex;align-items:flex-start;justify-content:center;padding:12vh 16px 16px;
  opacity:0;pointer-events:none;transition:opacity .16s;
}
.pal-wrap.on{opacity:1;pointer-events:auto}
.pal{
  width:min(560px,100%);background:var(--surface);border:1px solid var(--line);
  border-radius:16px;overflow:hidden;display:flex;flex-direction:column;max-height:70vh;
  box-shadow:0 24px 60px rgba(0,0,0,.6);
}
.pal-in{display:flex;align-items:center;gap:10px;padding:14px 16px;border-bottom:1px solid var(--line);flex:0 0 auto}
.pal-in input{flex:1 1 auto;min-width:0;background:none;border:none;outline:none;color:var(--txt);font:inherit;font-size:15px}
.pal-in input::placeholder{color:var(--txt3)}
.pal-list{flex:1 1 auto;min-height:0;overflow-y:auto;padding:6px}
.pal-row{display:flex;align-items:center;gap:11px;width:100%;text-align:left;
         padding:10px 12px;border-radius:10px;font-size:13.5px;font-weight:600;color:var(--txt2)}
.pal-row.on,.pal-row:hover{background:var(--acc-dim);color:var(--acc)}
.pal-ico{width:20px;text-align:center;flex:0 0 auto}
.pal-empty{padding:22px 16px;text-align:center;color:var(--txt3);font-size:13px}
.pal-foot{flex:0 0 auto;padding:9px 16px;border-top:1px solid var(--line);color:var(--txt3);font-size:11px;font-weight:700}

/* ── DETAIL SHEET — opens IN-SHELL, never a page ─────────────────────────────
   Bottom sheet on phone, right-hand panel on desktop. The shell stays mounted
   underneath: the sidebar and header never move. */
.sheet-scrim{position:fixed;inset:0;z-index:70;background:rgba(0,0,0,.6);opacity:0;pointer-events:none;transition:opacity .2s}
.sheet-scrim.on{opacity:1;pointer-events:auto}
.sheet{
  position:fixed;z-index:71;background:var(--surface);border:1px solid var(--line);
  display:flex;flex-direction:column;min-height:0;
  left:0;right:0;bottom:0;max-height:88dvh;border-radius:18px 18px 0 0;
  transform:translateY(101%);transition:transform .24s cubic-bezier(.4,0,.2,1);
  padding-bottom:var(--safe-bot);
}
.sheet.on{transform:none}
.sheet-head{flex:0 0 auto;display:flex;align-items:center;gap:10px;padding:14px 16px;border-bottom:1px solid var(--line)}
.sheet-head b{font-size:15px;font-weight:800;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.sheet-x{margin-left:auto;width:34px;height:34px;border-radius:10px;border:1px solid var(--line);
         display:flex;align-items:center;justify-content:center;flex:0 0 auto}
.sheet-body{flex:1 1 auto;min-height:0;overflow-y:auto;overscroll-behavior:contain;padding:14px 16px 20px}
.kv{display:grid;grid-template-columns:auto 1fr;gap:7px 14px;font-size:13px;margin-bottom:16px}
.kv dt{color:var(--txt3);font-weight:700;font-size:11.5px;text-transform:uppercase;letter-spacing:.05em;padding-top:2px}
.kv dd{margin:0;color:var(--txt);min-width:0;overflow-wrap:anywhere}
.items{border:1px solid var(--line);border-radius:12px;overflow:hidden;margin-bottom:16px}
.item{display:flex;gap:10px;padding:11px 13px;border-bottom:1px solid var(--line);font-size:13px}
.item:last-child{border-bottom:none}
.item .q{color:var(--txt3);flex:0 0 auto}
.item .n{min-width:0;overflow:hidden;text-overflow:ellipsis}
.item .p{margin-left:auto;flex:0 0 auto;font-weight:800}
.sheet-actions{display:grid;grid-template-columns:repeat(auto-fit,minmax(140px,1fr));gap:9px}
@media (min-width:900px){
  .sheet{
    left:auto;top:0;right:0;bottom:0;width:min(460px,42vw);max-height:none;
    border-radius:0;border-right:none;transform:translateX(101%);
  }
  .sheet.on{transform:none}
}

/* ── BOTTOM NAV — PHONE ONLY ─────────────────────────────────────────────────
   THE FIX for v1's desktop duplication: this lives inside a max-width query, so it
   is not merely hidden on desktop — it cannot be rendered beside a sidebar at all. */
.bnav{display:none}
@media (max-width:899px){
  .bnav{
    position:fixed;left:0;right:0;bottom:0;z-index:40;
    display:grid;grid-template-columns:repeat(4,1fr);
    height:calc(var(--bnav-h) + var(--safe-bot));
    padding-bottom:var(--safe-bot);
    background:rgba(5,5,5,.94);
    -webkit-backdrop-filter:blur(12px);backdrop-filter:blur(12px);
    border-top:1px solid var(--line);
  }
  .bnav button{
    display:flex;flex-direction:column;align-items:center;justify-content:center;gap:3px;
    font-size:10px;font-weight:700;color:var(--txt3);min-width:0;
  }
  .bnav button .ico{font-size:17px;line-height:1}
  .bnav button.on{color:var(--acc)}
  .bnav button span:last-child{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:100%}
}

/* ── DESKTOP ─────────────────────────────────────────────────────────────────*/
@media (min-width:900px){
  /* ── DESKTOP SCROLL ARCHITECTURE ───────────────────────────────────────────
     The page itself does NOT scroll. The frame is exactly one viewport tall and
     only the content column scrolls, so the sidebar and header never move and
     never fight the document for the scrollbar.

       .app      100vh, overflow hidden        <- the page cannot scroll
       .side     own column, own internal scroll
       .col      flex column, 100vh
         .hdr    flex:0 0 auto                 <- fixed by layout, not by position
         .main   flex:1 1 auto; min-height:0; overflow-y:auto   <- the ONLY scroller

     min-height:0 on .main is load-bearing: a flex child defaults to min-height:auto
     and refuses to shrink below its content, which makes the column grow and hands
     the scrollbar back to the page. */
  .app{
    display:grid;
    grid-template-columns:var(--rail-w) minmax(0,1fr);   /* minmax(0,1fr) or a wide
                                                            child would stretch the
                                                            column and reintroduce a
                                                            horizontal scrollbar */
    height:100vh;height:100dvh;overflow:hidden;
    padding:0;
    transition:grid-template-columns .18s cubic-bezier(.4,0,.2,1);
  }
  .side{position:relative;height:100vh;height:100dvh;transform:none;width:var(--rail-w);z-index:10;
        transition:width .18s cubic-bezier(.4,0,.2,1)}
  .scrim{display:none}
  .col{display:flex;flex-direction:column;min-width:0;height:100vh;height:100dvh}
  .hdr{position:static;height:var(--header-h);padding:0 16px;flex:0 0 auto;left:auto;right:auto}
  .main{flex:1 1 auto;min-height:0;overflow-y:auto;overscroll-behavior:contain;padding:20px 24px 32px}
  /* An iframe module fills the scroller exactly and scrolls its own document —
     no nested scrollbar, no double scroll trap.
     DEFINITE HEIGHT (lane 26, 2026-10-27; owner: "THE UPLOADER PAGE MUST BE SCROLLABLE IN ALL DEVICES"). This used to be height:100%, but the parent is
     .panel.show — display:block with an AUTO height — so the percentage resolved to auto and every framed module (Product Upload, POS, ...) collapsed to the
     150px default iframe height on desktop: measured top 56 / bottom 206 at 1024, 1366 and 1920. The frame is the whole column under the header: viewport
     height minus the header. The -20/-32 margins cancel .main's padding, so .main itself never scrolls (no double scroll). */
  .frame-wrap{height:calc(100vh - var(--header-h));height:calc(100dvh - var(--header-h));margin:-20px -24px -32px}
  .greet b{font-size:24px}

  /* ── COLLAPSED RAIL ────────────────────────────────────────────────────────
     Icon-only. Labels collapse to zero width rather than `display:none` so the
     transition animates and the icons stay put instead of jumping. The rail is
     never collapsed on phone — there it is a drawer, and a 64px permanent strip
     would eat the content area. */
  .app.collapsed{grid-template-columns:var(--rail-collapsed) minmax(0,1fr)}
  .app.collapsed .side{width:var(--rail-collapsed)}
  .app.collapsed .hdr{left:var(--rail-collapsed)}
  .app.collapsed .side-head{justify-content:center;padding:13px 6px}
  .app.collapsed .side-logo{width:34px;height:34px}
  .app.collapsed .side-shop,
  .app.collapsed .side-caret{opacity:0;width:0;overflow:hidden;flex:0 0 0}
  .app.collapsed .nav-item{justify-content:center;padding:10px 0}
  .app.collapsed .nav-item.on{padding:9px 0}
  .app.collapsed .nav-item .lbl{opacity:0;width:0;overflow:hidden}
  .app.collapsed .side-group{opacity:0;height:6px;padding:0;margin:4px 0;overflow:hidden}
  .app.collapsed .side-status{align-items:center;padding:10px 6px}
  .app.collapsed .chip .ctxt{opacity:0;width:0;overflow:hidden}
}

@media (prefers-reduced-motion:reduce){
  .side{transition:none}
  .scrim{transition:none}
}
