:root{
  --shell:#14181C; --panel:#1C2228; --panel-2:#232B32; --line:#2E383F;
  --tape:#FFFFFF; --tape-line:#DADDE1; /* the cheque paper is plain white (owner, 2026-09-05) */
  /* BeSmart navy (owner, 2026-09-05): the old brass accent is retired — --brass now carries a
     light navy so every existing var(--brass) frame/hover reads in the BeSmart theme, and the
     two navy tokens paint the cheque's buttons. */
  --navy:#203864; --navy-2:#2d4a80;
  --brass:#7C9BE0; --go:#2F7F5B; --stop:#B4402F; --alert:#D4622A;
  --text:#E7ECEF; --text-dim:#8A9AA5; --ink:#191512;
  --r:10px;
  --ui:'Space Grotesk',system-ui,sans-serif;
  --ar:'Noto Kufi Arabic','Space Grotesk',sans-serif;
  --mono:'IBM Plex Mono',ui-monospace,monospace;
  /* The login screen's own tokens — see the LOGIN / REVENUE CENTRE PICKER section below for why
     these exist separately from --shell/--panel rather than reusing them directly. */
  --login-bg:radial-gradient(ellipse farthest-corner at 50% 0%,#262f38 0%,#1e232a 28%,#191c1f 55%,#100f11 80%,#0b0d0e 100%);
  --login-card-bg:rgba(28,34,40,.9);
  --login-menu-bg:rgba(28,34,40,.97);
  --login-opt-bg:rgba(255,255,255,.03);
  --login-opt-bg-hover:rgba(255,255,255,.06);
}
/* "Clean slate" light theme — same variable names as the dark palette above, so every rule in
   this file that already reads var(--shell)/var(--text)/etc. picks it up with no other changes.
   --tape/--tape-line/--ink/--go/--stop/--alert are already light-surface-safe (they're either
   already light, or used as solid button/badge backgrounds with fixed light text on top) and
   stay the same in both themes on purpose. --brass is darkened here specifically — the dark
   theme's gold reads fine on a near-black panel but loses too much contrast used as small text
   on a near-white one, so light mode gets a deeper gold instead of reusing the same hex. Applied
   by toggling data-theme on <html> — see /shared/theme.js — never edit this selector by hand
   without checking style.css's other var(--brass)/var(--text-dim) usages still read cleanly. */
:root[data-theme="light"]{
  --shell:#FFFFFF; --panel:#F5F6F8; --panel-2:#EAECEF; --line:#DADDE1;
  /* --brass and --text-dim: darkened again from the first light-mode pass — both still landed
     under the 4.5:1 WCAG AA floor against --panel (#A67C1A measured ~3.5:1, #6B7280 ~4.47:1),
     caught live on the guest order page's price text and secondary labels. These clear ~5:1/~7:1. */
  --brass:#2d4a80;
  --text:#20242A; --text-dim:#4B5563;
  --login-bg:radial-gradient(ellipse farthest-corner at 50% 0%,#FFFFFF 0%,#F6F7F9 28%,#EEF0F3 55%,#E4E7EB 80%,#DDE1E6 100%);
  --login-card-bg:rgba(255,255,255,.9);
  --login-menu-bg:rgba(255,255,255,.97);
  --login-opt-bg:rgba(0,0,0,.03);
  --login-opt-bg-hover:rgba(0,0,0,.06);
}
*{box-sizing:border-box;-webkit-tap-highlight-color:transparent}
html,body{height:100%;margin:0}
body{background:var(--shell);color:var(--text);font-family:var(--ui);overflow:hidden}
button{font-family:inherit;color:inherit;border:0;background:none;cursor:pointer}
.ar{font-family:var(--ar);direction:rtl}

.conn-banner{position:fixed;top:0;left:0;right:0;z-index:80;background:var(--alert);color:#fff;text-align:center;font-size:12px;font-weight:600;letter-spacing:.03em;padding:6px}
.conn-banner[hidden]{display:none}

.app{display:flex;flex-direction:column;height:100dvh}
.topbar{display:flex;align-items:center;gap:14px;padding:10px 16px;background:var(--panel);border-bottom:1px solid var(--line);flex:0 0 auto}
.topbar[hidden]{display:none}
/* Left-aligned stack: the outlet name starts under the first letter of the wordmark. */
.brand{display:flex;flex-direction:column;align-items:flex-start;text-align:left;line-height:1.15}
.brand b{font-size:15px;letter-spacing:.02em}
.brand span{font-size:10px;color:var(--text-dim);letter-spacing:.14em;text-transform:uppercase}
.open-tables{padding:7px 10px;border-radius:8px;background:var(--panel-2);border:1px solid var(--line);color:var(--text);font-family:inherit;font-size:12px;max-width:170px;margin-left:6px}
.open-tables:hover{border-color:var(--brass)}
.btn-newtable{padding:7px 12px;border-radius:8px;background:var(--panel-2);border:1px dashed var(--line);font-size:12px;white-space:nowrap;color:var(--text-dim)}
.btn-newtable:hover{border-color:var(--brass);color:var(--brass)}
.btn-cheques{margin-left:auto;padding:7px 12px;border-radius:8px;background:var(--panel-2);border:1px solid var(--line);font-size:12px;white-space:nowrap;color:var(--text-dim)}
.btn-cheques:hover{border-color:var(--brass);color:var(--brass)}
/* At least one open cheque, anywhere, has an item sitting unsent for over 7 minutes — see
   anyTableHasOverdue. A glance-able signal for something you're not currently looking at,
   same red/pulse language as checkOverdueBtn (the per-table version of this same alert). */
.btn-cheques.overdue{background:var(--stop);border-color:var(--stop);color:#fff;animation:overdue-pulse 1.6s ease-in-out infinite}
/* BeSmart Admin's topbar has no Cheques button to carry the auto-margin that
   pushes the session-bar right, so give it one directly, scoped to this page only. */
.admin-topbar .session-bar{margin-left:auto}
/* Same reasoning for the KDS board's topbar — no Cheques button there either, so the
   workstation-name tag and Reload button (both now in .session-bar) need their own push right. */
.kds-topbar .session-bar{margin-left:auto}

.session-bar{display:flex;align-items:center;gap:6px}
.session-bar .a-addbtn{margin-top:0;white-space:nowrap}
.session-revenue-centre,.session-user{padding:7px 12px;border-radius:8px;background:var(--panel-2);border:1px solid var(--line);font-size:12px;white-space:nowrap}
.session-revenue-centre[hidden]{display:none}
.session-revenue-centre:hover{border-color:var(--brass);color:var(--brass)}
/* .session-revenue-centre is a <button> in the till (opens the RVC picker) but a <select>
   in BeSmart Admin (the RVC switcher) — selects don't inherit font/color/cursor the way
   buttons do, so set them explicitly here for the select case. */
select.session-revenue-centre{color:var(--text);font-family:inherit;cursor:pointer}
.session-user{color:var(--text-dim)}
.session-user:hover{border-color:var(--stop);color:var(--stop)}
/* A plain name display, not a control — both apps now have a dedicated Logout button instead,
   so the red "this logs you out" hover cue would be actively misleading here. */
.session-user.static{cursor:default}
.session-user.static:hover{border-color:var(--line);color:var(--text-dim)}

.main{flex:1;display:flex;min-height:0;position:relative;overflow:hidden}
/* Open cheques as a tab strip on the right edge (owner, 2026-09-05): New table first, then one
   small square per open table. Tapping a tab slides that cheque in over the grid. */
.cheque-tabs{flex:0 0 112px;background:var(--panel);border-left:1px solid var(--line);display:flex;flex-direction:column;gap:0;padding:10px 6px 10px 0;overflow-y:auto;min-height:0;z-index:31}
/* Index tabs, like the tabs on a paper file (owner, 2026-09-05): square on the side that touches
   the cheque, rounded on the outer edge; the open one wears the cheque's own paper colour so the
   tab and the sheet read as one piece. */
.cheque-tab{width:100px;height:36px;border-radius:0 12px 12px 0;background:var(--panel-2);border:1px solid var(--line);border-left:0;font-family:var(--mono);font-weight:700;font-size:14px;color:var(--text);display:flex;align-items:center;justify-content:center;text-align:center;line-height:1.1;flex:0 0 auto;position:relative;box-shadow:2px 2px 6px rgba(0,0,0,.12);margin-bottom:-9px}
/* Stacked like a paper file: each tab sits a little over the one below it (z-index set per tab in
   pos.js so the upper one is on top); the open tab always comes to the front. */
.cheque-tab:last-child{margin-bottom:0}
.cheque-tab.on,.cheque-tab:hover{z-index:60!important}
.cheque-tab:hover{border-color:var(--brass);color:var(--brass)}
.cheque-tab.on{background:var(--tape);border-color:var(--tape-line);color:var(--ink);width:106px;box-shadow:3px 3px 8px rgba(0,0,0,.18)}
.cheque-tab.new{background:var(--navy);border-color:var(--navy);color:#fff;font-family:var(--ui);font-size:11px;white-space:nowrap;font-weight:600;letter-spacing:.02em}
/* Items sitting unsent too long: the table's own tab flashes red (owner, 2026-09-05). */
.cheque-tab.overdue{background:var(--stop);border-color:var(--stop);color:#fff;animation:overdue-pulse 1.6s ease-in-out infinite}
.cheque-tab.overdue.on{background:var(--stop);color:#fff}
/* Closed cheques: pinned at the bottom of the strip, one line, never part of the stack. */
.cheque-tab.closed{margin-top:auto;margin-bottom:0;align-self:center;margin-left:6px;height:36px;width:96px;border-radius:8px;border-left:1px solid var(--line);font-family:var(--ui);font-size:11.5px;font-weight:600;white-space:nowrap;box-shadow:none;z-index:1}
.cheque-tab.locked{opacity:.5}
.cheque-tab .cq-lock{position:absolute;right:-2px;bottom:-2px;font-size:9px}
.main[hidden]{display:none}
/* News-style crawl for "order ready" alerts — sits between the topbar and the grid, only takes
   up space while a message is scrolling (see showReadyTicker in pos.js), so it never steals
   room from the grid/check panel the rest of the time. */
.ticker{position:relative;overflow:hidden;height:30px;flex:0 0 auto;background:var(--go);border-bottom:1px solid var(--line)}
.ticker.cash{background:#e07f0e} /* the cash-assist strip — orange so a waiter tells it from the green ready bar at a glance */
.ticker-track{position:absolute;top:0;left:100%;z-index:1;white-space:nowrap;line-height:30px;padding:0 16px;font-size:13px;font-weight:700;letter-spacing:.02em;color:#fff}
.ticker-dismiss{position:absolute;top:0;right:0;z-index:2;height:30px;width:34px;display:flex;align-items:center;justify-content:center;background:rgba(0,0,0,.18);border:0;border-left:1px solid rgba(255,255,255,.25);color:#fff;font-size:14px;line-height:1;cursor:pointer}
.ticker-dismiss:hover{background:rgba(0,0,0,.32)}
@keyframes ticker-scroll{from{left:100%}to{left:-100%}}
/* .cats-col wraps the category nav plus the Logout button pinned under it — same left-sidebar,
   bottom, white-text placement as BeSmart Admin's own Logout (.a-tab, reused here as-is rather
   than a lookalike copy, so both apps' logout controls are pixel-identical, not just similar). */
.cats-col{flex:0 0 122px;background:var(--panel);border-right:1px solid var(--line);display:flex;flex-direction:column;min-height:0}
.cats-col .a-tab{margin:0 8px 10px}
.cats-col .ws-name-tag{margin:0 8px 16px;text-align:center;white-space:normal;line-height:1.3}
/* order.html's QR page uses .cats directly, unwrapped (see .order-app .cats override below) —
   background/border-right stay on the base rule too so that page keeps its look unchanged. */
.cats{flex:1;background:var(--panel);border-right:1px solid var(--line);display:flex;flex-direction:column;padding:10px 8px;gap:6px;overflow-y:auto;min-height:0}
.cats-col .cats{border-right:0}
.cat{padding:13px 10px;border-radius:var(--r);text-align:left;font-size:13px;color:var(--text-dim);border:1px solid transparent}
.cat.on{background:var(--panel-2);color:var(--text);border-color:var(--line)}
.cat small{display:block;font-size:11px;opacity:.6;margin-top:3px;font-family:var(--ar);direction:rtl}

/* grid-auto-rows:min-content is required, not cosmetic. With the default `auto` rows this grid
   resolved every row to the tile's 96px minimum instead of to its contents, so a tile carrying a
   photo (~199px) was laid out inside a 96px row and its picture spilled over the tiles below —
   the reported "pics are over each other", worst on a screen with gaps, where short blank tiles
   share the row. min-content sizes each row to what it actually holds: ~199px where there are
   photos, 96px for a row of text-only tiles or gaps. */
.grid{flex:1;padding:12px;display:grid;gap:10px;overflow-y:auto;grid-template-columns:repeat(auto-fill,minmax(150px,1fr));grid-auto-rows:min-content;align-content:start}
.tile{background:var(--panel);border:1px solid var(--line);border-radius:var(--r);padding:12px;min-height:96px;text-align:left;display:flex;flex-direction:column;justify-content:space-between;transition:transform .08s,border-color .12s;position:relative}
.tile:active{transform:scale(.97);border-color:var(--brass)}
.tile .nm{font-size:13.5px;font-weight:500;line-height:1.25}
.tile .nar{font-size:12px;color:var(--text-dim);margin-top:3px}
.tile .pr{font-family:var(--mono);font-size:13px;color:var(--brass);margin-top:10px}
.tile .flag{position:absolute;top:9px;right:9px;z-index:1;font-size:9px;letter-spacing:.1em;color:#fff;background:rgba(0,0,0,.55);border:1px solid rgba(255,255,255,.2);border-radius:4px;padding:2px 5px}
/* Allergy warning badge on guest QR tiles — top-LEFT (the sizes .flag owns top-right), solid
   ground + z-index so it stays readable over a photo tile. Shown only when the item's allergy
   box is hand-ticked in Admin (never inferred). */
.tile .allergy-flag{position:absolute;top:9px;left:9px;z-index:1;font-size:11px;line-height:1;background:#7a5c1e;color:#ffd76a;border:1px solid #a8842f;border-radius:4px;padding:3px 5px}
.tile-addon{border-style:dashed;border-color:var(--brass)}
.tile-addon .nm::before{content:'+ ';color:var(--brass)}

/* The cheque is a slide-over now (owner, 2026-09-05: "the same as the ResMaro flyer"): it lives
   over the right side of the grid, slides in when a table is open and hides on Hide / after a
   send, leaving the tab strip to pick the next one. */
.check{position:absolute;top:0;right:112px;bottom:0;width:340px;max-width:calc(100% - 112px);background:var(--tape);color:var(--ink);display:flex;flex-direction:column;z-index:30;transform:translateX(calc(100% + 112px));transition:transform .22s;box-shadow:-8px 0 30px rgba(0,0,0,.28)}
.check.open{transform:none}
.check-hd{padding:12px 16px 8px;border-bottom:1px dashed var(--tape-line);display:flex;align-items:baseline;justify-content:space-between}
.check-hd b{font-size:14px}
.check-hd span{font-family:var(--mono);font-size:11px;opacity:.55}
.check-hd-right{display:flex;align-items:baseline;gap:8px}
.check-pax{font-family:var(--ui);font-size:11px;opacity:.75;padding:2px 8px;border-radius:999px;background:rgba(0,0,0,.06);border:1px dashed var(--tape-line)}
.check-pax:hover{border-color:var(--go);color:var(--go)}
.check-pax[hidden]{display:none}
.check-overdue-btn{font-family:var(--ui);font-size:11px;font-weight:600;padding:3px 10px;border-radius:999px;background:var(--stop);color:#fff;border:none;animation:overdue-pulse 1.6s ease-in-out infinite}
.check-overdue-btn[hidden]{display:none}
@keyframes overdue-pulse{0%,100%{opacity:1}50%{opacity:.6}}
.overdue-line{color:var(--stop)}
.overdue-line .ln-n::after{content:" · not sent yet";font-size:10px;font-weight:600;text-transform:uppercase;letter-spacing:.03em}
.lines{flex:1;overflow-y:auto;padding:8px 16px}
.empty{padding:40px 8px;text-align:center;font-size:13px;color:#8a8272;line-height:1.6}
.floor-map{position:relative;width:100%;line-height:0}
.floor-map img{width:100%;height:auto;border-radius:var(--r);display:block}
.fp-marker{position:absolute;transform:translate(-50%,-50%);display:flex;flex-direction:column;align-items:center;justify-content:center;
  width:80px;height:80px;border-radius:50%;border:3px solid var(--line);background:var(--panel-2);color:var(--text);
  cursor:pointer;line-height:1.2;padding:0;gap:1px}
.fp-marker.occupied{border-color:var(--go);background:color-mix(in srgb, var(--go) 18%, var(--panel-2))}
.fp-marker.empty{border-style:dashed}
.fp-marker.overdue{border-color:var(--stop);background:color-mix(in srgb, var(--stop) 22%, var(--panel-2))}
.fp-marker.locked{opacity:.6}
.fp-marker .fp-num{font-family:var(--mono);font-size:19px;font-weight:700}
/* Seats/PAX has to be readable at a glance, at arm's length, without tapping in — same reason
   the number itself is large. A small pill (not just dim text) keeps it legible over any
   background photo, busy or plain. */
.fp-marker .fp-seats{font-size:12px;font-weight:600;color:var(--text);background:color-mix(in srgb, var(--panel) 70%, transparent);
  border-radius:8px;padding:0 5px;white-space:nowrap}
.fp-marker .fp-lock{position:absolute;bottom:-18px;font-size:9px;white-space:nowrap;color:var(--text-dim)}
.ln{padding:7px 0;border-bottom:1px dotted var(--tape-line);cursor:pointer;border-radius:6px}
.ln:hover{background:rgba(0,0,0,.04)}
.ln:active{background:rgba(0,0,0,.08)}
.ln-top{display:flex;gap:8px;align-items:baseline;font-family:var(--mono);font-size:12.5px}
.ln-q{width:26px;flex:0 0 auto}
.ln-n{flex:1;font-family:var(--ui);font-size:13px}
.ln-p{font-variant-numeric:tabular-nums}
.ln-sub{margin:3px 0 0 26px;font-size:11px;color:#6f6857}
.ln-rem{margin:3px 0 0 26px;font-size:11px;color:var(--stop);font-weight:600}
.ln-alg{margin:3px 0 0 26px;font-size:10px;color:#fff;background:var(--stop);display:inline-block;padding:2px 6px;border-radius:4px;letter-spacing:.08em}
.ln-ctl{display:flex;gap:6px;margin:6px 0 0 26px}
.ln-ctl button{width:26px;height:26px;border-radius:6px;background:rgba(32,56,100,.12);color:var(--navy);font-size:14px;font-weight:600;line-height:1}
.ln-ctl button.rm{margin-left:auto;background:transparent;color:var(--stop);width:auto;padding:0 6px;font-size:11px}
.ln.sent{opacity:.72}
.ln.sent .ln-n::after{content:'SENT';font-family:var(--mono);font-size:9px;letter-spacing:.1em;background:var(--go);color:#fff;padding:2px 5px;border-radius:4px;margin-left:7px;vertical-align:1px}
.ln.sent .ln-ctl button:not(.rm){display:none}
.btn.fire{background:var(--navy);color:#fff;font-weight:600}

.kq{display:flex;flex-direction:column;gap:10px}
.kq-empty{padding:26px 8px;text-align:center;color:var(--text-dim);font-size:13px}
.tkt{background:#fff;color:#000;border-radius:8px;padding:12px;font-family:var(--mono);font-size:12.5px;line-height:1.5;border-left:5px solid var(--go)}
.tkt.warm{border-left-color:var(--brass)}
.tkt.late{border-left-color:var(--stop)}
.tkt-hd{display:flex;justify-content:space-between;font-weight:600;border-bottom:1px solid #ddd;padding-bottom:5px;margin-bottom:6px}
.tkt-fired{font-size:10.5px;color:#777;margin:-2px 0 6px}
.tkt-el{font-variant-numeric:tabular-nums}
.tkt.late .tkt-el{color:var(--stop)}
.tkt .no{font-weight:700;text-transform:uppercase}
.tkt .plus{padding-left:12px;color:#555}
.tkt .alg{background:#000;color:#fff;text-align:center;padding:4px;font-weight:700;letter-spacing:.08em;margin:-12px -12px 8px;border-radius:8px 8px 0 0}
.tkt .paid-tag{background:var(--go);color:#fff;border-radius:4px;padding:1px 6px;font-size:10px;letter-spacing:.05em;margin-left:6px;vertical-align:middle}
.tkt-act{margin-top:8px;width:100%;padding:9px;border-radius:6px;background:#111;color:#fff;font-family:var(--ui);font-size:12px}
.tkt.done{opacity:.45}
.tkt.done .tkt-act{background:#888}

/* ================= KDS BOARD (kds.html) =================
   Same .tkt ticket-card CSS as the till's Kitchen Queue modal above — this is only the full-page
   wrapper: a wide, wall-mounted-tablet-friendly grid instead of the modal's single-column list,
   plus the station filter control in the topbar. */
/* Multi-outlet KDS board — one column per assigned Revenue Centre, each tinted from a small
   fixed palette (data-color cycles c0..c5) so 2-3 outlets sharing one physical screen stay
   instantly tellable apart from across a kitchen. Ticket cards inside reuse .tkt as-is. */
.kds-outlets{flex:1;min-height:0;display:flex;overflow-x:auto}
.kds-outlets[hidden]{display:none}
.kds-outlet{flex:1 0 320px;display:flex;flex-direction:column;min-height:0;border-right:1px solid var(--line)}
.kds-outlet:last-child{border-right:0}
.kds-outlet-hd{flex:0 0 auto;display:flex;align-items:center;justify-content:space-between;gap:10px;padding:10px 12px 10px 16px;font-weight:700;font-size:15px;letter-spacing:.01em;border-bottom:1px solid rgba(0,0,0,.08)}
.kds-clear-btn{flex:0 0 auto;padding:6px 12px;border-radius:6px;border:1px solid rgba(0,0,0,.15);background:rgba(255,255,255,.65);color:inherit;font-family:var(--ui);font-size:11.5px;font-weight:600;letter-spacing:0;cursor:pointer}
.kds-clear-btn:hover{background:rgba(255,255,255,.95)}
.kds-outlet-board{flex:1;min-height:0;overflow-y:auto;padding:14px;display:grid;grid-template-columns:repeat(auto-fill,minmax(250px,1fr));gap:12px;align-content:start}
.kds-outlet-board .tkt{font-size:14px}
.kds-outlet-board .kq-empty{grid-column:1/-1;padding:60px 8px;font-size:15px}
.kds-outlet[data-color="c0"]{background:#e3f6e8}
.kds-outlet[data-color="c1"]{background:#fbe4e2}
.kds-outlet[data-color="c2"]{background:#e1eefb}
.kds-outlet[data-color="c3"]{background:#fbf3d9}
.kds-outlet[data-color="c4"]{background:#eee3fa}
.kds-outlet[data-color="c5"]{background:#fbe9d9}
/* Reload/Logout — plain buttons, distinct from the .session-user text-link style, since these
   are meant to read as clearly-tappable controls on a wall-mounted touch screen. */
.topbar .a-addbtn{margin-top:0;padding:7px 12px;font-size:12px}
/* Which physical device this is — see kds-boot.js's showWorkstationName. Same tag look on the
   login screen (centered, above the PIN card's own heading) and in the topbar once logged in. A
   plain rounded-square box, matching every other button/tag's corner radius in this app — not a
   pill, which read as an odd shape mismatched against the square Reload button next to it. */
.ws-name-tag{font-size:11px;letter-spacing:.04em;color:var(--text-dim);background:var(--panel-2);border:1px solid var(--line);border-radius:6px;padding:5px 12px;margin-bottom:14px;display:inline-block}
/* In the topbar (next to Reload), not the login card — drop the margin meant to space it from
   the PIN pad below it, and match Reload's own padding/font-size exactly so both sit on the same
   baseline instead of the tag looking slightly low/mismatched next to the button. */
.session-bar .ws-name-tag{margin-bottom:0;padding:7px 12px;font-size:12px}

.totals{padding:10px 16px;border-top:1px dashed var(--tape-line);font-family:var(--mono);font-size:12px}
.tr{display:flex;justify-content:space-between;padding:2px 0}
.tr.grand{font-size:17px;font-weight:600;padding-top:8px;margin-top:6px;border-top:1px solid var(--ink)}
.tr.alt{font-size:12px;opacity:.6}
.tr span:last-child{font-variant-numeric:tabular-nums}

.pay{padding:8px 12px 10px;display:flex;flex-direction:column;gap:6px}
.pay-row{display:flex;gap:6px}
/* Cheque buttons in the BeSmart theme (owner, 2026-09-05): light navy by default, full navy for
   the two row-wide actions (Send, Payment). The old beige/brass buttons are gone. */
.btn{flex:1;padding:8px 10px;border-radius:8px;font-size:12.5px;line-height:1.2;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;background:var(--navy-2);color:#fff;font-weight:500}
.btn:active{transform:scale(.98)}
.btn:disabled{opacity:.4;pointer-events:none}
.btn.primary{background:var(--navy);color:#fff;font-weight:600}
.btn.navy{background:var(--navy);color:#fff;font-weight:600}
.btn.white{background:#fff;color:var(--navy);border:1px solid var(--navy-2);font-weight:600}
.btn.ghost{background:var(--navy-2);color:#fff;border:0;font-size:12px;padding:9px}
.btn.small{flex:0 0 auto;padding:5px 10px;font-size:12px;min-height:30px}
.btn.voice{background:var(--ink);color:var(--brass);font-size:12px}
/* Small and square — it sits beside a full-width button, so flex:1 (the .btn default) would
   have it eat half the row for what is a single glyph. */
.btn.mic{flex:0 0 46px;padding:0;height:38px;font-size:17px;line-height:1;background:var(--ink);color:#fff;display:flex;align-items:center;justify-content:center}
.btn.mic.busy{opacity:.6}
/* Listening has to be obvious from across a room: a live mic nobody realises is live is the one
   real privacy problem this feature has. */
.btn.mic.on{background:var(--alert);color:#fff;animation:micPulse 1.4s ease-in-out infinite}
@keyframes micPulse{0%,100%{box-shadow:0 0 0 0 rgba(200,60,50,.55)}50%{box-shadow:0 0 0 7px rgba(200,60,50,0)}}
@media (prefers-reduced-motion:reduce){.btn.mic.on{animation:none;outline:2px solid var(--alert);outline-offset:2px}}
.voice-strip{padding:7px 10px;border-radius:var(--r);background:var(--panel-2);color:var(--text-dim);font-size:11.5px;line-height:1.35;text-align:center}
.voice-strip[hidden]{display:none}
.voice-strip.heard{color:var(--ink);font-weight:600}
/* A pending settle is the one state where the strip is telling the waiter that money is about to
   move, so it stops looking like a hint and starts looking like a prompt. */
.voice-strip.failed{background:var(--alert);color:#fff}
.voice-strip.confirming{background:var(--alert);color:#fff;font-weight:600}
.btn[disabled]{opacity:.35;pointer-events:none}

.scrim{position:fixed;inset:0;background:rgba(8,10,12,.74);display:flex;align-items:center;justify-content:center;padding:18px;z-index:40}
.scrim[hidden]{display:none}
/* border-radius:12px unifies this with the rest of the floating-overlay family (login card,
   shutdown box, context menus) — they used to carry five different one-off radii (8/10/12/14/16px)
   with no relation to each other or to the --r:10px token everything else in the app uses. */
/* Every pop-up wears the login card look (owner, 2026-09-05): a translucent floating card with a
   deep shadow, and a sane default width so a two-column list no longer spans half the screen. */
.modal{background:var(--login-card-bg);backdrop-filter:blur(6px);border:1px solid var(--line);border-radius:14px;width:100%;max-width:560px;max-height:88dvh;overflow-y:auto;padding:20px;box-shadow:0 20px 50px rgba(0,0,0,.5)}
.modal.wide{max-width:820px}
/* Every modal used to render at the same fixed 800px regardless of content — a two-line "Log out?"
   confirm stretched into the same box as a 20-field editor form, with huge dead side margins. This
   shrinks it automatically for genuine confirm/alert dialogs (no input/select/textarea and none of
   the richer content wrappers below) while leaving every real form/list/receipt/queue modal at its
   existing width — no JS changes, so it can't drift out of sync the way a hand-toggled class on 70+
   call sites would if a future one forgot to set it. */
.modal:not(:has(input, select, textarea, .receipt, .line-menu, .opts, .mgroup, .lines, .kq, .split-lines, .split-legs, .cheque-list, .layout-grid, .totals, .order-cartrow)){max-width:420px}
.modal h3{margin:0 0 2px;font-size:17px}
.modal .sub{margin:0 0 14px;font-size:12px;color:var(--text-dim)}
/* Darker/bolder than .sub on purpose — this is the one line in the order-placed confirmation a
   guest actually needs to act on (keep the tab, or save it), not background detail like the
   ticket number above it. */
.order-stay-note{margin:0 0 14px;font-size:13px;font-weight:600;color:var(--text)}
.mgroup{margin-bottom:14px}
.mgroup>p{margin:0 0 7px;font-size:10px;letter-spacing:.12em;text-transform:uppercase;color:var(--text-dim);display:flex;justify-content:space-between}
.opts{display:flex;flex-wrap:wrap;gap:7px}
.opt{padding:9px 12px;border-radius:8px;background:var(--panel-2);border:1px solid var(--line);font-size:13px;display:flex;align-items:center;gap:6px}
.opt.on{border-color:var(--brass);color:var(--brass)}
.opt em{font-style:normal;font-family:var(--mono);font-size:11px;opacity:.7}
.opt .a{font-family:var(--ar);font-size:12px;opacity:.75}
.opt.rem.on{border-color:var(--stop);color:var(--stop);background:rgba(180,64,47,.12);text-decoration:line-through}
.opt.locked{opacity:.4;pointer-events:none}
.line-menu{display:flex;flex-direction:column;gap:8px;margin-bottom:14px}
.line-menu .opt{width:100%;justify-content:flex-start;padding:12px 14px;font-size:14px}
.opt.danger{color:var(--stop);border-color:rgba(180,64,47,.4)}
/* Armed tap-again-to-confirm state (guest order page's Cash button) — --alert, not --stop/--danger,
   since this isn't an error state, just "one more tap needed." */
.opt.confirming{color:var(--alert);border-color:var(--alert);background:rgba(212,98,42,.12);font-weight:600}
.cheque-list{display:flex;flex-direction:column;gap:8px;margin-bottom:4px}
.cheque-row{width:100%;padding:12px 14px;border-radius:10px;background:var(--panel-2);border:1px solid var(--line);display:flex;justify-content:space-between;align-items:center;gap:10px;text-align:left}
.cheque-row:hover{border-color:var(--brass)}
.cheque-row.overdue-cheque{border-color:var(--stop);background:rgba(180,64,47,.12)}
.cheque-row.overdue-cheque .cq-table{color:var(--stop)}
.cheque-row .cq-main{display:flex;flex-direction:column;gap:2px}
.cheque-row .cq-table{font-size:14px;font-weight:600}
.cheque-row .cq-ref{font-family:var(--mono);font-size:11px;color:var(--text-dim)}
.cheque-row .cq-amt{font-family:var(--mono);font-size:14px;font-variant-numeric:tabular-nums;flex:0 0 auto}
.cheque-empty{padding:14px;text-align:center;color:var(--text-dim);font-size:12px}
.opt.danger:hover{background:rgba(180,64,47,.12)}
.allergy{display:flex;align-items:center;gap:9px;padding:11px;border-radius:8px;background:rgba(212,98,42,.12);border:1px solid var(--alert);font-size:12.5px;margin-top:4px}
.allergy input{width:18px;height:18px;accent-color:var(--alert)}
.noteBox{width:100%;min-height:62px;padding:10px;border-radius:8px;background:var(--shell);border:1px solid var(--line);color:var(--text);font-family:var(--ui);font-size:13px;resize:vertical;line-height:1.45}
.noteBox:focus{outline:2px solid var(--brass);outline-offset:-1px}
.chips{display:flex;flex-wrap:wrap;gap:6px;margin-bottom:8px}
.chip{padding:7px 10px;border-radius:999px;background:var(--panel-2);border:1px solid var(--line);font-size:12px;color:var(--text-dim)}
.chip:active{border-color:var(--brass)}
.ln-note{margin:3px 0 0 26px;font-size:11px;color:#5c5647;font-style:italic}
.tkt .note{background:#f2f2f2;border-left:3px solid #000;padding:3px 7px;margin:3px 0;font-style:italic}
.modal-foot{display:flex;gap:8px;margin-top:6px}
.modal-foot .btn{background:var(--panel-2);color:var(--text)}
.modal-foot .btn.primary{background:var(--navy);color:#fff;font-weight:600}
.modal-foot .btn.danger{background:var(--stop);color:#fff}

.receipt{background:var(--tape);color:var(--ink);font-family:var(--mono);font-size:12px;padding:18px;border-radius:10px;line-height:1.55}
.receipt h4{font-family:var(--ui);font-size:15px;margin:0 0 2px;text-align:center}
.receipt .ctr{text-align:center;font-size:11px;opacity:.6;margin-bottom:10px}
.receipt hr{border:0;border-top:1px dashed var(--tape-line);margin:8px 0}
.rl{display:flex;justify-content:space-between;gap:10px}
.rl span:last-child{font-variant-numeric:tabular-nums}

/* Printing a cheque: window.print() today, a real receipt printer later —
   either way only the .receipt itself should hit paper, not the app chrome
   around it. */
@media print{
  #connBanner,.app,.toast,.login-view,.revenue-centre-picker{display:none!important}
  .scrim{position:static!important;inset:auto!important;background:none!important;padding:0!important;display:block!important}
  .modal{box-shadow:none!important;border:none!important;max-width:100%!important;max-height:none!important;padding:0!important;border-radius:0!important;background:#fff!important;overflow:visible!important}
  .modal>*:not(.receipt){display:none!important}
  .receipt{border-radius:0!important;font-size:13px}
}



.tender{display:flex;flex-direction:column;gap:10px}
.tender .quick{display:flex;gap:7px;flex-wrap:wrap}
.tender .quick button{padding:10px 13px;border-radius:8px;background:var(--panel-2);border:1px solid var(--line);font-family:var(--mono);font-size:13px}
.tender input{width:100%;padding:12px;border-radius:8px;background:var(--shell);border:1px solid var(--line);color:var(--text);font-family:var(--mono);font-size:18px}
/* The $+LBP tender's amount + method pairs — input stretches, dropdown keeps its width.
   Deliberately slimmer than the single-tender input (owner: the full-size rows ate the box). */
.tender .mix-row{display:flex;gap:6px;margin-bottom:6px}
.tender .mix-row input{flex:1;min-width:0;padding:7px 10px;font-size:14px}
.tender .mix-ccy{width:84px;padding:7px 6px;border-radius:8px;background:var(--shell);border:1px solid var(--line);color:var(--text);font-family:var(--mono);font-size:13px}
.tip-custom-in{width:100%;padding:10px;border-radius:8px;background:var(--shell);border:1px solid var(--line);color:var(--text);font-family:var(--mono);font-size:14px;margin-top:8px}
.tip-note{color:var(--text-dim);font-size:11px;font-weight:400}
.change{font-family:var(--mono);font-size:14px;padding:10px;border-radius:8px;background:var(--panel-2)}
/* Payment window (owner, 2026-09-05): every control the same height as the Send button, the
   custom-tip box gets air on both sides, and the cash change can be split across both currencies. */
.modal .opt,.modal .quick button,.modal .mix-ccy{padding:4px 10px;min-height:30px;font-size:12.5px;line-height:1.2;box-sizing:border-box}
.modal .tender input,.modal .mix-row input,.modal .change,.modal .tip-custom-in,.modal .ret-row input{padding:5px 10px;min-height:32px;font-size:12.5px;line-height:1.2;box-sizing:border-box}
.modal .tip-custom-in{margin:8px 0}
/* Cancel and the other plain modal buttons in the light navy (owner, 2026-09-05). */
.modal-foot .btn{background:var(--navy-2);color:#fff}
.modal .tender input{margin:0}
.whish-pay{display:flex;gap:12px;align-items:center;margin-top:8px}
.whish-qr{width:150px;height:150px;object-fit:contain;background:#fff;border:1px solid var(--line);border-radius:8px;flex:0 0 auto}
.whish-pay .change{flex:1}
.ret-row{display:flex;gap:6px;margin-top:6px;align-items:center}
.ret-row label{flex:1;display:flex;flex-direction:column;gap:3px;font-size:11px;color:var(--text-dim);letter-spacing:.04em;text-transform:uppercase}
.ret-row input{width:100%;padding:7px 10px;min-height:36px;border-radius:8px;background:var(--shell);border:1px solid var(--line);color:var(--text);font-family:var(--mono);font-size:13px;box-sizing:border-box}
.change b{color:var(--brass)}

/* ---------- split payment (pos.js) ---------- */
.split-lines,.split-legs{display:flex;flex-direction:column;gap:8px;margin-bottom:14px;max-height:44vh;overflow-y:auto}
.split-line-row{display:flex;align-items:center;gap:10px;padding:10px 12px;border-radius:8px;background:var(--panel-2);border:1px solid var(--line);cursor:pointer;font-size:13px}
.split-line-row .pr{font-family:var(--mono);color:var(--brass);margin-left:auto}
.split-assignee{font-size:11px;color:var(--text-dim);min-width:76px;text-align:right}
.split-leg-row{padding:11px 12px;border-radius:8px;background:var(--panel-2);border:1px solid var(--line)}
.split-leg-row .split-leg-hd{display:flex;align-items:center;gap:8px;margin-bottom:9px;font-size:13.5px;font-weight:500}
.split-leg-row .split-leg-hd .pr{font-family:var(--mono);color:var(--brass);margin-left:auto}

/* pointer-events:none because a toast is purely informational — it has no click handler
   anywhere, and while it sat over a button (the guest page's "View order" bar) it silently
   swallowed taps meant for what was underneath. */
.toast{position:fixed;bottom:22px;left:50%;transform:translateX(-50%);background:var(--panel-2);border:1px solid var(--brass);padding:11px 18px;border-radius:999px;font-size:13px;z-index:60;max-width:88vw;text-align:center;pointer-events:none}
.toast[hidden]{display:none}
/* On the guest page the toast must clear the fixed bottom bar (View order / Make Payment),
   which sits at bottom:14px and stands ~46px tall — "Added X" was landing right on top of it. */
.order-body .toast{bottom:calc(76px + env(safe-area-inset-bottom, 0px))}

.check-toggle{display:none}
@media (max-width:880px){
  /* Phones: the cheque is a bottom sheet, fully hidden until a tab is tapped; the tab strip
     runs along the bottom edge instead of the right. */
  .check{position:fixed;inset:auto 0 56px 0;right:0;width:auto;max-width:none;height:72dvh;z-index:30;border-radius:16px 16px 0 0;transform:translateY(calc(100% + 60px));transition:transform .22s;box-shadow:0 -8px 30px rgba(0,0,0,.5)}
  .check.open{transform:translateY(0)}
  .cheque-tabs{position:fixed;left:0;right:0;bottom:0;height:56px;flex:none;flex-direction:row;border-left:0;border-top:1px solid var(--line);padding:5px 8px;overflow-x:auto;overflow-y:hidden}
  .cheque-tab{width:68px;height:40px;border-radius:12px 12px 0 0;border-left:1px solid var(--line);border-bottom:0;margin-bottom:0;margin-right:-10px}
  .cheque-tab:last-child{margin-right:0}
  .cheque-tab.on{width:72px}
  .cheque-tab.closed{margin-top:0;margin-left:auto;margin-right:0;height:40px;width:78px;border-radius:8px}
  /* padding-bottom reserves room for the collapsed check-toggle bar (position:fixed, full
     width, pinned to the bottom) so it doesn't sit on top of whatever's at the bottom of the
     sidebar — Logout, right below Settings — the same overlap .grid's own padding-bottom below
     already exists to avoid for the menu tiles. */
  .cats-col{flex:0 0 96px;padding-bottom:70px}
  .grid{padding-bottom:80px;grid-template-columns:repeat(auto-fill,minmax(128px,1fr))}

  .topbar{flex-wrap:wrap;row-gap:8px;gap:8px;padding:8px 10px}
  /* Scoped to .topbar specifically — order.html's .order-hd reuses the same .brand class for its
     own, differently-structured header (a column flex layout, not a wrapping row), and this same
     rule unscoped used to also apply there: flex-basis:100% on a column child of an auto-height
     container inflated it to ~2.5x its real content height, pushing everything after it (the
     "Just here to pay?" link) down past the header's own box and into the category row below. */
  .topbar .brand{flex:1 0 100%;flex-direction:row;align-items:baseline;gap:6px}
  .open-tables{margin-left:0;max-width:none;flex:1 1 auto;min-width:0}
  .btn-newtable,.btn-cheques{flex:0 0 auto;margin-left:0}
  .session-bar{margin-left:auto}
  .open-tables,.btn-newtable,.btn-cheques,.session-revenue-centre,.session-user,.btn-admin{padding:6px 9px;font-size:11px}
}
@media (max-width:420px){
  .session-revenue-centre{display:none!important}
  /* Scoped to .topbar for the same reason as above — order.html's guests need to keep seeing
     their table number here, it isn't just decorative the way it is on the POS topbar. */
  .topbar .brand span{display:none}
}
@media (prefers-reduced-motion:reduce){*{transition:none!important}}

/* ================= ADMIN ================= */
.admin{flex:1;display:flex;min-height:0}
.admin[hidden]{display:none}
.a-tabs{flex:0 0 168px;background:var(--panel);border-right:1px solid var(--line);display:flex;flex-direction:column;padding:10px 8px;gap:4px;overflow-y:auto}
.a-tab{padding:11px 12px;border-radius:var(--r);text-align:left;font-size:13px;color:var(--text-dim);border:1px solid transparent;transition:background .12s,color .12s,border-color .12s}
/* SVG-icon tabs (Clock In / Settings / Logout) — inline SVG instead of Unicode symbol glyphs,
   which can render as tofu boxes on a machine whose fonts don't cover that codepoint. */
.icon-tab{display:flex;align-items:center;gap:8px}
.icon-tab svg{flex:none}
/* The cursor is already a hand (buttons get it globally) — what was missing is any sign the
   thing under the pointer reacts, which is what makes a sidebar feel clickable. */
.a-tab:hover{background:var(--panel-2);color:var(--text);border-color:var(--line)}
.a-tab.on{background:var(--panel-2);color:var(--text);border-color:var(--line)}
.a-tab.on:hover{border-color:var(--brass)}
.a-subnav{display:flex;gap:8px;margin-bottom:16px}
.a-subtab{padding:8px 14px;border-radius:8px;font-size:12.5px;color:var(--text-dim);background:var(--panel);border:1px solid var(--line);transition:color .12s,border-color .12s}
.a-subtab:hover{color:var(--text);border-color:var(--brass)}
.a-subtab.on{background:var(--panel-2);color:var(--brass);border-color:var(--brass)}
/* Reports tab — a second-level nav (by report group) alongside the main tab strip, since ~15
   report types in one flat sub-nav (the pattern every other tab's sub-nav uses) would be
   unusable. Each group gets its own small subnav row rather than one giant flat list. */
.a-report-layout{display:flex;gap:22px;align-items:flex-start}
.a-report-nav{flex:0 0 190px;display:flex;flex-direction:column;gap:14px}
.a-report-group-label{margin:0 0 6px;font-size:10px;letter-spacing:.12em;text-transform:uppercase;color:var(--text-dim)}
.a-report-nav .a-subnav{flex-direction:column;gap:4px;margin-bottom:0}
.a-report-nav .a-subtab{text-align:left}
.a-report-main{flex:1;min-width:0}
.a-report-filters{display:flex;flex-wrap:wrap;align-items:end;gap:14px;margin-bottom:8px}
.a-report-filters label{display:flex;flex-direction:column;gap:4px;font-size:10px;letter-spacing:.1em;text-transform:uppercase;color:var(--text-dim)}
.a-nightaudit-panel{background:var(--panel);border:1px solid var(--line);border-radius:var(--r);padding:16px;margin-bottom:18px}
.a-nightaudit-status{display:flex;align-items:center;gap:10px;margin-bottom:10px}
.a-nightaudit-panel .a-hint{margin:8px 0 0}
.a-nightaudit-panel .mgroup{margin-top:14px;margin-bottom:0}
.a-report-filters input{padding:8px 10px;border-radius:8px;background:var(--shell);border:1px solid var(--line);color:var(--text);font-family:var(--ui);font-size:13px;color-scheme:dark}
.a-report-export{display:flex;gap:8px;margin-left:auto}
.a-report-export .btn.ghost{color:var(--text)}
/* Same fix as the export bar above: ghost buttons inherit .btn's ink color (meant for tan
   fills) and vanish on the dark theme — the drill view's Back/Reprint row needs theme text. */
.a-report-reprint .btn.ghost{color:var(--text)}
/* Numeric report columns read right-aligned with lined-up digits, the way ledgers do. */
.a-report-table th.num,.a-report-table td.num{text-align:right;font-variant-numeric:tabular-nums}
.a-report-title{margin:4px 0 14px;font-size:15px;color:var(--text)}
.a-report-table-wrap{overflow-x:auto;border:1px solid var(--line);border-radius:var(--r)}
.a-report-table{width:100%;border-collapse:collapse;font-size:12.5px;white-space:nowrap}
.a-report-table th,.a-report-table td{padding:9px 12px;text-align:left;border-bottom:1px solid var(--line)}
.a-report-table th{color:var(--text-dim);font-weight:600;font-size:10.5px;letter-spacing:.06em;text-transform:uppercase;background:var(--panel)}
.a-report-table tbody tr:last-child td{border-bottom:0}
.a-report-table tbody tr:hover td{background:var(--panel-2)}
.a-report-reprint{display:flex;gap:10px;margin-top:16px}
@media (max-width:880px){
  .a-report-layout{flex-direction:column}
  .a-report-nav{flex-direction:row;flex-wrap:wrap;width:100%}
}
/* AI Assistant tab — a chat, not a report table, so it gets its own small set of rules rather
   than stretching .a-report-* to cover a bubble layout they were never meant for. */
.a-ai-layout{max-width:820px}
.a-ai-transcript{display:flex;flex-direction:column;gap:12px;min-height:220px;max-height:56vh;overflow-y:auto;border:1px solid var(--line);border-radius:var(--r);background:var(--panel);padding:16px;margin-bottom:12px}
.a-ai-msg{display:flex}
.a-ai-msg-user{justify-content:flex-end}
.a-ai-bubble{max-width:78%;padding:10px 14px;border-radius:12px;font-size:13.5px;line-height:1.5;background:var(--panel-2);border:1px solid var(--line);color:var(--text)}
.a-ai-msg-user .a-ai-bubble{background:var(--brass);border-color:var(--brass);color:#1a1408}
.a-ai-msg-error .a-ai-bubble{border-color:#b0453a;color:#e8897c}
.a-ai-bubble p{margin:0 0 8px}
.a-ai-bubble p:last-child{margin-bottom:0}
.a-ai-bubble ul{margin:0 0 8px;padding-left:18px}
.a-ai-bubble table{width:100%;border-collapse:collapse;font-size:12.5px;margin:4px 0 8px}
.a-ai-bubble th,.a-ai-bubble td{padding:6px 9px;text-align:left;border-bottom:1px solid var(--line)}
.a-ai-bubble th{color:var(--text-dim);font-weight:600;font-size:10.5px;letter-spacing:.05em;text-transform:uppercase}
.a-ai-typing{font-size:12px;color:var(--text-dim);margin:-4px 0 12px;font-style:italic}
.a-ai-chart{margin-top:6px;max-width:520px}
.a-ai-chart-title{margin:0 0 4px;font-size:11px;color:var(--text-dim)}
.a-ai-chart-legend{display:flex;flex-wrap:wrap;gap:12px;margin-top:4px}
.a-ai-chart-legend span{display:flex;align-items:center;gap:5px;font-size:11px;color:var(--text-dim)}
.a-ai-chart-legend i{display:inline-block;width:9px;height:9px;border-radius:2px}
.a-ai-actions{display:flex;gap:8px;margin-top:10px}
.a-ai-actions .btn.ghost{font-size:11.5px;padding:6px 10px;color:var(--text-dim)}
.ai-chart-type-picker{display:flex;gap:10px;justify-content:center}
.a-ai-inputrow{display:flex;gap:10px;align-items:flex-end}
.a-ai-inputrow textarea{flex:1;resize:vertical;min-height:44px;padding:10px 12px;border-radius:8px;background:var(--shell);border:1px solid var(--line);color:var(--text);font-family:var(--ui);font-size:13px}
.screen-editor{display:grid;grid-template-columns:1fr 1fr;gap:20px;align-items:start}
.screen-picker{display:flex;flex-direction:column;gap:10px}
.screen-picker input[type=text]{padding:9px 10px;border-radius:8px;background:var(--shell);border:1px solid var(--line);color:var(--text);font-family:var(--ui);font-size:13px}
.screen-picker-list{max-height:52vh;overflow-y:auto}
.screen-pick-row{cursor:grab}
.screen-pick-row.a-row-disabled{opacity:.4;pointer-events:none}
/* The item number, shown in the picker and in the placed-entries list — monospace and fixed
   width so the numbers line up into a readable column instead of drifting with name length. */
.screen-pick-num{font-family:var(--mono);font-size:11px;color:var(--text-dim);min-width:34px;display:inline-block;text-align:right;margin-right:8px}
/* From #/To # bulk add — mirrors the item grids' own "+ Add range" row. */
.screen-pick-range{display:flex;align-items:center;gap:6px;margin:8px 0;flex-wrap:wrap}
.screen-pick-range label{font-size:11px;color:var(--text-dim);text-transform:uppercase;letter-spacing:.06em}
.screen-pick-range input{width:72px;padding:6px 8px;border-radius:6px;background:var(--shell);border:1px solid var(--line);color:var(--text);font-family:var(--mono);font-size:12px}
.screen-pick-range .btn{width:auto;padding:6px 12px;font-size:12px}
/* Both lists here are long — a whole outlet's menu on the left, a whole screen's contents on
   the right — so the rows are deliberately tighter than the standard .a-row/.a-checkrow used
   elsewhere in Admin: more of the menu fits on screen without scrolling, which is the whole
   job of this editor. Scoped to the screen editor so other tabs keep their roomier rows. */
.screen-contents{display:flex;flex-direction:column;gap:4px;min-height:120px;padding:6px;border-radius:var(--r);border:1px dashed var(--line);max-height:52vh;overflow-y:auto}
.screen-entry-row{cursor:grab;padding:5px 10px}
.screen-entry-row .a-nm{font-size:12.5px}
.screen-entry-row .a-meta{font-size:10.5px}
.screen-entry-row .a-actions button{width:26px;padding:3px 0;font-size:11px}
.screen-picker-list .screen-pick-row{padding:4px 8px;margin-bottom:3px}
.screen-picker-list .a-cn{font-size:12.5px}
.screen-picker-list .a-meta{font-size:10.5px}
/* Sits where the old read-only preview used to, at the bottom of the editor — the way into the
   Layout editor, which is now the single place the till grid is both seen and arranged. */
.screen-layout-cta{grid-column:1;display:flex;align-items:center;justify-content:space-between;gap:16px;flex-wrap:wrap;padding:14px 16px;border-radius:var(--r);border:1px dashed var(--line);background:var(--shell)}
.screen-layout-cta b{display:block;font-size:14px;margin-bottom:3px}
.screen-layout-cta p{margin:0 0 4px;font-size:12px;color:var(--text-dim);max-width:60ch}
.a-content{flex:1;overflow-y:auto;padding:18px 22px}
.a-head{display:flex;align-items:center;justify-content:space-between;margin-bottom:14px}
.a-head h2{margin:0;font-size:18px}
.a-head p{margin:2px 0 0;font-size:12px;color:var(--text-dim)}
.a-list{display:flex;flex-direction:column;gap:8px}
.a-row{display:flex;align-items:center;gap:10px;padding:11px 14px;border-radius:var(--r);background:var(--panel);border:1px solid var(--line)}
/* Clicking a row opens its editor (see admin.js's row->edit rule) — so say so on hover. Keyed
   off the row actually CONTAINING an Edit button, which is exactly the same condition the JS
   uses, so a row with no editor (Workstations) keeps the normal arrow and stays inert. */
.a-row:has(button[data-edit-serving-period]),
.a-row:has(button[data-edit-family-group]),
.a-row:has(button[data-edit-major-group]),
.a-row:has(button[data-edit-option-group]),
.a-row:has(button[data-edit-item-class]),
.a-row:has(button[data-edit-kds-class]),
.a-row:has(button[data-edit-printer]),
.a-row:has(button[data-edit-kds]),
.a-row:has(button[data-edit-user]),
.a-row:has(button[data-edit-role]),
.a-row:has(button[data-edit-revenue-centre]),
.a-row:has(button[data-open-screen]){cursor:pointer}
.a-row:has(button[data-edit-serving-period]):hover,
.a-row:has(button[data-edit-family-group]):hover,
.a-row:has(button[data-edit-major-group]):hover,
.a-row:has(button[data-edit-option-group]):hover,
.a-row:has(button[data-edit-item-class]):hover,
.a-row:has(button[data-edit-kds-class]):hover,
.a-row:has(button[data-edit-printer]):hover,
.a-row:has(button[data-edit-kds]):hover,
.a-row:has(button[data-edit-user]):hover,
.a-row:has(button[data-edit-role]):hover,
.a-row:has(button[data-edit-revenue-centre]):hover,
.a-row:has(button[data-open-screen]):hover{border-color:var(--brass)}
/* Inside such a row the controls keep their own meaning — a text field must still look typeable
   and a disabled button must not look clickable. */
.a-row input[type=text],.a-row input[type=number]{cursor:text}
.a-row button:disabled{cursor:default}
.a-row.inactive{opacity:.5}
.a-row .a-nm{font-size:13.5px;font-weight:500}
.a-row .a-nar{font-size:12px;color:var(--text-dim);margin-left:6px}
.a-tag{display:inline-block;margin-left:8px;padding:2px 8px;border-radius:999px;font-size:10.5px;letter-spacing:.03em}
.a-tag.warn{background:rgba(212,98,42,.15);border:1px solid var(--alert);color:var(--alert)}
.a-tag:not(.warn){background:rgba(184,151,90,.15);border:1px solid var(--brass);color:var(--brass)}
.mgroup-disabled>p{opacity:.55}
.pin-reveal{margin:14px 0;padding:18px;border-radius:var(--r);background:var(--shell);border:1px solid var(--line);text-align:center;font-family:var(--mono);font-size:32px;letter-spacing:.25em;color:var(--brass)}
.a-row .a-meta{font-family:var(--mono);font-size:11px;color:var(--text-dim);margin-left:auto}
.a-row select{padding:5px 8px;border-radius:6px;background:var(--panel-2);border:1px solid var(--line);color:var(--text);font-family:inherit;font-size:11.5px}
.ws-ip-input{width:132px;padding:5px 8px;border-radius:6px;background:var(--panel-2);border:1px solid var(--line);color:var(--text);font-family:var(--mono);font-size:11.5px}
.ws-ip-input::placeholder{color:var(--text-dim);opacity:.8}
.ws-ip-input:focus{border-color:var(--brass);outline:none}
.a-row .a-actions{display:flex;gap:6px}
.a-row .a-actions button{padding:6px 10px;border-radius:6px;background:var(--panel-2);border:1px solid var(--line);font-size:11px}
.a-row .a-actions button:hover{border-color:var(--brass);color:var(--brass)}
.a-row .a-actions button.danger:hover{border-color:var(--stop);color:var(--stop)}
.a-row .a-actions button:disabled{opacity:.35;pointer-events:none}
.a-cathd{font-size:10px;letter-spacing:.12em;text-transform:uppercase;color:var(--text-dim);margin:16px 0 6px}
.a-cathd:first-child{margin-top:0}
.a-empty{padding:24px;text-align:center;color:var(--text-dim);font-size:13px}
.a-field{margin-bottom:12px}
.a-field label{display:block;font-size:10px;letter-spacing:.1em;text-transform:uppercase;color:var(--text-dim);margin-bottom:5px}
.a-field input[type=text],.a-field input[type=number],.a-field input[type=time],.a-field select{width:100%;padding:10px;border-radius:8px;background:var(--shell);border:1px solid var(--line);color:var(--text);font-family:var(--ui);font-size:13px}
.a-field input[type=time]{color-scheme:dark}
.a-row2{display:flex;gap:10px}
.a-row2>.a-field{flex:1}

/* ---------- Menu Item spreadsheet grid ---------- */
.item-grid-config{display:flex;gap:12px;align-items:flex-end;flex-wrap:wrap;padding:12px 14px;background:var(--panel-2);border:1px solid var(--line);border-radius:var(--r);margin-bottom:14px}
.item-grid-config .a-field{margin-bottom:0;min-width:180px;flex:1}
/* The search box no longer eats the whole toolbar — it's a fixed, modest width so the add
   buttons and the Family Group picker sit right beside it instead of being pushed to the edge. */
.item-grid-config .a-field-search{flex:0 0 240px;min-width:160px}
.item-grid-range{display:flex;gap:10px;align-items:flex-end;flex-wrap:wrap;padding:12px 14px;background:var(--panel-2);border:1px dashed var(--line);border-radius:var(--r);margin-bottom:14px}
.item-grid-range .a-field{margin-bottom:0}
.item-grid-range .a-field input{width:110px}
.item-grid-wrap{overflow-x:auto;border:1px solid var(--line);border-radius:var(--r)}
/* table-layout:fixed — required for the <colgroup> widths set by manual column resize (see
   itemGridColgroupHtml/the mousedown handler in admin.js) to actually govern column sizing
   instead of just hinting at it. width:100% + a 9th unsized filler <col> (see
   itemGridColgroupHtml) is what lets the table always fill its container: the 8 real columns
   keep the exact literal pixel width they were dragged to, and the filler silently absorbs
   whatever's left — never the browser proportionally re-stretching every column to fill the
   gap, which would undo a manual resize. A total wider than the container still scrolls inside
   .item-grid-wrap's own overflow-x, same as before. */
.item-grid{width:100%;table-layout:fixed;border-collapse:collapse;font-size:12.5px}
.item-grid th{position:relative;text-align:left;font-size:10px;letter-spacing:.1em;text-transform:uppercase;color:var(--text-dim);padding:9px 8px;background:var(--panel-2);border-bottom:1px solid var(--line);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.item-grid td{padding:2px 5px;border-bottom:1px solid var(--line);vertical-align:middle;overflow:hidden;text-overflow:ellipsis}
.item-grid tr:last-child td{border-bottom:0}
.item-grid tr.inactive{opacity:.5}
/* Dragged from a header's right edge to resize that column — see the mousedown listener in
   admin.js. The hit area is a wider 8px strip (easy to grab without much precision), but only
   the thin ::after line down its middle is ever drawn, so the boundary itself is always visible
   as a fine separator rather than only appearing once you happen to hover over it. */
.item-grid th .col-resize-handle{position:absolute;top:0;right:0;width:8px;height:100%;cursor:col-resize;z-index:1}
.item-grid th .col-resize-handle::after{content:"";position:absolute;top:0;left:50%;width:1px;height:100%;background:var(--line)}
.item-grid th .col-resize-handle:hover::after,.item-grid th .col-resize-handle.active::after{background:var(--brass);width:2px}
.item-grid input[type=text],.item-grid input[type=number]{width:100%;padding:4px 8px;border-radius:6px;background:var(--shell);border:1px solid transparent;color:var(--text);font-family:var(--ui);font-size:12.5px}
.item-grid input[type=text]:focus,.item-grid input[type=number]:focus{border-color:var(--brass);outline:none}
.item-grid input.code-cell,.item-grid input.price-cell{font-family:var(--mono)}
/* Photo column — every menu picture visible straight from the grid, and each thumbnail is the
   button that opens that item's photo editor. Items with no photo of their own show a "+"; an
   outlet item falling back to the catalogue photo is faded so inherited never looks like set. */
.item-grid td.photo-cell{padding:3px 5px}
.item-grid /* Reads as the cell's text, behaves as a control: an option-group set doesn't fit a <select>,
   so the cell itself opens the picker. Underlined on hover so it's discoverably clickable. */
/* Licensing tab: install on the left, what-you-own on the right. Single column on narrow. */
.shutdown-wall{flex:1;display:flex;align-items:center;justify-content:center;padding:40px}
.shutdown-box{max-width:520px;text-align:center;background:var(--panel);border:1px solid var(--stop);border-radius:12px;padding:34px 38px}
.shutdown-box h2{margin:0 0 12px;color:var(--stop)}
.shutdown-box p{font-size:13.5px;line-height:1.6;color:var(--text-dim)}
.unlicensed-badge{background:var(--stop);color:#fff;font-size:10px;font-weight:700;letter-spacing:.12em;padding:4px 9px;border-radius:5px;margin-left:10px;flex:0 0 auto}
.lic-cols{display:grid;grid-template-columns:1fr 1fr;gap:28px;max-width:960px;margin-top:14px}
@media (max-width:900px){.lic-cols{grid-template-columns:1fr}}
.lic-col{background:var(--panel);border:1px solid var(--line);border-radius:var(--r);padding:16px}
.lic-h{margin:0 0 12px;font-size:13px;letter-spacing:.08em;text-transform:uppercase;color:var(--text-dim)}
.lic-ok{padding:9px 12px;border-radius:var(--r);background:rgba(60,140,80,.12);border:1px solid rgba(60,140,80,.4);font-size:12.5px;max-width:960px}
.lic-fact{display:flex;justify-content:space-between;gap:12px;padding:7px 0;border-bottom:1px dotted var(--line);font-size:12.5px}
.lic-fact b{text-align:right}
.lic-modules{margin-top:14px;display:flex;flex-direction:column;gap:6px}
.lic-module{display:flex;align-items:center;gap:10px;padding:9px 12px;border-radius:8px;background:var(--panel-2);border:1px solid var(--line);font-size:13px;opacity:.55}
.lic-module.on{opacity:1;border-color:rgba(60,140,80,.5)}
.lic-module-mark{font-weight:700;width:16px;text-align:center}
.lic-module-exp{margin-left:auto;font-size:11.5px;color:var(--text-dim);font-variant-numeric:tabular-nums}
.lic-module.on .lic-module-mark{color:#3c8c50}
.lic-module:not(.on) .lic-module-mark{color:var(--stop)}
.screen-order-row{cursor:grab}
.screen-order-row:active{cursor:grabbing}
.drag-grip{color:var(--text-dim);font-size:13px;cursor:grab;user-select:none}
.og-cell{background:none;border:0;padding:0;font:inherit;color:inherit;text-align:left;cursor:pointer;width:100%}
.og-cell:hover{color:var(--brass);text-decoration:underline}
.photo-thumb{width:44px;height:44px;padding:0;border-radius:6px;overflow:hidden;background:var(--panel-2);border:1px solid var(--line);display:flex;align-items:center;justify-content:center;cursor:pointer}
.item-grid .photo-thumb img{width:100%;height:100%;object-fit:cover;display:block}
.item-grid .photo-thumb span{color:var(--text-dim);font-size:16px;line-height:1}
.item-grid .photo-thumb:hover{border-color:var(--brass)}
.item-grid .photo-thumb.inherited img{opacity:.55}
/* A locked name (sold before, or disabled — see nameCellHtml/assertRenamable): reads as plain
   text rather than an editable field, so it's obvious before you click into it. Hovering shows
   the reason. */
.item-grid input[type=text][readonly]{background:transparent;color:var(--text-dim);cursor:default}
.item-grid input[type=text][readonly]:focus{border-color:transparent}
.item-grid select{width:100%;min-width:0;padding:4px 8px;border-radius:6px;background:var(--shell);border:1px solid transparent;color:var(--text);font-family:var(--ui);font-size:12px}
.item-grid select:focus{border-color:var(--brass);outline:none}
.item-grid .item-grid-actions{display:flex;gap:4px;white-space:nowrap}
.item-grid .item-grid-actions button{padding:5px 8px;font-size:11px;border-radius:6px;background:var(--panel-2);border:1px solid var(--line)}
.item-grid .item-grid-actions button:hover{border-color:var(--brass);color:var(--brass)}
.item-grid .item-grid-actions button.danger:hover{border-color:var(--stop);color:var(--stop)}
.item-grid .item-grid-actions button:disabled{opacity:.35;pointer-events:none}
/* This outlet's own Menu Items grid only (data-context-grid="menuItem") — its More…/Disable
   buttons are tapped constantly on the floor, so they get a larger touch target than Full
   Menu's own single "More…" button, which shares the same base .item-grid-actions styling. */
table[data-context-grid="menuItem"] .item-grid-actions button{padding:6px 12px;font-size:12.5px}
.item-grid .tiers-note{font-family:var(--mono);font-size:11px;color:var(--text-dim);white-space:nowrap}
.item-grid td.major-group-cell{color:var(--text-dim);font-size:12px;white-space:nowrap}
.item-grid td.code-cell{font-family:var(--mono);font-size:12.5px;padding-left:8px;white-space:nowrap;cursor:default}
.item-grid .outlet-chips{display:flex;flex-wrap:wrap;gap:3px;max-width:160px}
.item-grid .outlet-chip{padding:2px 6px;border-radius:20px;background:var(--panel-2);border:1px solid var(--line);font-size:10px;color:var(--text-dim);white-space:nowrap}
.item-grid .outlet-chip.here{border-color:var(--brass);color:var(--brass)}
.item-grid .import-btn{padding:5px 8px;font-size:11px;border-radius:6px;background:var(--panel-2);border:1px solid var(--line);white-space:nowrap}
.item-grid .import-btn.on{border-color:var(--brass);color:var(--brass)}
.item-grid .import-btn:hover{border-color:var(--brass);color:var(--brass)}

.ctx-menu{position:fixed;z-index:500;min-width:150px;background:var(--panel-2);border:1px solid var(--line);border-radius:12px;box-shadow:0 8px 24px rgba(0,0,0,.35);padding:5px;font-size:12.5px}
.ctx-menu button{display:block;width:100%;text-align:left;padding:8px 10px;border-radius:6px;background:transparent;color:var(--text)}
.ctx-menu button:hover{background:var(--panel)}
.ctx-menu button.danger:hover{color:var(--stop)}
.ctx-menu-insert{position:fixed;z-index:500;background:var(--panel-2);border:1px solid var(--line);border-radius:12px;box-shadow:0 8px 24px rgba(0,0,0,.35);padding:14px;display:flex;gap:10px;align-items:flex-end;flex-wrap:wrap;max-width:360px}
.ctx-menu-insert .a-field{margin-bottom:0}
.ctx-menu-insert .a-field input{width:90px}
.ctx-menu-insert p.a-hint{width:100%;margin:0 0 2px}
.a-variant-row,.a-comp-row{display:flex;gap:8px;align-items:center;margin-bottom:6px}
.a-variant-row input{flex:1}
.a-variant-row button,.a-comp-row button{flex:0 0 auto;padding:8px 10px;border-radius:6px;background:var(--panel-2);border:1px solid var(--line);font-size:12px}
.a-addbtn{margin-top:4px;padding:6px 12px;border-radius:6px;min-height:30px;background:var(--panel-2);border:1px dashed var(--line);font-size:12px;color:var(--text-dim)}
.a-addbtn:hover{border-color:var(--brass);color:var(--brass)}
.a-addbtn:disabled{opacity:.4;cursor:not-allowed}
.a-addbtn:disabled:hover{border-color:var(--line);color:var(--text-dim)}
.a-checkrow{display:flex;align-items:center;gap:10px;padding:8px 10px;border-radius:8px;background:var(--panel-2);border:1px solid var(--line);margin-bottom:6px}
.a-checkrow input[type=checkbox]{width:16px;height:16px}
.a-checkrow .a-cn{flex:1;font-size:13px}
.a-checkrow .a-removable{display:flex;align-items:center;gap:6px;font-size:11px;color:var(--text-dim)}
.a-checklist{display:grid;grid-template-columns:repeat(auto-fill,minmax(240px,1fr));gap:0 12px}
.a-warn{padding:10px 12px;border-radius:8px;background:rgba(212,98,42,.12);border:1px solid var(--alert);font-size:12.5px;margin-bottom:12px}
.a-formerr{padding:10px 12px;border-radius:8px;background:rgba(212,98,42,.12);border:1px solid var(--alert);font-size:12.5px;margin-bottom:12px}
/* Every other .a-formerr is only ever added to the DOM once there's a real message to show — the
   one exception is the Licensing tab's #stLicenseErr, left in the markup empty and filled in later
   on an install failure, which meant an empty box (padding+border+background, no text) sat there
   permanently. :empty catches that case generically, so the same mistake can't quietly reappear
   somewhere else the same way. */
.a-formerr:empty{display:none}
.a-hint{font-size:12px;color:var(--text-dim);margin:0 0 12px}
.a-headbtns{display:flex;gap:8px}
.a-headbtns .a-addbtn{margin-top:0}
.a-code{font-family:var(--mono);font-size:11.5px;color:var(--text-dim);margin-right:8px}

/* ---------- Admin on a phone/small tablet: the 168px text-label sidebar has no room to live
   next to content, so it becomes a horizontal scrollable strip along the top instead (same
   breakpoint the till uses for its own mobile layout, above). Everything else here just
   un-crowds rows/forms/grids that were laid out assuming desktop width. ---------- */
@media (max-width:880px){
  .admin{flex-direction:column}
  .a-tabs{flex:0 0 auto;flex-direction:row;overflow-x:auto;overflow-y:hidden;border-right:0;border-bottom:1px solid var(--line);-webkit-overflow-scrolling:touch}
  .a-tabs .a-tab{flex:0 0 auto;white-space:nowrap}
  .a-tabs .a-tab[data-tab="__exit"]{margin-top:0!important;margin-left:auto!important}
  .a-content{padding:14px 16px 28px}
  .a-head{flex-wrap:wrap;row-gap:10px}
  .a-row{flex-wrap:wrap;row-gap:8px}
  .a-row .a-meta{flex:1 1 100%;margin-left:0;order:2}
  .a-row .a-actions{order:3;flex:1 1 100%;flex-wrap:wrap;justify-content:flex-end}
  .a-row2{flex-direction:column;gap:0}
  .a-subnav{flex-wrap:wrap;row-gap:8px}
  .screen-editor{grid-template-columns:1fr}
}
@media (max-width:480px){
  .a-content{padding:12px 12px 24px}
  .item-grid-config .a-field,.item-grid-config{min-width:0}
  .item-grid-config .a-field{flex:1 1 128px}
}
/* Below 420px the till's own topbar rule (above) hides .session-revenue-centre outright to save
   space — fine there, since the till mostly stays on the RC it was logged into. In Admin it's a
   <select> and the only way a multi-RC admin switches location, so it needs to stay reachable
   even on a small phone; this wins on specificity over that rule despite both using !important. */
@media (max-width:420px){
  .admin-topbar .session-revenue-centre{display:block!important;flex:1 1 auto;min-width:0}
}

/* ================= LOGIN / REVENUE CENTRE PICKER =================
   The logo PNG (logo-v3.png) is fully transparent outside its mark, so it sits cleanly on either
   theme's background with no seam — this gradient just needs to track --shell/--panel the same
   way the rest of the app does, via the --login-* tokens defined in :root above. */
.login-view,.revenue-centre-picker,.workstation-setup{position:fixed;inset:0;z-index:70;background:var(--login-bg);display:flex;flex-direction:column;align-items:center;justify-content:center;gap:32px;padding:24px}
.login-view[hidden],.revenue-centre-picker[hidden],.workstation-setup[hidden]{display:none}

.login-logo{width:min(80vw,440px);height:auto;filter:drop-shadow(0 16px 34px rgba(0,0,0,.55))}

/* Small corner control, page-level (not nested in any one screen) — lets anyone reaching this
   server's public URL (e.g. an ngrok tunnel) grab the desktop installers without needing to log
   in first, same reasoning as /downloads itself being unauthenticated. z-index above the
   login-view/revenue-centre-picker/workstation-setup overlays' own 70 so it stays reachable no
   matter which of those is currently covering the screen — including the very first thing a
   fresh install shows, the workstation-setup PIN screen, which sits on top of everything else. */
.dl-wrap{position:fixed;top:10px;right:10px;z-index:80}
.dl-btn{padding:6px 11px;border-radius:999px;background:var(--panel-2);border:1px solid var(--line);color:var(--text-dim);font-family:inherit;font-size:11px;cursor:pointer;white-space:nowrap}
.dl-btn:hover{color:var(--text);border-color:var(--text-dim)}
.dl-menu{position:absolute;top:calc(100% + 8px);right:0;min-width:170px;background:var(--login-menu-bg);backdrop-filter:blur(6px);border:1px solid var(--line);border-radius:12px;padding:6px;box-shadow:0 20px 50px rgba(0,0,0,.5);display:flex;flex-direction:column;gap:2px}
.dl-menu[hidden]{display:none}
.dl-menu a{padding:9px 12px;border-radius:8px;color:var(--text);text-decoration:none;font-size:13px;display:block}
.dl-menu a:hover{background:var(--panel-2)}

.login-card{width:100%;max-width:320px;padding:28px;background:var(--login-card-bg);backdrop-filter:blur(6px);border:1px solid var(--line);border-radius:12px;text-align:center;box-shadow:0 20px 50px rgba(0,0,0,.5)}
/* .btn.ghost has no color of its own (see its own rule) since it sits on backgrounds the rest of
   the app doesn't share a class with (the login card here, the till's own check panel elsewhere
   for .check/.pay) — each context sets it explicitly rather than one value trying to fit both.
   var(--text) is correct in both themes here since the login card now tracks theme too. */
.login-card .btn.ghost{color:var(--text)}
.login-brand{margin-bottom:20px}
.login-brand b{display:block;font-size:19px;letter-spacing:.02em}
.login-brand span{display:block;font-size:11px;color:var(--text-dim);letter-spacing:.1em;text-transform:uppercase;margin-top:4px}
.country-card{max-width:380px}
.country-card .login-brand span{text-transform:none;letter-spacing:normal;font-size:12.5px;margin-top:6px;line-height:1.4}
.country-options{display:flex;flex-direction:column;gap:10px}
.country-opt{display:flex;align-items:center;gap:14px;width:100%;padding:16px 18px;border-radius:12px;background:var(--login-opt-bg);border:1px solid var(--line);color:var(--text);font-size:15px;font-weight:600;text-align:left;cursor:pointer}
.country-opt:hover{border-color:var(--brass);background:var(--login-opt-bg-hover)}
.country-flag{font-size:28px;line-height:1}
.pin-dots{display:flex;justify-content:center;gap:14px;margin-bottom:10px}
/* The login screen's outlet dropdown — same quiet glassy language as the login option rows. */
.login-rc-select{width:100%;margin-bottom:14px;padding:12px 14px;border-radius:10px;background:var(--login-opt-bg);border:1px solid var(--line);color:var(--text);font-family:inherit;font-size:14px;font-weight:600;text-align:center}
.login-rc-select:focus{border-color:var(--brass);outline:none}
.login-rc-select[hidden]{display:none}
.pin-dots span{width:14px;height:14px;border-radius:50%;border:2px solid var(--line);background:transparent}
.pin-dots span.filled{background:var(--brass);border-color:var(--brass)}
.pin-error{color:var(--stop);font-size:12px;font-weight:600;min-height:16px;margin-bottom:10px}
.pin-error[hidden]{visibility:hidden}
.pin-pad{display:grid;grid-template-columns:repeat(3,1fr);gap:10px}
.pin-pad button{padding:16px 0;border-radius:10px;background:var(--panel-2);border:1px solid var(--line);font-size:18px;font-family:var(--mono)}
.pin-pad button:active{transform:scale(.96);border-color:var(--brass)}
.pin-pad button.ghost{font-size:12px;font-family:var(--ui);color:var(--text-dim)}
.revenue-centre-list{display:flex;flex-direction:column;gap:10px;margin-bottom:16px}
.revenue-centre-list button{padding:16px;border-radius:10px;background:var(--panel-2);border:1px solid var(--line);font-size:15px;text-align:left}

/* ---------- workstation setup (src/shared/workstationSetup.js) — reuses .pin-dots/.pin-pad as-is
   (6 dots instead of 4, same visual language as login) for the PIN entry; this is just the
   confirmation screen's centered name display. ---------- */
.ws-setup-name{text-align:center;font-size:28px;font-weight:700;color:var(--brass);letter-spacing:.01em;padding:18px 10px;word-break:break-word}

/* ================= CUSTOMER ORDER (order.html) =================
   Phone-first by default (customers are almost always on a phone), reusing
   .tile/.cats/.opt/.modal/.ln-* as-is for anything that isn't page layout.
   A desktop breakpoint below frames this same column as a floating card on
   a branded backdrop instead of stranding a bare phone-width layout in a
   sea of flat background — a guest who scans the QR code from a tablet or
   a laptop at the table still gets an intentional-looking page. */
.order-body{overflow-y:auto;display:flex;justify-content:center;background:var(--shell)}
.order-state{width:100%;max-width:520px;min-height:100dvh;display:flex;flex-direction:column;align-items:center;justify-content:center;padding:24px;text-align:center}
.order-state[hidden]{display:none}
.order-spinner{display:flex;flex-direction:column;align-items:center}
.order-spinner h2{margin:0 0 8px;font-size:18px}
.order-spinner p{margin:0;color:var(--text-dim);font-size:13.5px;max-width:38ch}
.order-logo-lg{width:min(46vw,140px);height:auto;margin-bottom:18px;filter:drop-shadow(0 10px 22px rgba(0,0,0,.45))}
.order-app{width:100%;max-width:520px;margin:0 auto;display:flex;flex-direction:column;min-height:100dvh}
.order-app[hidden]{display:none}
@media (min-width:701px){
  .order-body{background:var(--login-bg)}
  .order-app,.order-state{max-width:560px;min-height:calc(100dvh - 48px);margin:24px auto;background:var(--shell);border:1px solid var(--line);border-radius:20px;box-shadow:0 30px 80px rgba(0,0,0,.35);overflow:hidden}
}
/* "Your order's ready" — fixed above everything (even the header) so it's seen no matter where
   on the page the guest is when it lands; stays until they dismiss it, not timed like a toast,
   since this is worth more than a few seconds' notice. See order.js's showReadyBanner. */
.order-ready-banner{position:fixed;top:0;left:0;right:0;z-index:90;display:flex;align-items:center;justify-content:space-between;gap:10px;padding:12px 16px;padding-top:calc(12px + env(safe-area-inset-top, 0px));background:var(--brass);color:var(--ink);font-weight:600;font-size:13.5px;box-shadow:0 6px 20px rgba(0,0,0,.35)}
.order-ready-banner[hidden]{display:none}
.order-ready-banner button{flex:0 0 auto;width:26px;height:26px;border-radius:50%;background:rgba(0,0,0,.12);border:0;color:inherit;font-size:13px;line-height:1;display:flex;align-items:center;justify-content:center;padding:0}
/* env(safe-area-inset-top) on the header (and the corner buttons/ready banner below): when this
   page runs as an installed Home Screen app, apple-mobile-web-app-status-bar-style
   black-translucent (see order.html) draws the page BEHIND the iPhone status bar/notch — without
   this padding the top strip of the header (the 🔔 bell, the theme toggle) sat invisible under
   the clock, and the whole page read as "bigger than the screen." Resolves to 0 in a normal
   browser tab, so nothing changes there. */
.order-hd{position:relative;padding:16px;padding-top:calc(16px + env(safe-area-inset-top, 0px));background:var(--panel);border-bottom:1px solid var(--line);flex:0 0 auto;display:flex;flex-direction:column;align-items:center;gap:8px;text-align:center}
/* .brand span is shared with the till's topbar (10px there is fine at arm's length on a fixed
   till screen) — bumped past the 11px legibility floor here only, where a guest reads it one-handed
   on a phone, without touching the shared rule the till still uses. */
.order-hd .brand span{font-size:11.5px}
/* Sun/moon theme toggle, same convention as the desktop app: the icon shows what you GET by
   tapping — a sun while the page is dark, a moon while it's light. Flips /shared/theme.js's
   data-theme attribute; the guest's choice persists per phone via localStorage. */
.order-theme-toggle{position:absolute;top:calc(12px + env(safe-area-inset-top, 0px));right:12px;width:34px;height:34px;border-radius:50%;background:var(--panel-2);border:1px solid var(--line);font-size:15px;line-height:1;display:flex;align-items:center;justify-content:center;padding:0}
.order-theme-toggle:active{transform:scale(.9)}
/* Mirrors .order-theme-toggle on the opposite corner — see order.js's subscribeToPush. */
.order-bell-toggle{position:absolute;top:calc(12px + env(safe-area-inset-top, 0px));left:12px;width:34px;height:34px;border-radius:50%;background:var(--panel-2);border:1px solid var(--line);font-size:15px;line-height:1;display:flex;align-items:center;justify-content:center;padding:0}
.order-bell-toggle[hidden]{display:none}
.order-bell-toggle:active{transform:scale(.9)}
.order-logo{height:32px;width:auto}
.order-settle-link{font-size:11px;color:var(--text-dim);text-decoration:underline;background:none;border:none}
.order-settle-link[hidden]{display:none}
/* mask fade on the trailing edge hints there's more to scroll when a category list runs off-screen
   (e.g. an outlet with more than ~5 categories) — a guest who never tries a sideways swipe on a
   nav row would otherwise never discover the later categories exist. */
.order-app .cats{flex:0 0 auto;flex-direction:row;overflow-x:auto;padding:10px 12px;border-right:0;border-bottom:1px solid var(--line);-webkit-mask-image:linear-gradient(to right,#000 calc(100% - 28px),transparent 100%);mask-image:linear-gradient(to right,#000 calc(100% - 28px),transparent 100%)}
.order-app .cat{flex:0 0 auto;white-space:nowrap}
/* touch-action pan-y on the menu: vertical scroll stays native, but double-tap zoom and other
   ambiguous gestures are off — so a SECOND finger tapping/scrolling here registers immediately
   while the first finger holds the mic (multi-touch ordering: browse with one hand, talk with
   the other). Pairs with order.js's pointercancel handling on the mic FAB. */
.order-app .grid{flex:1;padding-bottom:88px;touch-action:pan-y}
.order-app .tile,.order-app .cat,.dish-row{touch-action:manipulation}
.order-empty{padding:40px 16px;text-align:center;color:var(--text-dim);font-size:13px}
.order-bottombar{position:fixed;left:12px;right:12px;bottom:calc(14px + env(safe-area-inset-bottom, 0px));display:flex;gap:10px;z-index:30}
/* Deliberately NOT brass, unlike .order-paybar right next to it — "View order" is informational
   (nothing fires), "Send to Kitchen"/"Make Payment" actually commits something. Sharing the same
   fill made the two read as equally weighted, easy to mis-tap one for the other one-handed. */
.order-cartbar{flex:1;display:flex;align-items:center;gap:10px;padding:14px 16px;border-radius:999px;background:var(--panel);color:var(--text);border:1px solid var(--line);font-weight:600;font-size:13.5px;box-shadow:0 10px 30px rgba(0,0,0,.2)}
.order-cartbar[hidden]{display:none}
.order-cartbar-cta{margin-left:auto;font-size:12px;text-decoration:underline;color:var(--brass)}
.order-paybar{flex:0 0 auto;padding:14px 18px;border-radius:999px;background:var(--brass);color:var(--ink);font-weight:700;font-size:13.5px;box-shadow:0 10px 30px rgba(0,0,0,.35)}
.order-paybar[hidden]{display:none}
/* Floating shortcut back to the voice-ordering avatar, for a guest who declined it at the QR
   gate (or just wants it now) — parked above the cart/pay bar so the two never collide. */
.order-avatar-fab{
  /* Mic LEFT, avatar RIGHT — owner's layout call 2026-08-30 (was mic-right/avatar-left). */
  position:fixed; left:14px; bottom:92px; z-index:29;
  width:50px; height:50px; border-radius:50%;
  background:var(--brass); color:var(--ink);
  display:flex; align-items:center; justify-content:center;
  font-size:21px; box-shadow:0 10px 24px rgba(0,0,0,.35);
  /* Press-and-hold to talk: without these, a real long-press on a phone triggers the browser's
     own text-selection/callout menu or a page scroll instead of reliably firing pointerdown/up
     on this button — confirmed needed the first time push-to-talk was tried on a real phone. */
  touch-action:none; user-select:none; -webkit-user-select:none; -webkit-touch-callout:none;
}
.order-avatar-fab:active{transform:scale(.93)}
.order-avatar-fab[hidden]{display:none}
.order-cartrow{padding:9px 0;border-bottom:1px solid var(--line)}
.order-cartrow:last-child{border-bottom:0}
.order-cartrow-top{display:flex;gap:8px;justify-content:space-between;font-size:13.5px}
.order-cartrow-sub{margin-top:3px;font-size:11.5px;color:var(--text-dim)}
.order-cartrow-ctl{display:flex;align-items:center;gap:8px;margin-top:6px}
.order-cartrow-ctl button{padding:5px 10px;border-radius:6px;background:var(--panel-2);border:1px solid var(--line);font-size:13px}
.order-cartrow-ctl span{font-family:var(--mono);min-width:16px;text-align:center}
.order-cartrow-ctl button.danger{margin-left:auto;color:var(--stop);border-color:rgba(180,64,47,.4);font-size:11px}
.order-cartrow.sent{opacity:.68}
.order-sent-tag{font-style:italic}
.order-cartrow .ln-n em{font-style:normal;color:var(--text-dim);margin-left:4px;font-size:12px}
/* width:100% is load-bearing: .order-totals sits inside flex containers with align-items:center
   (e.g. .order-state), which shrinks block children to content width instead of stretching them —
   without this, .tr's justify-content:space-between has no extra width to distribute and the
   label/amount render jammed together ("Total due$18.87"). */
.order-totals{width:100%;margin-top:10px;padding-top:10px;border-top:1px dashed var(--line);font-family:var(--mono);font-size:12.5px}
.order-choice-actions{display:flex;flex-direction:column;gap:10px;margin:18px auto 0;width:100%;max-width:280px}
.order-pay-choices{display:flex;gap:8px;margin:18px auto 0;width:100%;max-width:360px}
.order-pay-choices .btn{min-width:0}
.order-settle-lines{text-align:left;width:100%;max-width:360px;margin-top:6px}
.mono{font-family:var(--mono)}
.order-qr-preview{display:flex;justify-content:center;padding:14px;background:#fff;border-radius:10px;margin:12px 0 4px}
.order-qr-preview img{display:block}
/* .tr.grand's default border-top is var(--ink), meant for the till's light cheque tape —
   invisible on this page's dark modal, so give it the dark-theme line color instead. */
.order-totals .tr.grand{border-top-color:var(--line)}
.revenue-centre-list button:hover{border-color:var(--brass);color:var(--brass)}

/* ---------- menu item photo (Full Menu -> More… editor; see admin.js's drawMasterPriceForm) ---------- */
.mp-photo-row { display: flex; gap: 14px; align-items: flex-start; }
.mp-photo-preview {
  flex: none; width: 96px; height: 96px; border-radius: 10px; overflow: hidden;
  background: var(--panel-2); border: 1px solid var(--line);
  display: flex; align-items: center; justify-content: center;
  color: var(--text-dim); font-size: 11px; text-align: center;
}
.mp-photo-preview img { width: 100%; height: 100%; object-fit: cover; display: block; }
.mp-photo-actions { flex: 1; min-width: 0; display: flex; flex-wrap: wrap; gap: 8px; align-items: flex-start; }
.mp-photo-actions .a-hint { flex: 0 0 100%; }

/* Guest QR menu item photo — see order.js's renderGrid. Fixed square so a row of tiles keeps a
   straight baseline whatever each photo's own aspect ratio is, and so the grid's height doesn't
   jump as lazily-loaded photos arrive. */
/* A DEFINITE height, deliberately not aspect-ratio. The tile is a grid item AND a flex column,
   and with aspect-ratio the photo's height depends on the column width the grid is still busy
   resolving — a circular dependency the browser breaks by sizing the row as if the photo were
   zero-tall. The row came out at the tile's 96px minimum, the photo then laid out at its real
   height, and it spilled over the tiles below (worst on screens with many gaps, where short
   blank tiles set the row height). A fixed height removes the circularity outright: the row
   always accounts for the photo. flex:0 0 auto additionally stops the flex column squeezing it
   into a squashed strip. object-fit:cover keeps every photo filled and undistorted. */
.tile-img {
  width: 100%; height: 104px; border-radius: 8px; object-fit: cover;
  display: block; margin-bottom: 6px; background: var(--panel-2);
  flex: 0 0 auto;
}

/* ---------- the digital waiter, in-page on the QR menu (see order.html's #ovPanel) ----------
   A FULL-WIDTH TOP BANNER (owner's call 2026-08-31: "the upper banner that has the outlet name
   gets covered and the avatar shows in that area like a big screen — better than a small icon").
   While a voice session is open, the banner takes over the header zone: big face on the left,
   her words filling the middle, controls on the right — reads like a video call with the
   waiter. Opaque and pointer-events:auto (it genuinely covers the header, so taps must not
   leak through to the invisible theme/bell buttons beneath); the menu below stays fully
   tappable, and body:has() pushes the content down so nothing important hides under her. */
.ov-panel {
  position: fixed; left: 0; right: 0; top: 0; z-index: 60;
  /* Grid, not a single flex row: on a phone the 🌐 pill and ✕ were eating the bubble's width.
     Face spans both rows on the left; controls sit top-right; her words get the whole width
     beside the face. */
  display: grid; grid-template-columns: auto minmax(0, 1fr) auto; gap: 8px 12px;
  grid-template-areas: "face lang close" "face bubble bubble";
  align-items: start;
  padding: calc(env(safe-area-inset-top, 0px) + 10px) 12px 12px;
  background: var(--panel); border-bottom: 1px solid var(--line);
  box-shadow: 0 12px 30px rgba(0,0,0,.25);
  pointer-events: auto;
}
.ov-panel[hidden] { display: none; }
/* Push the page down while the banner is up, so the category nav and first menu rows aren't
   buried under her. Sized to the face clamp + padding; browsers without :has() just overlap. */
body:has(.ov-panel:not([hidden])) .order-app { padding-top: clamp(215px, calc(55vw + 45px), 325px); }
/* While she's up, the page's own intro collapses so the FIRST thing under the banner is the
   MENU (owner 2026-09-02: on Farrouj the hero photo + oversized brand block pushed Starters/
   Main Course a full screen down — "cannot browse the menu, it doesn't show fully"). The hero
   and welcome line go, the outlet name shrinks to one modest line, and the category nav lands
   right under her. Everything returns the moment the session closes. */
body:has(.ov-panel:not([hidden])) .world-hero { display: none; }
body:has(.ov-panel:not([hidden])) .world-welcome { display: none; }
body:has(.ov-panel:not([hidden])) .order-logo { display: none; }
body:has(.ov-panel:not([hidden])) .order-settle-link { display: none; }
body:has(.ov-panel:not([hidden])) .order-hd { padding-top: 8px; padding-bottom: 4px; }
body:has(.ov-panel:not([hidden])) .brand b { font-size: 18px !important; letter-spacing: .08em !important; text-indent: 0 !important; }
body:has(.ov-panel:not([hidden])) .brand span { display: none; }
body:has(.ov-panel:not([hidden])) .brand::after { display: none !important; }
/* Deliberately large: she is the feature, not a notification badge, and at 78px the face was
   too small to read as a person at arm's length — and far too small to show off lip-sync.
   Clamped so she still leaves room for the menu on a narrow phone. */
/* The face is a big RECTANGULAR video tile filling the banner's left side (owner drew the box:
   "I want the avatar to fill that box") — a video-call window, not a floating circle. */
.ov-face { grid-area: face; position: relative; width: clamp(180px, 55vw, 270px); aspect-ratio: 1; pointer-events: auto; }
.ov-photo {
  position: absolute; inset: 3px; width: calc(100% - 6px); height: calc(100% - 6px);
  border-radius: 16px; object-fit: cover; background: var(--panel-2);
}
/* The "Calling your waiter…" opening state — fills the face circle with one calm message while
   the live avatar session connects, replacing the photo→flicker→blank→video load-in. The soft
   pulse says "working on it" without a spinner. */
.ov-calling {
  position: absolute; inset: 3px; z-index: 2;
  border-radius: 16px;
  display: flex; align-items: center; justify-content: center; text-align: center;
  padding: 14px;
  background: var(--panel-2); border: 1px solid var(--line);
  color: var(--text); font-family: var(--ui); font-size: 13px; line-height: 1.35;
  animation: ovCallingPulse 1.6s ease-in-out infinite;
}
.ov-calling[hidden] { display: none; }
@keyframes ovCallingPulse { 0%, 100% { opacity: 1; } 50% { opacity: .55; } }
@media (prefers-reduced-motion: reduce) { .ov-calling { animation: none; } }
/* The state ring — the only thing telling a guest whether they're being heard right now, so it
   carries listening/thinking/speaking the same way the standalone avatar page's ring does. */
.ov-ring { position: absolute; inset: 0; border-radius: 18px; border: 2px solid transparent; transition: border-color .2s, box-shadow .2s; }
.ov-panel[data-mode="listening"] .ov-ring { border-color: var(--go); animation: ov-pulse 1.5s ease-out infinite; }
.ov-panel[data-mode="thinking"] .ov-ring { border-color: var(--line); border-top-color: var(--brass); animation: ov-spin 1.1s linear infinite; }
.ov-panel[data-mode="speaking"] .ov-ring { border-color: var(--brass); box-shadow: 0 0 22px rgba(201,162,39,.35); }
@keyframes ov-pulse { 0% { box-shadow: 0 0 0 0 rgba(47,127,91,.4); } 100% { box-shadow: 0 0 0 16px rgba(47,127,91,0); } }
@keyframes ov-spin { to { transform: rotate(360deg); } }
/* Tiny silence-countdown badge on the avatar face: seconds remaining before her reply, shown
   only once the guest pauses mid-listen (order.js hides/resets it whenever they speak again).
   Mono + tabular so the ticking digits don't wobble the badge width. */
.ov-countdown {
  position: absolute; bottom: 2px; right: 2px; z-index: 2;
  min-width: 30px; height: 30px; padding: 0 5px; border-radius: 999px;
  display: flex; align-items: center; justify-content: center;
  background: var(--go); color: #fff;
  font-family: var(--mono); font-size: 12px; font-weight: 600; font-variant-numeric: tabular-nums;
  box-shadow: 0 4px 10px rgba(0,0,0,.3);
}
.ov-countdown[hidden] { display: none; }
.ov-bubble {
  pointer-events: auto;
  grid-area: bubble; min-width: 0;
  max-height: clamp(140px, 45vw, 230px); overflow-y: auto;
  background: var(--panel-2); border: 1px solid var(--line); border-radius: 12px;
  padding: 9px 12px; font-size: 13.5px; line-height: 1.45; color: var(--text);
}
.ov-bubble[hidden] { display: none; }
.ov-bubble .ov-heard { display: block; margin-top: 5px; font-size: 11.5px; color: var(--text-dim); font-style: italic; }
.ov-close {
  pointer-events: auto; grid-area: close;
  width: 24px; height: 24px; border-radius: 50%; flex: none;
  background: var(--panel-2); border: 1px solid var(--line); color: var(--text-dim); font-size: 11px;
}
/* Mid-session language switch — shows the language you GET by tapping (same convention as the
   theme toggle). Sits next to the ✕; flips guestLang for the next listen turn (see order.js). */
.ov-lang {
  pointer-events: auto; grid-area: lang; justify-self: end;
  padding: 4px 10px; border-radius: 999px; flex: none;
  background: var(--panel-2); border: 1px solid var(--line); color: var(--text); font-size: 11.5px;
  font-family: var(--ui);
}
.ov-lang[hidden] { display: none; }
@media (prefers-reduced-motion: reduce) { .ov-ring { animation: none !important; } }

/* Mic button state (see order.js's ovMode) — the button the guest's thumb is actually on has to
   show what's happening, not just the panel beside it. Green while listening (and the glyph
   becomes a stop square, since a second tap ends the turn early), muted while thinking so a
   double-tap plainly reads as "not now", accent while she's speaking. */
.order-avatar-fab[data-mode="listening"] { background: var(--go); animation: ov-pulse 1.5s ease-out infinite; }
.order-avatar-fab[data-mode="thinking"] { opacity: .55; }
.order-avatar-fab[data-mode="speaking"] { background: var(--brass); }
/* "Your turn" — the turn-taking cue (owner wanted guests to know WHEN to tap; a soft green
   breathing glow on the button itself + the pill below beat the red-arrow idea: signal in the
   "go" color, exactly where the action is, zero tutorial clutter). Shown only while a session
   is open and she's waiting for the guest (see ovMode). */
/* !important so the per-outlet theme skins (which restyle the FAB with higher specificity)
   can't swallow the turn signal — "your turn" must read green in every world. */
.order-avatar-fab[data-hint="turn"] { background: var(--go) !important; animation: micTurnBreathe 1.8s ease-in-out infinite; }
@keyframes micTurnBreathe {
  0%, 100% { box-shadow: 0 10px 24px rgba(0,0,0,.35), 0 0 0 0 rgba(47,127,91,.55); }
  50% { box-shadow: 0 10px 24px rgba(0,0,0,.35), 0 0 0 12px rgba(47,127,91,0); }
}
.mic-hint {
  position: fixed; left: 14px; bottom: 148px; z-index: 29;
  padding: 6px 12px; border-radius: 999px;
  background: var(--panel); border: 1px solid var(--line); color: var(--text);
  font-family: var(--ui); font-size: 12.5px; white-space: nowrap;
  box-shadow: 0 6px 18px rgba(0,0,0,.25);
  pointer-events: none;
}
.mic-hint[data-kind="turn"] { border-color: var(--go); color: var(--go); font-weight: 600; }
.mic-hint[hidden] { display: none; }
@media (prefers-reduced-motion: reduce) { .order-avatar-fab[data-hint="turn"] { animation: none; } }

/* The live Simli stream, sitting exactly where the still photo does so the swap between them is
   invisible — same circle, same size, no reflow when she starts or stops speaking. */
.ov-video {
  position: absolute; inset: 3px; width: calc(100% - 6px); height: calc(100% - 6px);
  border-radius: 16px; object-fit: cover; background: var(--panel-2);
}
.ov-video[hidden] { display: none; }
/* (banner layout: the bubble fills the middle of the top bar — no max-width cap needed) */

/* Unsaved-changes marker in the screen editor header — screen edits are a draft until Save. */
.screen-dirty{font-size:11px;letter-spacing:.08em;text-transform:uppercase;color:var(--stop);border:1px solid var(--stop);border-radius:999px;padding:2px 8px;margin-left:8px;vertical-align:middle}

/* ---------- Screen Layout editor (Admin) ----------
   A working copy of the till grid: same auto-fill columns and tile proportions as .grid/.tile in
   the POS, so what's arranged here is what the kitchen-facing screen actually shows. Every cell
   is draggable; a "gap" is a real entry that holds its place and renders as nothing on the till. */
.layout-grid{display:grid;gap:10px;grid-template-columns:repeat(5,1fr);padding:12px;background:var(--shell);border:1px solid var(--line);border-radius:var(--r);max-height:62vh;overflow-y:auto}
.layout-cell{position:relative;background:var(--panel);border:1px solid var(--line);border-radius:var(--r);padding:12px;min-height:96px;display:flex;flex-direction:column;justify-content:space-between;cursor:grab}
.layout-cell:active{cursor:grabbing}
.layout-cell:hover{border-color:var(--brass)}
.layout-cell .nm{font-size:13px;line-height:1.25}
.layout-cell .pr{font-family:var(--mono);font-size:12px;color:var(--brass)}
.layout-num{position:absolute;top:6px;left:8px;font-family:var(--mono);font-size:10px;color:var(--text-dim)}
.layout-cell.tile-addon{border-style:dashed}
/* An empty cell — a drop target, nothing more. No content, no controls, nothing to drag out. */
.layout-empty{background:transparent;border-style:dashed;border-color:var(--line);cursor:default}
.layout-empty:hover{border-color:var(--brass);background:rgba(201,162,39,.06)}
.layout-x{position:absolute;top:4px;right:4px;width:22px;height:22px;border-radius:6px;background:var(--panel-2);border:1px solid var(--line);color:var(--text-dim);font-size:11px;line-height:1;display:flex;align-items:center;justify-content:center;opacity:0;transition:opacity .12s}
.layout-cell:hover .layout-x{opacity:1}
.layout-x:hover{border-color:var(--stop);color:var(--stop)}
/* The read-only preview shows gaps too, so it matches what Layout arranged. */
.snap-blank{background:repeating-linear-gradient(45deg,transparent,transparent 5px,rgba(255,255,255,.05) 5px,rgba(255,255,255,.05) 10px);border-style:dashed}
/* The till's own empty cell — holds its place, shows nothing, ignores taps. */
.tile-blank{background:transparent;border:1px dashed transparent;pointer-events:none}

/* The Layout editor runs at till proportions so what's arranged is what staff see. */
.modal-wide{max-width:990px}

/* Photos in the layout editor, so arranging a screen looks like the screen being arranged. */
.layout-cell.has-photo{padding-top:8px}
.layout-img{width:100%;height:56px;object-fit:cover;border-radius:6px;display:block;margin-bottom:6px;background:var(--panel-2);flex:0 0 auto}
