/* ==========================================================================
   JelPOS — main.css
   Core design system + component styles. Consumes ONLY the variables defined
   in themes.css. No hardcoded colors here (the one exception: pure white/black
   inside shadows/scrims via theme vars).

   Conventions:
   - Mobile-first. POS targets 7"/10" tablets + 15" touch monitors + laptop.
   - Touch targets >= 48x48px (WCAG 2.2 AA, spec POS note).
   - Focus rings always visible (never `outline:none` without a replacement).
   - RTL handled via logical properties + [dir="rtl"] overrides (Hebrew).
   - kebab-case class names .
   ========================================================================== */

/* -------------------------------- Fonts ---------------------------------- */
/* FiraGO (SIL OFL 1.1) — the app's UI face, self-hosted (CSP 'self', offline-
   capable, precached in sw.js). The one libre superfamily with first-class
   Georgian + Latin, so the Georgian UI renders in-brand (matching the landing)
   instead of falling back to an uninstalled system face (□□□ tofu). Subset
   weights: 400/500/600/700/900 — a request for 800 selects 900 by the CSS
   font-matching algorithm. Path is relative to css/. See assets/fonts/FiraGO-OFL.txt. */
@font-face { font-family:"FiraGO"; font-style:normal; font-weight:400; font-display:swap;
  src:url("../assets/fonts/firago-400.woff2") format("woff2"); }
@font-face { font-family:"FiraGO"; font-style:normal; font-weight:500; font-display:swap;
  src:url("../assets/fonts/firago-500.woff2") format("woff2"); }
@font-face { font-family:"FiraGO"; font-style:normal; font-weight:600; font-display:swap;
  src:url("../assets/fonts/firago-600.woff2") format("woff2"); }
@font-face { font-family:"FiraGO"; font-style:normal; font-weight:700; font-display:swap;
  src:url("../assets/fonts/firago-700.woff2") format("woff2"); }
@font-face { font-family:"FiraGO"; font-style:normal; font-weight:900; font-display:swap;
  src:url("../assets/fonts/firago-900.woff2") format("woff2"); }

/* ----------------------------- Reset / base ------------------------------ */
*, *::before, *::after { box-sizing: border-box; }
* { margin: 0; }
html { -webkit-text-size-adjust: 100%; text-size-adjust: 100%; }

body {
  font-family: "FiraGO", system-ui, -apple-system, "Segoe UI", "Noto Sans Georgian",
               "BPG Nino Mtavruli", "Noto Sans Hebrew", Roboto, Arial, sans-serif;
  background: var(--bg);
  color: var(--text);
  line-height: 1.5;
  font-size: 16px;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  min-height: 100dvh;
  overscroll-behavior: none;
}

img, svg { display: block; max-width: 100%; }
button, input, select, textarea { font: inherit; color: inherit; }
[hidden] { display: none !important; }

/* Numeric tabular alignment for money/quantities */
.tnum, .money, .num-pad button { font-variant-numeric: tabular-nums; }

/* Respect reduced motion */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
    scroll-behavior: auto !important;
  }
}

/* ------------------------------- Typography ------------------------------ */
h1, h2, h3, h4 { line-height: 1.2; font-weight: 700; letter-spacing: -0.01em; }
h1 { font-size: clamp(1.5rem, 1.2rem + 1.4vw, 2rem); }
h2 { font-size: clamp(1.25rem, 1.1rem + 0.8vw, 1.6rem); }
h3 { font-size: 1.15rem; }
small, .text-sm { font-size: 0.85rem; }
.text-xs { font-size: 0.75rem; }
.muted { color: var(--text-muted); }
.faint { color: var(--text-faint); }
.strong { font-weight: 700; }
.truncate { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* --------------------------- Layout primitives --------------------------- */
.app { min-height: 100dvh; display: flex; flex-direction: column; }
.container { width: 100%; max-width: 1280px; margin-inline: auto; padding-inline: clamp(16px, 4vw, 32px); }
.row { display: flex; gap: 12px; align-items: center; }
.row.wrap { flex-wrap: wrap; }
.col { display: flex; flex-direction: column; gap: 12px; }
.spread { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.center { display: grid; place-items: center; }
.grow { flex: 1 1 auto; min-width: 0; }
.gap-sm { gap: 8px; } .gap-lg { gap: 20px; }
.stack > * + * { margin-top: var(--stack-gap, 16px); }
.hide-mobile { display: none; }
@media (min-width: 768px) { .hide-mobile { display: revert; } .only-mobile { display: none; } }
/* A later component rule (.badge is inline-flex) outranks the bare utility
   above by source order — the dashboard's e-mail badge stayed on phones and
   pushed the header to 465px (UI test 4 D7). Below the cut, hidden wins. */
@media (max-width: 767.98px) { .hide-mobile { display: none !important; } }

/* -------------------------------- Buttons -------------------------------- */
.btn {
  --btn-bg: var(--surface-2);
  --btn-fg: var(--text);
  appearance: none;
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  min-height: 48px; padding: 0 18px;
  border: 1px solid transparent;
  border-radius: var(--radius);
  background: var(--btn-bg); color: var(--btn-fg);
  font-weight: 600; font-size: 0.95rem;
  cursor: pointer; user-select: none;
  /* I3 (UI test 6): an <a class="btn"> — every page's ← Back — drew the link
     underline (and underlined the &nbsp; gap as a stray dash). */
  text-decoration: none;
  transition: transform .08s ease, background-color .15s ease, box-shadow .15s ease, border-color .15s ease;
  -webkit-tap-highlight-color: transparent;
}
.btn:hover { background: var(--surface-3); }
.btn:active { transform: translateY(1px) scale(0.99); }   /* tactile push */
.btn:focus-visible { outline: none; box-shadow: var(--ring); }
.btn:disabled, .btn[aria-disabled="true"] { opacity: .5; cursor: not-allowed; transform: none; }
/* A locked FIELD must look locked. Buttons dimmed; inputs and selects did not,
   so the Central-Menu-managed fields a branch may not edit (name, price, photo
   and — since 2026-09-23, owner item 3 — the whole options editor) looked live
   and silently swallowed what was typed into them. */
.input:disabled, .select:disabled, textarea:disabled,
input:disabled, select:disabled { opacity: .6; cursor: not-allowed; }

.btn-primary { --btn-bg: var(--primary); --btn-fg: var(--text-on-primary); border-color: transparent; }
.btn-primary:hover { background: var(--primary-hover); }
.btn-accent  { --btn-bg: var(--accent); --btn-fg: var(--text-on-accent); }
.btn-accent:hover { background: color-mix(in srgb, var(--accent) 85%, var(--text)); }
.btn-ghost   { --btn-bg: transparent; --btn-fg: var(--text); border-color: var(--border); }
.btn-ghost:hover { background: var(--surface-2); }
.btn-danger  { --btn-bg: var(--danger); --btn-fg: #fff; }
.btn-danger:hover { background: color-mix(in srgb, var(--danger) 85%, var(--text)); }
.btn-subtle  { --btn-bg: var(--primary-soft); --btn-fg: var(--primary); }
.btn-subtle:hover { background: color-mix(in srgb, var(--primary-soft) 80%, var(--primary)); }

/* Dark Wine: filled action + primary/accent buttons wear the wine-red (#bc2f40)
   with cream ink. Danger stays red, ghost stays an outline, subtle/success keep
   their meaning — hence the :not() exclusions. --primary is left amber on purpose
   (it doubles as accent/link text that must read on the near-black). */
:root[data-theme="dark-wine"] .btn:not(.btn-danger):not(.btn-ghost):not(.btn-subtle):not(.btn-success):not(.btn-link) {
  --btn-bg: #bc2f40; --btn-fg: #f4ebde; border-color: transparent;
}
:root[data-theme="dark-wine"] .btn:not(.btn-danger):not(.btn-ghost):not(.btn-subtle):not(.btn-success):not(.btn-link):hover {
  background: #cf3446;
}

.btn-lg { min-height: 56px; font-size: 1.05rem; padding: 0 24px; }
.btn-xl { min-height: 64px; font-size: 1.2rem; padding: 0 28px; }
.btn-block { width: 100%; }
.btn-icon { padding: 0; width: 48px; min-width: 48px; }

/* -------------------------------- Inputs --------------------------------- */
.field { display: flex; flex-direction: column; gap: 6px; }
.field > label { font-size: 0.85rem; font-weight: 600; color: var(--text-muted); }
.input, .select, textarea.input {
  width: 100%; min-height: 48px; padding: 10px 14px;
  background: var(--surface); color: var(--text);
  border: 1px solid var(--border); border-radius: var(--radius);
  transition: border-color .15s ease, box-shadow .15s ease;
}
.input::placeholder { color: var(--text-faint); }   /* placeholder is NOT a label */
.input:focus-visible, .select:focus-visible, textarea.input:focus-visible {
  outline: none; border-color: var(--primary); box-shadow: var(--ring);
}
.input.is-invalid { border-color: var(--danger); }
.field-error { font-size: 0.8rem; color: var(--danger); min-height: 1em; }
.field-hint  { font-size: 0.8rem; color: var(--text-faint); }
/* I4 (UI test 6): the 42px gap was reserved for a magnifier nobody drew — the
   placeholder floated in empty space on 9 pages. The icon is drawn here. */
.input-search {
  padding-inline-start: 42px;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%238a93a3' stroke-width='2' stroke-linecap='round'%3E%3Ccircle cx='11' cy='11' r='7'/%3E%3Cpath d='M20 20l-4-4'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-size: 18px 18px; background-position: left 14px center;
}
[dir="rtl"] .input-search { background-position: right 14px center; }

/* --------------------------------- Cards --------------------------------- */
.card {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius-lg); box-shadow: var(--shadow-sm);
}
.card-pad { padding: clamp(16px, 2vw, 24px); }
.panel { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-lg); }

/* ----------------------------- Badges / chips ---------------------------- */
.badge {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 3px 10px; border-radius: var(--radius-pill);
  font-size: 0.75rem; font-weight: 700; line-height: 1.4;
  background: var(--surface-2); color: var(--text-muted);
}
.badge-success { background: var(--success-soft); color: var(--success); }
.badge-warning { background: var(--warning-soft); color: var(--warning); }
.badge-danger  { background: var(--danger-soft);  color: var(--danger); }
.badge-info    { background: var(--info-soft);    color: var(--info); }
/* color-blind safe: badges pair color with a dot/glyph supplied in markup */
.badge .dot { width: 7px; height: 7px; border-radius: 50%; background: currentColor; }

.chip {
  display: inline-flex; align-items: center; gap: 8px;
  min-height: 40px; padding: 0 16px; border-radius: var(--radius-pill);
  background: var(--surface-2); color: var(--text); border: 1px solid transparent;
  font-weight: 600; cursor: pointer; white-space: nowrap;
  transition: background-color .15s ease, color .15s ease;
}
.chip:hover { background: var(--surface-3); }
.chip[aria-pressed="true"], .chip.is-active {
  background: var(--primary); color: var(--text-on-primary);
}
.chip:focus-visible { outline: none; box-shadow: var(--ring); }
/* KDS K3 (UI test 12): a link chip (the KDS view switch) is not underlined. */
a.chip { text-decoration: none; }
/* Sub-category chips (POS parent -> child drill-down row): visually subordinate
   to root chips — smaller, lighter, outlined rather than filled — so the second
   row reads as a child set, not a duplicate strip. The .is-active fill still
   wins (higher-specificity .chip.is-active rule above sets --primary bg/fg). */
.chip-sub {
  min-height: 32px; padding: 0 12px; font-size: .85rem; font-weight: 500;
  background: transparent; color: var(--text-muted); border-color: var(--border);
}
.chip-sub:hover { background: var(--surface-2); }
.chip-row { display: flex; gap: 8px; overflow-x: auto; padding-bottom: 4px;
  scrollbar-width: thin; -webkit-overflow-scrolling: touch; }
.chip-row::-webkit-scrollbar { height: 6px; }
.chip-row::-webkit-scrollbar-thumb { background: var(--border-strong); border-radius: 3px; }
/* Per-vertical inline line-icon (signup business-type picker) — inherits chip
   text color via currentColor, so it flips on .is-active automatically. */
.vert-ic { display: inline-flex; align-items: center; flex: 0 0 auto; }
.vert-ic svg { width: 18px; height: 18px; display: block; }
/* Chrome icons (toolbars/top bars/dialog actions) — js/ui-icons.js */
.tb-ic { display: inline-flex; align-items: center; flex: 0 0 auto; vertical-align: -3px; }
.tb-ic svg { width: 19px; height: 19px; display: block; }
.btn-icon .tb-ic svg { width: 22px; height: 22px; }
.btn-icon > svg { width: 22px; height: 22px; } /* JS-built icon buttons: el({html: JP.uiIcon()}) */
/* Manager dashboard module-card icon — slightly larger than chip icons to
   match the card title weight (replaces the old 1.5rem emoji). */
.mod-ic svg { width: 22px; height: 22px; }

/* Module-card header: localized titles run much longer than English
   (e.g. ka "რეცეპტები და ინგრედიენტები", ru "Поставщики и закупки"), so let the
   title wrap inside the card and keep the status badge intact at the inline-end
   instead of letting either overflow the 260px card. */
.mod-card-head { align-items: flex-start; }
.mod-card-head .mod-card-title { min-width: 0; flex: 1 1 auto; }
.mod-card-head .mod-card-title strong { overflow-wrap: anywhere; }
.mod-card-head .badge { flex: 0 0 auto; white-space: nowrap; }

/* Owner email in the top bar can be long — truncate it rather than push the
   bell/POS/sign-out controls (or wrap and break the fixed-height bar). */
#whoami { display: inline-block; max-width: 180px; overflow: hidden;
  text-overflow: ellipsis; white-space: nowrap; vertical-align: bottom; }

/* --------------------------- Modal / dialog ------------------------------ */
.scrim {
  position: fixed; inset: 0; background: var(--overlay);
  display: grid; place-items: center; padding: 16px; z-index: 100;
  animation: fade-in .15s ease;
}
.modal {
  width: 100%; max-width: 460px; max-height: 90dvh; overflow: auto;
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius-xl); box-shadow: var(--shadow-lg);
  animation: pop-in .18s cubic-bezier(.16,1,.3,1);
}
.modal-wide { max-width: 720px; }
.modal-head { display: flex; align-items: center; justify-content: space-between;
  gap: 12px; padding: 18px 20px; border-bottom: 1px solid var(--border); }
.modal-body { padding: 20px; }
.modal-foot { display: flex; gap: 12px; padding: 16px 20px; border-top: 1px solid var(--border);
  justify-content: flex-end; }
@keyframes fade-in { from { opacity: 0; } }
@keyframes pop-in { from { opacity: 0; transform: translateY(8px) scale(.98); } }

/* ------------------------------- Toasts ---------------------------------- */
.toast-stack {
  position: fixed; inset-block-end: 16px; inset-inline-end: 16px;
  display: flex; flex-direction: column; gap: 10px; z-index: 200; max-width: 360px;
}
/* APK items 2+3 (2026-10-03): on a phone the stack was half off-screen — 360px
   plus its 16px margin is wider than the screen, and it sat under the gesture /
   navigation bar. It spans the screen with a margin each side and clears the
   bottom safe area (the app pads natively; a notched browser uses env()). */
.toast-stack { inset-block-end: calc(16px + env(safe-area-inset-bottom, 0px)); }
@media (max-width: 480px) {
  .toast-stack { inset-inline: 12px; max-width: none; }
  .toast { overflow-wrap: anywhere; }
}
.toast {
  display: flex; align-items: center; gap: 10px;
  padding: 12px 16px; border-radius: var(--radius);
  background: var(--surface); border: 1px solid var(--border);
  box-shadow: var(--shadow-md); color: var(--text);
  animation: slide-in .2s cubic-bezier(.16,1,.3,1);
}
.toast-success { border-inline-start: 4px solid var(--success); }
.toast-error   { border-inline-start: 4px solid var(--danger); }
.toast-info    { border-inline-start: 4px solid var(--info); }
@keyframes slide-in { from { opacity: 0; transform: translateY(8px); } }

/* --------------------- State patterns: loading / empty ------------------- */
.empty-state {
  display: grid; place-items: center; text-align: center; gap: 10px;
  padding: 48px 24px; color: var(--text-muted);
}
.empty-state .empty-icon { font-size: 2.4rem; opacity: .8; }
.empty-state h3 { color: var(--text); }
/* In-table variant: empty message spanning all columns */
td.td-empty { text-align: center; padding: 24px; color: var(--text-muted); }

/* ----------------------------- Top app bar ------------------------------- */
.topbar {
  display: flex; align-items: center; gap: 12px;
  height: 64px; padding-inline: clamp(12px, 3vw, 20px);
  background: var(--surface); border-bottom: 1px solid var(--border);
  position: sticky; top: 0; z-index: 50;
}
/* APK item 2: under a status bar / notch (viewport-fit=cover) the bar grows by
   the top safe area instead of sliding beneath it. */
.topbar { height: calc(64px + env(safe-area-inset-top, 0px)); padding-top: env(safe-area-inset-top, 0px); }
.brand { display: flex; align-items: center; gap: 10px; font-weight: 800; letter-spacing: -0.02em; }
.brand .brand-mark {
  width: 34px; height: 34px; border-radius: 9px; display: grid; place-items: center;
  background: var(--primary); color: var(--text-on-primary); font-weight: 900; font-size: 1rem;
}

/* ====================================================================== */
/*  POS / Cashier screen                                                   */
/* ====================================================================== */
.pos {
  display: grid; grid-template-columns: 1fr; gap: 0;
  height: 100dvh; overflow: hidden;
}
@media (min-width: 900px) {
  .pos { grid-template-columns: 1fr minmax(340px, 400px); }
}
/* C12 (UI test 5): the connectivity banner sits ABOVE the till. With the till
   at a fixed 100dvh the banner (offline / sales queued) pushed it 36px down and
   Charge fell below the fold exactly when the till was offline. The page is one
   100dvh column; the till takes what the banner leaves. */
.pos-page { display: flex; flex-direction: column; height: 100dvh; overflow: hidden; }
/* APK item 2: the till clears the status bar and the home indicator too. */
.pos-page { box-sizing: border-box; padding-top: env(safe-area-inset-top, 0px); padding-bottom: env(safe-area-inset-bottom, 0px); }
.pos-page > .pos { flex: 1 1 auto; min-height: 0; height: auto; }
/* C19 (UI test 5): the sync chip is fixed at the bottom-left corner — the till's
   last row of tiles (and, below 900px, Charge itself) scroll clear of it. */
.pos-page .product-grid { padding-bottom: 72px; }
/* C18 (UI test 5): below 900px the till is one column — products over cart,
   an equal share each — and the cart's content (totals, promo, quick actions,
   Charge) is taller than its share: Charge sat at y=1056 on a 768×1024 iPad,
   clipped by .pos { overflow: hidden }. The cart pane scrolls as one piece
   instead, lines at their natural height. */
@media (max-width: 899.98px) {
  .pos { grid-template-rows: minmax(0, 3fr) minmax(0, 2fr); }
  .pos-page .cart-actions { padding-bottom: 72px; }
  /* .cart / .cart-items phone rules live AFTER the base cart rules below. */
}

/* min-width:0 lets the single-column mobile grid track (`.pos` = 1fr) shrink to
   the viewport. Without it the grid item's default min-width:auto pins the track
   to its ~652px min-content (the non-wrapping toolbar), overflowing a 375px phone
   and getting clipped by body{overflow-x:hidden} — cart/toolbar silently cut off. */
.pos-main { display: flex; flex-direction: column; min-width: 0; min-height: 0; background: var(--bg); }
/* flex-wrap:wrap reflows the 9 toolbar controls to extra rows on a narrow phone
   instead of forcing a min-content wider than the screen. */
.pos-toolbar { display: flex; flex-wrap: wrap; gap: 10px; align-items: center; padding: 12px clamp(12px,2vw,18px);
  border-bottom: 1px solid var(--border); background: var(--surface); }
.pos-cats { padding: 10px clamp(12px,2vw,18px) 4px; }

.product-grid {
  flex: 1 1 auto; min-height: 0; overflow-y: auto;
  display: grid; gap: 12px; padding: 12px clamp(12px,2vw,18px) 24px;
  grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
  align-content: start;
}
.product-card {
  position: relative;
  display: flex; flex-direction: column; text-align: start;
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius); overflow: hidden; cursor: pointer;
  min-height: 120px; transition: transform .08s ease, box-shadow .15s ease, border-color .15s ease;
}
/* ★ favorite toggle overlay (top-inline-end corner of each product tile) */
.pc-fav {
  position: absolute; inset-block-start: 4px; inset-inline-end: 4px; z-index: 1;
  width: 26px; height: 26px; display: grid; place-items: center;
  border-radius: 999px; cursor: pointer; line-height: 1;
  font-size: 1rem; color: var(--text-faint);
  background: color-mix(in srgb, var(--surface) 78%, transparent);
  transition: color .12s ease, background .12s ease, transform .08s ease;
}
.pc-fav:hover { color: var(--accent); background: var(--surface); }
.pc-fav:active { transform: scale(0.9); }
.pc-fav:focus-visible { outline: none; box-shadow: var(--ring); }
.pc-fav.is-fav { color: var(--warning); }
.product-card:hover { box-shadow: var(--shadow-md); border-color: var(--border-strong); }
.product-card:active { transform: scale(0.98); }
.product-card:focus-visible { outline: none; box-shadow: var(--ring); }
.product-card .pc-thumb {
  aspect-ratio: 16/10; background: var(--surface-2); display: grid; place-items: center;
  font-size: 1.8rem; color: var(--text-faint); overflow: hidden;
}
/* Product photo thumbnail (a downscaled dataURL on the product's `image`
   field) fills the tile; the emoji-icon fallback keeps the same box. */
.product-card .pc-thumb .pc-thumb-img { width: 100%; height: 100%; object-fit: cover; display: block; }

/* Inventory/menu product editor: photo picker + preview. */
.inv-photo-row { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.inv-photo-thumb { width: 72px; height: 72px; object-fit: cover; border-radius: var(--radius);
  border: 1px solid var(--border); background: var(--surface-2); }
/* Master-menu list row: tiny leading thumbnail beside the product name. */
.menu-row-thumb { width: 32px; height: 32px; object-fit: cover; border-radius: 6px;
  border: 1px solid var(--border); background: var(--surface-2); flex: 0 0 auto; }
.product-card .pc-body { padding: 8px 10px; display: flex; flex-direction: column; gap: 2px; }
.product-card .pc-name { font-weight: 600; font-size: 0.9rem; line-height: 1.25;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
.product-card .pc-price { font-weight: 700; font-variant-numeric: tabular-nums; }
.product-card.is-out { opacity: .55; cursor: not-allowed; }
.product-card.is-out:active { transform: none; }
.pc-stock { font-size: 0.72rem; }

/* Cart panel */
.cart {
  display: flex; flex-direction: column; min-height: 0;
  background: var(--surface); border-inline-start: 1px solid var(--border);
}
.cart-head { padding: 14px 16px; border-bottom: 1px solid var(--border); }
.cart-items { flex: 1 1 auto; min-height: 0; overflow-y: auto; padding: 8px 8px; }
.cart-line {
  display: grid; grid-template-columns: 1fr auto; gap: 4px 10px;
  padding: 10px 8px; border-radius: var(--radius); align-items: center;
}
.cart-line:hover { background: var(--surface-2); }
/* C5 (UI test 5): stepper · discount · remove as ONE row under the name —
   they were three extra cells of the two-column line and ✕ wrapped. */
.cl-ctrl { grid-column: 1 / -1; display: flex; align-items: center; gap: 6px; }
.cl-ctrl .qty-stepper { margin-inline-end: auto; }
.charge-hint { margin: 0; text-align: center; color: var(--warning); font-weight: 600; }
.cart-line .cl-name { font-weight: 600; }
.cart-line .cl-meta { font-size: 0.78rem; color: var(--text-muted); }
.cart-line .cl-total { font-weight: 700; font-variant-numeric: tabular-nums; }
.qty-stepper { display: inline-flex; align-items: center; gap: 0; border: 1px solid var(--border);
  border-radius: var(--radius-pill); overflow: hidden; }
.qty-stepper button { width: 36px; height: 36px; border: 0; background: var(--surface-2);
  cursor: pointer; font-size: 1.1rem; font-weight: 700; }
.qty-stepper button:hover { background: var(--surface-3); }
.qty-stepper .qv { min-width: 34px; text-align: center; font-weight: 700; font-variant-numeric: tabular-nums; }

.cart-totals { padding: 12px 16px; border-top: 1px solid var(--border); }
.cart-totals .tline { display: flex; justify-content: space-between; padding: 3px 0; color: var(--text-muted); }
.cart-totals .tline.grand { color: var(--text); font-size: 1.3rem; font-weight: 800;
  padding-top: 8px; margin-top: 4px; border-top: 1px dashed var(--border); }
.cart-actions { padding: 12px 16px 16px; display: grid; gap: 10px; }
/* Quick actions (discount / hold / resume / refund) — 4-up, compact */
.cart-quick-row { display: grid; grid-template-columns: repeat(4, 1fr); gap: 8px; }
.cart-quick-row .btn { flex-direction: column; gap: 2px; padding: 8px 4px; font-size: 0.8rem; }
.cart-quick-row .badge { position: absolute; top: 2px; inset-inline-end: 4px; }
/* APK item 4 (field test 2026-10-03): on a 360px phone the total / promo /
   quick-action / Charge panel was 13px wider than the screen — a bare 1fr
   track never shrinks below its Georgian label, and the promo input kept its
   intrinsic width — and since the cart scrolls (overflow-y: auto) it panned
   sideways under the cashier's thumb. Every track may shrink now, labels wrap,
   the pane never scrolls sideways, and below 480px the six tiles are two rows
   of three, tighter, so Charge is closer to the cart. */
.cart-actions { grid-template-columns: minmax(0, 1fr); }
.cart-actions > .row > .input { min-width: 0; width: 0; }
.cart-quick-row { grid-template-columns: repeat(4, minmax(0, 1fr)); }
.cart-quick-row .btn { min-width: 0; white-space: normal; overflow-wrap: anywhere; text-align: center; line-height: 1.15; }
.cart { overflow-x: hidden; }
@media (max-width: 480px) {
  .cart { touch-action: pan-y; overscroll-behavior: contain; }
  .cart-head { padding: 10px 14px; }
  .cart-totals { padding: 8px 14px; }
  .cart-totals .tline.grand { font-size: 1.15rem; }
  .cart-actions { padding: 10px 12px 12px; gap: 8px; }
  .cart-quick-row { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 6px; }
  .cart-quick-row .btn { min-height: 44px; padding: 6px 4px; font-size: 0.74rem; }
  .cart-actions > .row > .btn { padding-inline: 12px; }
}
/* NEXT_TASKS 2026-10-04 #2 (Android field test): C18 put these in the 900px
   block ABOVE the base cart rules, so the same-specificity base rules won —
   .cart-items stayed flex 1 1 auto / min-height 0 / overflow-y auto, and in the
   one-column till it shrank to ~16px: a cart line sat cut in half under the
   totals. Declared after the base rules, the cart pane scrolls as one piece and
   every line keeps its natural height. */
@media (max-width: 899.98px) {
  .cart { overflow-y: auto; border-inline-start: 0; border-top: 1px solid var(--border); }
  .cart-items { flex: 0 0 auto; min-height: auto; overflow: visible; }
}

/* Numpad (cash tender, PIN) */
.num-pad { display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px; }
.num-pad button {
  min-height: 60px; font-size: 1.4rem; font-weight: 700;
  border: 1px solid var(--border); border-radius: var(--radius);
  background: var(--surface-2); color: var(--text); cursor: pointer;
}
.num-pad button:hover { background: var(--surface-3); }
.num-pad button:active { transform: scale(0.97); }
.num-pad button:focus-visible { outline: none; box-shadow: var(--ring); }

/* ====================================================================== */
/*  Auth / portal                                                          */
/* ====================================================================== */
.auth-wrap { min-height: 100dvh; display: grid; place-items: center; padding: 24px;
  /* the column is the screen: an auto track grew to the widest content and pushed the card past a phone */
  grid-template-columns: minmax(0, 1fr); }
/* signup business types: each group label over its own wrapping chips, not one sideways strip */
.chip-row.signup-types { flex-direction: column; align-items: stretch; overflow-x: visible; gap: 6px; }
.auth-card { width: 100%; max-width: 420px; }
.pin-display { display: flex; justify-content: center; gap: 14px; margin: 8px 0 18px; }
.pin-dot { width: 16px; height: 16px; border-radius: 50%; border: 2px solid var(--border-strong);
  transition: background-color .12s ease, border-color .12s ease; }
.pin-dot.filled { background: var(--primary); border-color: var(--primary); }
.pin-error { animation: shake .3s; }
@keyframes shake { 0%,100%{transform:translateX(0)} 25%{transform:translateX(-6px)} 75%{transform:translateX(6px)} }

.seg { display: inline-flex; background: var(--surface-2); border-radius: var(--radius-pill); padding: 4px; }
.seg button { border: 0; background: transparent; color: var(--text-muted); cursor: pointer;
  min-height: 40px; padding: 0 18px; border-radius: var(--radius-pill); font-weight: 600; }
.seg button.is-active { background: var(--surface); color: var(--text); box-shadow: var(--shadow-sm); }

/* Offline / connection banner */
.conn-banner { display: flex; align-items: center; gap: 10px; justify-content: center;
  padding: 8px 16px; font-size: 0.85rem; font-weight: 600;
  background: var(--warning-soft); color: var(--warning); }
.conn-banner.is-online { background: var(--success-soft); color: var(--success); }

/* Theme picker swatches */
.theme-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(120px,1fr)); gap: 10px; }
.theme-swatch { cursor: pointer; border: 2px solid var(--border); border-radius: var(--radius);
  padding: 10px; display: flex; flex-direction: column; gap: 8px; background: var(--surface); }
.theme-swatch.is-active { border-color: var(--primary); box-shadow: var(--ring); }
.theme-swatch .sw-dots { display: flex; gap: 6px; }
.theme-swatch .sw-dots i { width: 18px; height: 18px; border-radius: 50%; display: block; }
.theme-swatch .sw-name { font-size: 0.82rem; font-weight: 600; }

/* ====================================================================== */
/*  Inventory module                                                       */
/* ====================================================================== */
.inv-toolbar { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; margin-bottom: 16px; }
.inv-row {
  display: grid; align-items: center; gap: 12px;
  /* Definite min-widths per column so the long Georgian header (თვითღირებულება)
     and the action buttons each fit on ONE line; every track is minmax(min,fr)
     — no content-sized `auto` — so the header lines up with the body exactly and
     the spare width is spread across the flexible columns. */
  /* Stock-cell fix (2026-10-01): the four Georgian action buttons are ~393px
     but the track was 340px — right-aligned and nowrap, they spilled LEFT over
     the Stock cell ("მარაგის კორექცია" on top of the badge), and the 90px Stock
     track squeezed "3 ც. · 1 ვადა იწურება" into a round blob. Actions get 400px
     (a fixed min, not max-content — the header's empty last cell would size to
     0 and the headers would stop lining up), Stock 140px; Category/Cost give
     the width back. */
  grid-template-columns: minmax(0,2.4fr) minmax(100px,1.2fr) minmax(80px,.7fr) minmax(150px,.9fr) minmax(140px,.9fr) minmax(400px,1.4fr);
  padding: 12px 14px; border-bottom: 1px solid var(--border);
}
.inv-row:hover { background: var(--surface-2); }
/* A branch whose books could not be read (H5): listed, dimmed, never a figure. */
tr.is-unreadable { opacity: .6; }
.inv-row.is-archived { opacity: .5; }
.inv-col { font-variant-numeric: tabular-nums; min-width: 0; }
.inv-col.low { font-weight: 700; }
/* Numbers (Price/Cost) right-align — headers carry .money too so label + value
   hug the same edge. Actions sit at the row end on one line; buttons are slightly
   compact so the Georgian labels fit without wrapping. */
.inv-row .money { text-align: end; }
/* Wrap, never spill: a longer language wraps the buttons inside their own
   track instead of painting over the Stock column. */
.inv-row > :last-child { justify-content: flex-end; }
.inv-row:not(.menu-row) > :last-child { flex-wrap: wrap; row-gap: 6px; }
/* A wrapped stock badge is a rounded box, not a pill-shaped blob; the Stock
   column steps off the right-aligned Cost column ("თვითღირებულება მარაგი"). */
.inv-col .badge { border-radius: var(--radius-sm, 8px); max-width: 100%; }
.inv-row:not(.menu-row) > :nth-child(5) { padding-inline-start: 12px; }
.inv-row .row.gap-sm .btn { min-height: 40px; padding: 0 12px; font-size: .85rem; white-space: nowrap; }
/* The name cell (icon + name/badges/sku) must SHRINK inside its grid track —
   without min-width:0 down the flex chain its one-line minimum content paints
   OVER the Category column when the track runs out of width; let the
   name+badges row wrap to a second line instead. */
.inv-row > .row.gap-sm.grow > div { min-width: 0; }
.inv-row > .row.gap-sm.grow .row.gap-sm { flex-wrap: wrap; row-gap: 4px; }
/* The Central Menu list (menu.js) is a FIVE-cell row — name · category · price ·
   branches · actions — on the same .inv-row chrome. Without its own template the
   actions landed in inventory's fifth (90px) track and stacked one button per
   line (live drive 2026-09-22, three buttons since the row Archive). */
/* M4 (UI test 7): the three Georgian buttons are 408px — in a 300px track they
   spilled leftwards over the Branches column ("1 ფილი[რედაქტირება]"). The
   actions track is sized to its buttons; the flexible columns share the rest.
   Every row is its own grid, so a content-sized track would differ row to row
   (the header's empty cell, an archived row's one Restore) — the rows are a
   SUBGRID of .menu-panel and share its tracks. The per-row template stays as
   the fallback where subgrid is unsupported. */
.inv-row.menu-row { grid-template-columns: minmax(0,2.4fr) minmax(120px,1.2fr) minmax(80px,.7fr) minmax(120px,.9fr) max-content; }
.menu-panel { display: grid; column-gap: 12px; grid-template-columns: minmax(0,2.4fr) minmax(120px,1.2fr) minmax(80px,.7fr) minmax(120px,.9fr) max-content; }
.menu-panel > .inv-row.menu-row { grid-column: 1 / -1; grid-template-columns: subgrid; }
/* M3/M6 (UI test 7): the Central Menu head. It never wrapped, so five tools
   squeezed the title onto two lines, put each emoji above its words, and on a
   375px phone pinned the layout at 903px. The tools drop below the title as a
   group and wrap whole buttons. */
.menu-head { flex-wrap: wrap; align-items: flex-start; row-gap: 12px; margin-bottom: 16px; }
.menu-head > :first-child { min-width: 0; }
.menu-tools { flex-wrap: wrap; }
.menu-tools .btn { white-space: nowrap; }
.menu-tools .tb-ic { display: inline-flex; width: 18px; height: 18px; }
.menu-tools .tb-ic svg { width: 18px; height: 18px; }
/* M18: the desk tools fold behind a toggle on a phone (inventory I16's shape). */
.menu-desk { display: contents; }
.menu-tools-toggle { display: none; }
@media (max-width: 640px) {
  .menu-tools-toggle { display: inline-flex; }
  .menu-tools:not(.tools-open) .menu-desk { display: none; }
}
/* M16: the branch checkboxes were the browser's 13px on a touch till. */
.menu-locs label { min-height: 36px; align-items: center; }
.menu-locs input[type="checkbox"] { width: 20px; height: 20px; accent-color: var(--primary); flex: 0 0 auto; }
/* M13: a category line — name + counts stacked, its two buttons on one line. */
.menu-cat-line > :first-child { min-width: 0; }
.menu-cat-line .row .btn { white-space: nowrap; }
@media (min-width: 901px) and (max-width: 1250px) {
  /* The desktop fixed minimums (120+80+180+90+340 + gaps ≈ 900px) starve the
     flexible name column to zero on mid-size viewports; relax them so every
     column keeps readable width (action buttons may wrap to a second line). */
  /* I5 (UI test 6): the Cost track was 120px and its Georgian header
     (თვითღირებულება) is 150px — it ran into "მარაგი" as one word. */
  .inv-row { grid-template-columns: minmax(0,2.4fr) minmax(90px,1.2fr) minmax(70px,.7fr) minmax(150px,.9fr) minmax(110px,.8fr) minmax(210px,1.1fr); }
  /* M5 (UI test 7): the menu's three buttons stacked one per line in a 220px
     track (every row three buttons tall at 1024px) — they keep one line. */
  .inv-row.menu-row { grid-template-columns: minmax(0,2.4fr) minmax(80px,1.2fr) minmax(70px,.7fr) minmax(80px,.9fr) max-content; }
  .menu-panel { grid-template-columns: minmax(0,2.4fr) minmax(80px,1.2fr) minmax(70px,.7fr) minmax(80px,.9fr) max-content; }
  .inv-row > :last-child { flex-wrap: wrap; row-gap: 6px; }
  .inv-row.menu-row > :last-child { flex-wrap: nowrap; }
}
/* I6 (UI test 6): a bare 1fr track is minmax(auto,1fr) — it grew to the options
   row's one-line width (384px) inside a 301px phone dialog, so every field ran
   45px off the right edge. minmax(0,…) keeps the tracks inside the dialog. */
.inv-form { display: grid; gap: 14px; grid-template-columns: minmax(0,1fr) minmax(0,1fr); }
.inv-form-full { grid-column: 1 / -1; }
@media (max-width: 900px) {
  /* Below 900px the full-width Georgian action buttons no longer fit beside the
     data columns, so collapse to Name | Price | Stock and move Actions to their
     own full-width row (Category/Cost hidden so nothing runs off the edge). */
  .inv-row { grid-template-columns: minmax(0,1fr) auto auto; }
  /* An auto Stock track grew to the expiry badge's one-line width (185px of a
     375px phone) and crushed the product name to 62px — cap it; the badge wraps. */
  .inv-row:not(.menu-row) { grid-template-columns: minmax(0,1fr) auto minmax(auto,140px); }
  /* M3 (UI test 7): the desktop `.inv-row.menu-row` template (0,2,0) outranked
     the rule above, so a phone kept five tracks — name and price overlapped. */
  .inv-row.menu-row { grid-template-columns: minmax(0,1fr) auto auto; }
  .menu-panel { grid-template-columns: minmax(0,1fr) auto auto; }
  .inv-row > .hide-mobile { display: none; }
  .inv-row > :last-child { grid-column: 1 / -1; justify-content: flex-start; flex-wrap: wrap; }
  /* I17 (UI test 6): the header's empty actions cell still took a full row. */
  .inv-row > :last-child:empty { display: none; }
}
/* I16 (UI test 6): the desk tools (export / import / labels / bulk) fold
   behind one toggle on a phone — seven stacked buttons pushed the list 628px
   down an 812px screen. Bulk stays in view while products are selected. */
.inv-tools { display: contents; }
.inv-tools-toggle { display: none; }
@media (max-width: 640px) {
  .inv-form { grid-template-columns: minmax(0,1fr); }
  .inv-tools-toggle { display: inline-flex; }
  .inv-toolbar:not(.tools-open) .inv-tools > .btn:not(#bulkEditBtn),
  .inv-toolbar:not(.tools-open) .inv-tools > #bulkEditBtn:disabled { display: none; }
  .inv-toolbar > .input-search { flex: 1 1 100%; max-width: none !important; }
  /* I6: an option row (name · ±₾ · ☆ · stock · ✕) left its NAME 30px wide. */
  .opt-row { flex-wrap: wrap; }
  .opt-row > .grow { flex: 1 1 100%; }
}

/* ---- Inventory stocktake  ------------------------- */
.stk-hub-row {
  display: flex; align-items: center; gap: 10px; width: 100%; text-align: start;
  border: 1px solid var(--border); border-radius: var(--radius); background: var(--surface);
  padding: 10px 12px; cursor: pointer; font: inherit; color: inherit;
}
.stk-hub-row:hover { background: var(--surface-2); border-color: var(--primary); }

.stk-screen {
  position: fixed; inset: 0; z-index: 60; background: var(--bg, var(--surface));
  display: flex; flex-direction: column; overflow: hidden;
}
.stk-head { padding: 14px 18px; border-bottom: 1px solid var(--border); background: var(--surface); display: grid; gap: 10px; }
.stk-head-top { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.stk-toolbar { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.stk-scan-wrap { flex: 1 1 240px; max-width: 360px; }
.stk-list-head {
  display: grid; align-items: center; gap: 12px;
  grid-template-columns: minmax(0,2fr) 0.9fr 0.9fr 0.7fr;
  padding: 8px 18px; font-size: .78rem; font-weight: 700; color: var(--text-muted);
  background: var(--surface-2); border-bottom: 1px solid var(--border);
}
.stk-rows { flex: 1; overflow-y: auto; padding-bottom: 40px; }
.stk-row {
  display: grid; align-items: center; gap: 12px;
  grid-template-columns: minmax(0,2fr) 0.9fr 0.9fr 0.7fr;
  padding: 10px 18px; border-bottom: 1px solid var(--border);
}
.stk-row.is-counted { background: color-mix(in srgb, var(--success, #2f9e44) 7%, transparent); }
.stk-row.stk-flash { animation: stk-flash 0.6s ease; }
@keyframes stk-flash { 0% { background: color-mix(in srgb, var(--primary) 35%, transparent); } 100% {} }
.stk-name { min-width: 0; }
.stk-exp { font-variant-numeric: tabular-nums; color: var(--text-muted); }
.stk-count { width: 100%; max-width: 110px; text-align: center; font-variant-numeric: tabular-nums; }
.stk-var { font-weight: 700; font-variant-numeric: tabular-nums; }
.stk-var.is-ok { color: var(--text-muted); }
.stk-var.is-off { color: var(--danger); }
.stk-vcol { text-align: end; }
.stk-metrics { display: grid; grid-template-columns: repeat(4, 1fr); gap: 10px; }
.stk-metric { background: var(--surface-2); border-radius: var(--radius); padding: 12px; text-align: center; }
.stk-metric-val { font-size: 1.35rem; font-weight: 800; font-variant-numeric: tabular-nums; }
.stk-metric-label { font-size: .72rem; color: var(--text-muted); margin-top: 2px; }
.stk-diff-row { display: flex; align-items: center; gap: 10px; padding: 6px 0; border-bottom: 1px dashed var(--border); }
@media (max-width: 640px) {
  .stk-list-head { grid-template-columns: 1fr auto auto; }
  .stk-list-head .stk-exp { display: none; }
  .stk-row { grid-template-columns: 1fr auto auto; }
  .stk-row .stk-exp { display: none; }
  .stk-metrics { grid-template-columns: repeat(2, 1fr); }
}

/* ---- Product options: variant/modifier authoring (inventory form) ------ */
.opt-editor { border: 1px solid var(--border); border-radius: var(--radius);
  padding: 14px; background: var(--surface-2, var(--surface)); display: grid; gap: 10px; }
.opt-sec-head { display: flex; justify-content: space-between; align-items: center; gap: 10px; }
.opt-list { display: grid; gap: 8px; }
.opt-row { display: flex; align-items: center; gap: 8px; }
.opt-row .opt-num, .opt-adj .input { width: 84px; }
.opt-adj { display: flex; align-items: center; gap: 4px; white-space: nowrap; }
.opt-pm { color: var(--text-muted); font-size: .85rem; }
.opt-stk .input { width: 92px; }
/* M10 (UI test 7): the default option's ★ drew like every other ☆. */
.opt-row .btn-icon.is-active { color: var(--warning); }
.opt-group { border: 1px solid var(--border); border-radius: 8px; padding: 10px; display: grid; gap: 8px;
  background: var(--surface); }
.opt-group-head { display: flex; align-items: center; gap: 8px; }
.opt-group-meta { display: flex; align-items: center; gap: 16px; flex-wrap: wrap; }

/* ---- Product options: picker dialog (POS + self-order) ----------------- */
.opt-block { display: grid; gap: 8px; }
.opt-label { font-weight: 600; display: flex; justify-content: space-between; align-items: baseline; gap: 8px; }
.opt-hint { font-weight: 400; font-size: .8rem; color: var(--text-muted); }
.opt-choices { display: flex; flex-wrap: wrap; gap: 8px; }
.opt-chip { padding: 8px 14px; border: 1.5px solid var(--border); border-radius: 999px;
  background: var(--surface); color: var(--text); cursor: pointer; font-size: .92rem;
  transition: border-color .12s, background .12s; }
.opt-chip:hover { border-color: var(--primary); }
.opt-chip.is-sel { border-color: var(--primary); background: var(--primary-soft, var(--primary));
  color: var(--primary); font-weight: 600; }
/* Phase 3 follow-up: a size chip's shelf count; an empty one is marked. */
.opt-chip-n { margin-inline-start: 8px; font-size: .8rem; font-weight: 600; color: var(--text-muted);
  font-variant-numeric: tabular-nums; }
.opt-chip.is-sel .opt-chip-n { color: inherit; }
.opt-chip.is-out { border-style: dashed; color: var(--text-muted); }
.opt-price { display: flex; justify-content: space-between; align-items: center;
  padding-top: 8px; border-top: 1px dashed var(--border); font-size: 1.05rem; }
.opt-price .money { font-size: 1.2rem; }

/* ---- Option labels shown on cart / basket lines ------------------------ */
.cart-line .cl-opts, .so-line-opts { font-size: .76rem; color: var(--text-muted); line-height: 1.3; }
.so-line-opts { display: block; }

/* ----------------------------- Customers / CRM --------------------------- */
/* R4 (UI test 14): the Georgian Edit button + the archive icon did not fit a
   1.2fr actions track, so every row stacked them to 129px — at 1280 too. The
   head and every row are a SUBGRID of .cust-panel (central menu M4 /
   ingredients N7): each column is sized ONCE for all rows and the actions
   track is content-sized. The per-row template is the fallback where subgrid
   is unsupported. ≤900px a row is a card — see the media query. */
.cust-panel { display: grid; column-gap: 12px;
  grid-template-columns: minmax(0,1fr) max-content max-content max-content max-content max-content; }
.cust-panel > .cust-row-head, .cust-panel > .cust-rows { grid-column: 1 / -1; }
.cust-rows { display: grid; grid-template-columns: subgrid; }
.cust-rows > * { grid-column: 1 / -1; }
.cust-row {
  display: grid; align-items: center; column-gap: 12px; row-gap: 6px;
  grid-template-columns: minmax(0,1fr) max-content max-content max-content max-content max-content;
  padding: 12px 14px; border-bottom: 1px solid var(--border);
}
.cust-panel > .cust-row-head, .cust-rows > .cust-row { grid-template-columns: subgrid; }
.cust-row > * { min-width: 0; }
.cust-name { flex-wrap: nowrap; }
.cust-name .strong { overflow-wrap: anywhere; }
.cust-actions { display: flex; gap: 8px; align-items: center; justify-content: flex-end; flex-wrap: nowrap; }
.cust-row:hover { background: var(--surface-2); cursor: pointer; }
.cust-row:focus-visible { outline: none; box-shadow: var(--ring); }
.cust-row-head { font-weight: 700; color: var(--text-muted); background: var(--surface-2); cursor: default; }
.cust-row-head:hover { background: var(--surface-2); cursor: default; }
.cust-row-head > span { white-space: nowrap; }
.cust-row.is-archived { opacity: .5; }
.cust-row .num { font-variant-numeric: tabular-nums; white-space: nowrap; }
/* The visits count's own word is for a screen reader on a wide screen (the
   head names the column) and visible on a card, which has no head. */
.cust-k { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }
.cust-rows > .empty-state { border-bottom: 0; }
/* R21 the page clears the fixed sync chip · R8 the failed-read line ·
   the count badge wraps under a long Georgian title on a phone. */
.cust-page { padding-block-end: 88px; }
.cust-status { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; margin: 0 0 12px; color: var(--danger); }
.cust-status:empty { display: none; }
.cust-head { flex-wrap: wrap; gap: 8px; }
.cust-head .badge { white-space: nowrap; }
.tag-archived { color: var(--danger); border-color: currentColor; }
.bday-row { inline-size: 100%; text-align: start; font: inherit; color: inherit; background: transparent; }
.bday-row:hover { background: var(--surface-2); }
.cust-avatar {
  inline-size: 40px; block-size: 40px; border-radius: 50%; flex: none;
  display: grid; place-items: center; font-weight: 800; color: #fff;
  background: var(--primary); font-size: .95rem;
}
.tag { display: inline-block; padding: 1px 8px; border-radius: var(--radius-pill);
  font-size: .68rem; font-weight: 700; background: var(--surface-2);
  color: var(--text-muted); border: 1px solid var(--border); }
.tier-badge { display: inline-flex; align-items: center; gap: 5px; font-weight: 700; }
.metric-grid { display: grid; gap: 12px; grid-template-columns: repeat(auto-fit,minmax(120px,1fr)); }
.metric { background: var(--surface-2); border-radius: var(--radius); padding: 10px 12px; }
.metric .mv { font-size: 1.25rem; font-weight: 800; font-variant-numeric: tabular-nums; }
.metric .ml { font-size: .72rem; color: var(--text-muted); }
.hist-item { padding: 10px 0; border-bottom: 1px solid var(--border); }
.hist-item:last-child { border-bottom: 0; }

/* Segments + duplicate merge  */
.chip-x { border: 0; background: transparent; color: inherit; cursor: pointer;
  font-weight: 800; padding: 0 2px; margin-inline-start: 6px; }
.dup-row { display: flex; align-items: center; gap: 10px; padding: 8px 10px;
  border: 1px solid var(--border); border-radius: var(--radius); cursor: pointer; }
.dup-row.is-primary { border-color: var(--accent); background: var(--accent-soft, var(--surface-2)); }
.crm-form { display: grid; gap: 14px; grid-template-columns: 1fr 1fr; }
/* R3 (UI test 14): ≤767px the six-track row kept only three visible cells —
   at 375 the Edit button lay over the tier badge, at 700 three cells shared
   half the width. A row is a card now: name + tags beside tier over spend,
   then phone · visits, then the actions. The column head has nothing to head.
   The five content-sized tracks need ~665px in Georgian, which leaves the
   name nothing on a 768–900 tablet, so tablets get the card too. */
@media (max-width: 900px) {
  .cust-row-head { display: none; }
  .cust-panel, .cust-rows { display: block; }
  .cust-rows > .cust-row { grid-template-columns: minmax(0,1fr) auto;
    grid-template-areas: "name tier" "name spend" "phone visits" "act act"; }
  .cust-row > .cust-name { grid-area: name; align-self: start; }
  .cust-row > .cust-tier { grid-area: tier; justify-self: end; }
  .cust-row > .cust-spend { grid-area: spend; justify-self: end; }
  .cust-row > .cust-phone { grid-area: phone; color: var(--text-muted); }
  .cust-row > .cust-visits { grid-area: visits; justify-self: end; color: var(--text-muted); }
  .cust-row > .cust-actions { grid-area: act; justify-content: flex-start; flex-wrap: wrap; }
  .cust-k { position: static; width: auto; height: auto; margin: 0; overflow: visible; clip: auto; }
}
@media (max-width: 640px) {
  .crm-form { grid-template-columns: 1fr; }
  /* R20: the tier / tag filters share one row, each wide enough for its words; search goes last. */
  .cust-page .inv-toolbar > .select { flex: 1 1 calc(50% - 5px); min-width: 0; max-width: none !important; }
  .cust-page .inv-toolbar > .input-search { order: 1; }
}

/* ---- Gift cards ------------------------------------------------------- */
/* G21 (UI test 15): the fixed sync chip no longer covers the last row. */
.gc-page { padding-block-end: 88px; }
/* G6 (UI test 15): a failed read says so, with Retry. */
.gc-status-line { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; margin: 0 0 12px; color: var(--danger); }
.gc-status-line:empty { display: none; }
.gc-row {
  display: grid; align-items: center; gap: 12px;
  grid-template-columns: minmax(0,1.6fr) minmax(0,1.2fr) minmax(0,.8fr) minmax(0,.8fr) minmax(0,1.2fr);
  padding: 12px 14px; border-bottom: 1px solid var(--border);
}
.gc-row > * { min-width: 0; }
.gc-row > :last-child { justify-content: flex-end; flex-wrap: wrap; }
.gc-row:hover { background: var(--surface-2); cursor: pointer; }
.gc-row:focus-visible { outline: none; box-shadow: var(--ring); }
.gc-row.is-dim { opacity: .55; }
.gc-row .num { text-align: end; font-variant-numeric: tabular-nums; white-space: nowrap; }
/* G24 (UI test 15): the head row is not a card — no hover, no pointer. */
.gc-row-head { font-weight: 700; color: var(--text-muted); background: var(--surface-2); }
.gc-row-head:hover { background: var(--surface-2); cursor: default; }
.gc-row-head > span { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* G3: a masked code is one token — it never breaks after "GC-". */
.gc-code { font-family: ui-monospace, "SFMono-Regular", Menlo, Consolas, monospace;
  font-weight: 700; letter-spacing: .5px; white-space: nowrap; }
.gc-status { display: inline-block; padding: 2px 9px; border-radius: var(--radius-pill);
  font-size: .68rem; font-weight: 800; text-transform: uppercase; letter-spacing: .3px; white-space: nowrap; }
/* G25 (UI test 15): the raw status colour on its own 18% tint was 2.9–3.9:1 in
   the light themes (sync chip af0c96a) — the words mix toward the text colour. */
.gc-status.s-active   { background: color-mix(in srgb, var(--success) 18%, transparent); color: color-mix(in srgb, var(--success) 60%, var(--text)); }
.gc-status.s-depleted { background: var(--surface-2); color: var(--text-muted); }
.gc-status.s-expired  { background: color-mix(in srgb, var(--warning) 20%, transparent); color: color-mix(in srgb, var(--warning) 60%, var(--text)); }
.gc-status.s-void     { background: color-mix(in srgb, var(--danger) 18%, transparent); color: color-mix(in srgb, var(--danger) 60%, var(--text)); }
.gc-hist { list-style: none; margin: 0; padding: 0; }
.gift-voucher {
  border: 2px dashed var(--primary); border-radius: var(--radius-lg);
  padding: 22px; text-align: center; position: relative; overflow: hidden;
  background: linear-gradient(135deg, color-mix(in srgb, var(--primary) 12%, var(--surface)), var(--surface));
}
.gift-voucher .gv-code {
  font-family: ui-monospace, "SFMono-Regular", Menlo, Consolas, monospace;
  font-size: 1.5rem; font-weight: 800; letter-spacing: 2px; margin: 8px 0; user-select: all;
  overflow-wrap: anywhere;
}
.gift-voucher .gv-value { font-size: 2rem; font-weight: 800; }
/* G3 (UI test 15): ≤640 the row hid the holder AND the balance — a gift-card
   list with no balances — and broke the code after "GC-". A row is a card:
   code + status, holder + balance. The row itself opens the card, so the
   arrow goes. */
@media (max-width: 640px) {
  .gc-row-head { display: none; }
  .gc-row { grid-template-columns: minmax(0,1fr) auto; row-gap: 4px;
    grid-template-areas: "code status" "who bal"; }
  .gc-row > .gc-cell-code { grid-area: code; }
  .gc-row > .gc-cell-status { grid-area: status; justify-self: end; }
  .gc-row > .gc-cell-who { grid-area: who; color: var(--text-muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .gc-row > .gc-cell-bal { grid-area: bal; justify-self: end; font-weight: 700; }
  .gc-row > .gc-cell-act { display: none; }
  /* G28: search and the status filter share one row (164 → 106px). */
  .gc-page .inv-toolbar > .input-search { flex: 1 1 150px; min-width: 0; max-width: none !important; }
  .gc-page .inv-toolbar > .select { flex: 0 1 auto; min-width: 0; }
}

/* ====================================================================== */
/*  KDS — Kitchen Display System                                           */
/* ====================================================================== */
.kds-body { min-height: 100dvh; background: var(--bg); display: flex; flex-direction: column; }
.kds-bar { display: flex; align-items: center; justify-content: space-between; gap: 12px;
  padding: 10px clamp(12px,2vw,20px); background: var(--surface); border-bottom: 1px solid var(--border);
  position: sticky; top: 0; z-index: 5; flex-wrap: wrap; }
.kds-bar-l { display: flex; align-items: center; gap: 14px; min-width: 0; flex-wrap: wrap; }
.kds-bar-r { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.kds-logo { font-size: 1.1rem; letter-spacing: -.02em; white-space: nowrap; }
.kds-metric { display: flex; flex-direction: column; align-items: center; line-height: 1.1; min-width: 48px; }
.kds-metric-v { font-size: 1.3rem; font-weight: 800; font-variant-numeric: tabular-nums; }
.kds-metric-l { font-size: .66rem; text-transform: uppercase; color: var(--text-muted); letter-spacing: .04em; }

.kds-board { flex: 1 1 auto; display: grid; gap: 12px; padding: 14px clamp(12px,2vw,20px);
  grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); align-content: start; }
.kds-empty { grid-column: 1/-1; display: grid; place-items: center; gap: 8px; padding: 60px 0; color: var(--text-muted); }
/* the refused-stream notice: Retry first, a new code second (offline parity, item 4) */
.kds-disconnected-actions { display: flex; flex-wrap: wrap; gap: 10px; justify-content: center; margin-top: 6px; }

/* FINA-parity F4: KDS view switch + Expo (pass) + public Queue monitor */
.kds-views .chip.is-active { background: var(--primary); color: #fff; border-color: var(--primary); }
.kds-queue #recallBtn, .kds-queue #soundBtn, .kds-queue #printBtn, .kds-queue #stationTabs,
.kds-expo #stationTabs { display: none; }

/* Public customer queue monitor — order numbers only, no PII */
.qm-wrap { grid-column: 1/-1; display: grid; grid-template-columns: 1fr 1fr; gap: clamp(12px,2vw,24px); }
.qm-col { background: var(--surface); border: 2px solid var(--border); border-radius: var(--radius-lg);
  padding: clamp(12px,2vw,20px); min-height: 44vh; }
.qm-col.is-ready { border-color: var(--success); }
.qm-title { display: flex; align-items: center; justify-content: space-between; margin: 0 0 14px;
  font-size: clamp(1.1rem,2.4vw,1.9rem); text-transform: uppercase; letter-spacing: .04em; }
.qm-col.is-ready .qm-title { color: var(--success); }
.qm-count { font-variant-numeric: tabular-nums; opacity: .7; }
.qm-nums { display: grid; grid-template-columns: repeat(auto-fill, minmax(96px, 1fr)); gap: 10px; }
.qm-num { display: grid; place-items: center; font-weight: 800; font-variant-numeric: tabular-nums;
  font-size: clamp(1.5rem,3.4vw,2.8rem); padding: .35em .2em; border-radius: var(--radius);
  background: var(--surface-2); color: var(--text); }
.qm-col.is-ready .qm-num { border: 2px solid var(--success); color: var(--success);
  animation: kdsPulse 1.6s ease-in-out infinite; }
.qm-empty { grid-column: 1/-1; text-align: center; color: var(--text-muted); padding: 26px 0; font-size: 1.4rem; }
@media (max-width: 640px) { .qm-wrap { grid-template-columns: 1fr; } }

/* Expo (pass) view — ready-to-serve first, then cooking */
.expo-sec { grid-column: 1/-1; }
.expo-h { display: flex; align-items: center; gap: 10px; margin: 6px 2px 10px;
  font-size: 1.05rem; text-transform: uppercase; letter-spacing: .04em; color: var(--text-muted); }
.expo-sec.is-ready .expo-h { color: var(--success); }
.expo-count { font-variant-numeric: tabular-nums; background: var(--surface-2); border-radius: 999px;
  padding: .05em .6em; font-size: .9em; }
.expo-grid { display: grid; gap: 12px; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); align-content: start; }
.expo-empty { padding: 20px 0; }

.kds-card { display: flex; flex-direction: column; background: var(--surface);
  border: 2px solid var(--border); border-inline-start-width: 6px; border-radius: var(--radius-lg);
  overflow: hidden; box-shadow: var(--shadow-sm); }
.kds-card.is-green  { border-inline-start-color: var(--success); }
.kds-card.is-yellow { border-inline-start-color: var(--warning); }
.kds-card.is-red    { border-inline-start-color: var(--danger); animation: kdsPulse 1.6s ease-in-out infinite; }
.kds-card.is-prog   { border-inline-start-color: var(--text-muted); opacity: .92; }
.kds-card.is-ready  { border-inline-start-color: var(--success); background: var(--success-soft); }
.kds-card.is-asap   { outline: 2px dashed var(--accent); outline-offset: -2px; }
@keyframes kdsPulse { 0%,100%{ box-shadow: var(--shadow-sm); } 50%{ box-shadow: 0 0 0 3px var(--danger-soft); } }

.kds-card-head { display: flex; align-items: center; justify-content: space-between; gap: 8px;
  padding: 8px 12px; border-bottom: 1px solid var(--border); background: var(--surface-2); }
.kds-ord { display: flex; align-items: baseline; gap: 8px; min-width: 0; }
.kds-ord strong { font-size: 1.15rem; }
.kds-type { font-size: .78rem; color: var(--text-muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.kds-timer { font-variant-numeric: tabular-nums; font-weight: 700; white-space: nowrap; }

.kds-items { list-style: none; margin: 0; padding: 6px; display: flex; flex-direction: column; gap: 2px; flex: 1 1 auto; }
.kds-item { padding: 7px 8px; border-radius: var(--radius); cursor: pointer; }
.kds-item:hover { background: var(--surface-2); }
.kds-item:focus-visible { outline: none; box-shadow: var(--ring); }
.kds-item.prog .kds-name { color: var(--text-muted); }
.kds-item.done { opacity: .55; }
.kds-item.done .kds-name { text-decoration: line-through; }
.kds-item-main { display: flex; align-items: center; gap: 7px; }
.kds-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--border-strong); flex: none; }
.kds-item.prog .kds-dot { background: var(--warning); }
.kds-item.done .kds-dot { background: var(--success); }
.kds-qty { font-weight: 800; font-variant-numeric: tabular-nums; min-width: 26px; }
.kds-name { font-weight: 600; }
.kds-stn { margin-inline-start: auto; font-size: .95rem; }
.kds-mod { font-size: .8rem; color: var(--info); padding-inline-start: 22px; }
.kds-note { font-size: .8rem; color: var(--text-muted); padding-inline-start: 22px; font-style: italic; }
.kds-allergy { font-size: .76rem; font-weight: 800; color: var(--danger); background: var(--danger-soft);
  display: inline-block; padding: 1px 8px; border-radius: var(--radius-pill); margin: 3px 0 0 22px; }
.kds-card-foot { display: flex; gap: 8px; padding: 8px; border-top: 1px solid var(--border); }

.kds-reprint { min-height: 30px; padding: 2px 6px; opacity: .65; }
.kds-reprint:hover { opacity: 1; }

/* Coursing — fired course headers + held (not yet fired) groups */
.kds-card.is-held { border-inline-start-color: var(--border-strong); opacity: .9; }
.kds-course-head { display: flex; align-items: center; justify-content: space-between; gap: 8px;
  padding: 5px 12px; font-size: .76rem; font-weight: 800; letter-spacing: .04em;
  text-transform: uppercase; color: var(--text-muted); background: var(--surface-2);
  border-block: 1px solid var(--border); }
.kds-course-head.is-held { color: var(--warning); background: var(--warning-soft); }
.kds-fire { padding: 3px 12px; font-size: .8rem; min-height: 30px; }
.kds-items.is-held { opacity: .55; }
.kds-item.is-held { cursor: default; }
.kds-item.is-held:hover { background: transparent; }
/* UI test 12 (KDS). K15: the fixed sync chip no longer covers the last card.
   K14: the failed-read line. K13: the PUBLIC queue monitor shows no way to the
   full board and no staff metrics (its columns carry their own counts). */
.kds-board { padding-block-end: 88px; }
.kds-board:focus { outline: none; }
.kds-status { margin: 10px clamp(12px,2vw,20px) 0; display: flex; flex-wrap: wrap; align-items: center; gap: 10px;
  color: var(--danger); font-weight: 600; }
.kds-status:empty { display: none; }
.kds-queue .kds-views, .kds-queue .kds-metric { display: none; }
@media (max-width: 640px) {
  /* K3: on a phone the bar wraps to four rows — let it scroll away */
  .kds-bar { position: static; }
  .kds-bar-l { width: 100%; }
}

/* ====================================================================== */
/*  Tables — floor plan + order screen                                     */
/* ====================================================================== */
.tbl-toolbar { display: flex; gap: 12px; align-items: center; flex-wrap: wrap; margin-bottom: 12px; }
.tbl-search { max-width: 200px; }
/* T21 (UI test 13): the floor's last row sat under the fixed sync chip. */
.tbl-page { padding-block-end: 88px; }
.tbl-status { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin: 0 0 12px; }
.tbl-status[hidden] { display: none; }
@media (max-width: 480px) {
  /* T3 (UI test 13): Takeaway · KDS · Back did not fit a 375px topbar — Back
     hung half off-screen and the page scrolled sideways (409px). Takeaway
     folds to its icon; the hidden label still names the link. */
  .topbar .tb-fold > [data-i18n] { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
    overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }
}
/* v133 multi-floor: floor tabs (row above the zone chips) + zone row. */
.tbl-floorbar { margin: 2px 0 8px; }
.tbl-floorbar .chip-floor { font-weight: 700; }
.tbl-zonebar { margin-bottom: 12px; }
.tbl-legend { display: flex; gap: 16px; flex-wrap: wrap; margin-bottom: 14px; }
.tbl-leg { display: inline-flex; align-items: center; gap: 6px; }
.tbl-dot { width: 12px; height: 12px; border-radius: 4px; display: inline-block; }
.tbl-dot.s-free { background: var(--success); }
.tbl-dot.s-occupied { background: var(--danger); }
.tbl-dot.s-bill-requested { background: var(--warning); }
.tbl-dot.s-reserved { background: var(--info); }
.tbl-dot.s-cleaning { background: var(--text-muted); }

/* Redesign 2026-08-08 (v132 polish): filled, status-tinted summary chips with a
   big colored count — click to filter. Matches the approved floor mockup. */
.tbl-summary { display: flex; flex-wrap: wrap; gap: 10px; margin-bottom: 16px; }
.tbl-sum { flex: 1 1 128px; display: flex; align-items: baseline; gap: 8px; padding: 12px 16px;
  border: 1px solid var(--border); border-radius: var(--radius); background: var(--surface);
  cursor: pointer; font: inherit; text-align: start;
  transition: transform .1s ease, box-shadow .12s ease, border-color .12s ease; }
.tbl-sum:hover { transform: translateY(-1px); }
.tbl-sum.is-active { box-shadow: var(--ring); }
.tbl-sum-n { font-size: 1.5rem; font-weight: 800; font-variant-numeric: tabular-nums; line-height: 1; }
.tbl-sum-l { color: var(--text-muted); font-size: .82rem; font-weight: 600; }
.tbl-sum.s-free { background: color-mix(in srgb, var(--success) 12%, var(--surface)); border-color: color-mix(in srgb, var(--success) 32%, transparent); }
.tbl-sum.s-free .tbl-sum-n { color: var(--success); }
.tbl-sum.s-occupied { background: color-mix(in srgb, var(--danger) 12%, var(--surface)); border-color: color-mix(in srgb, var(--danger) 32%, transparent); }
.tbl-sum.s-occupied .tbl-sum-n { color: var(--danger); }
.tbl-sum.s-bill-requested { background: color-mix(in srgb, var(--warning) 14%, var(--surface)); border-color: color-mix(in srgb, var(--warning) 34%, transparent); }
.tbl-sum.s-bill-requested .tbl-sum-n { color: var(--warning); }
.tbl-sum.s-reserved { background: color-mix(in srgb, var(--info) 12%, var(--surface)); border-color: color-mix(in srgb, var(--info) 32%, transparent); }
.tbl-sum.s-reserved .tbl-sum-n { color: var(--info); }
.tbl-sum.s-cleaning { background: color-mix(in srgb, var(--text-muted) 12%, var(--surface)); border-color: color-mix(in srgb, var(--text-muted) 30%, transparent); }
.tbl-sum.s-cleaning .tbl-sum-n { color: var(--text-muted); }
@media (max-width: 640px) {
  /* T4 (UI test 13): four rows of tools, the legend and three rows of status
     chips put the first table below the fold of an 812px phone. The tools
     fold (inventory I16), the legend — a copy of the chips — steps aside,
     and the chips sit two rows. */
  .tbl-legend { display: none; }
  .tbl-summary { gap: 8px; }
  .tbl-sum { flex: 1 1 30%; min-width: 0; flex-direction: column; align-items: flex-start; padding: 8px 10px; gap: 2px; }
  .tbl-sum-n { font-size: 1.2rem; }
  .tbl-sum-l { font-size: .72rem; overflow-wrap: anywhere; }
}

/* Redesign 2026-08-08: the floor is a scroll/pan viewport around an absolutely-
   positioned canvas. On phones the canvas keeps its min size so the same spatial
   layout pans instead of reflowing (owner: "canvas everywhere"). */
.floor { position: relative; overflow: auto; border-radius: var(--radius-lg);
  background: var(--surface-2, var(--surface)); padding: 6px;
  min-height: 60vh; -webkit-overflow-scrolling: touch; }
.tbl-canvas { position: relative; width: 100%; min-width: 720px; min-height: 820px; height: 82vh; }
.tbl-canvas .tbl-tile { position: absolute; margin: 0; }
.tbl-tile.is-dimmed { opacity: .28; filter: grayscale(.4); pointer-events: none; }
/* Redesign 2026-08-08 (v132 polish): tiles are dark cards with a thick colored
   TOP accent + a corner status dot per status (matches the approved mockup).
   Shape data no longer forces circles/ellipses — every tile is a rounded card. */
.tbl-tile { position: relative; text-align: start; cursor: pointer; min-height: 96px;
  display: flex; flex-direction: column; gap: 3px; padding: 11px 13px;
  border: 1px solid var(--border); border-top: 3px solid var(--border-strong);
  border-radius: 14px; background: var(--surface); box-shadow: var(--shadow-sm);
  transition: transform .08s ease, box-shadow .15s ease; overflow: hidden; }
.tbl-tile:hover { box-shadow: var(--shadow-md); transform: translateY(-1px); }
.tbl-tile:active { transform: scale(.99); }
.tbl-tile:focus-visible { outline: none; box-shadow: var(--ring); }
.tbl-tile.sh-circle, .tbl-tile.sh-rectangle, .tbl-tile.sh-square { border-radius: 14px; }
/* corner status dot (hidden in edit mode where the edit/drag badges sit) */
.tbl-tile::after { content: ""; position: absolute; inset-block-start: 11px; inset-inline-end: 11px;
  width: 9px; height: 9px; border-radius: 3px; background: var(--border-strong); }
.tbl-tile.s-free { border-top-color: var(--success); }
.tbl-tile.s-free::after { background: var(--success); }
.tbl-tile.s-free .tbl-status { display: none; }
.tbl-tile.s-occupied { border-top-color: var(--danger); background: color-mix(in srgb, var(--danger) 7%, var(--surface)); }
.tbl-tile.s-occupied::after { background: var(--danger); }
.tbl-tile.s-occupied .tbl-status { color: var(--danger); }
.tbl-tile.s-bill-requested { border-top-color: var(--warning); background: color-mix(in srgb, var(--warning) 8%, var(--surface)); }
.tbl-tile.s-bill-requested::after { background: var(--warning); }
.tbl-tile.s-bill-requested .tbl-status { color: var(--warning); }
.tbl-tile.s-reserved { border-top-color: var(--info); background: color-mix(in srgb, var(--info) 6%, var(--surface)); }
.tbl-tile.s-reserved::after { background: var(--info); }
.tbl-tile.s-reserved .tbl-status { color: var(--info); }
.tbl-tile.s-cleaning { border-top-color: var(--border-strong); opacity: .58; }
.tbl-name { font-size: 1.3rem; font-weight: 800; letter-spacing: -.02em; line-height: 1.1; }
.tbl-status { font-size: .68rem; font-weight: 700; text-transform: uppercase; letter-spacing: .04em; color: var(--text-muted); }
.tbl-meta { margin-top: auto; display: flex; flex-wrap: wrap; gap: 2px 8px; font-size: .75rem;
  font-variant-numeric: tabular-nums; align-items: center; color: var(--text-muted); }
.tbl-total { font-weight: 800; color: var(--text); }
.tbl-clock { color: var(--text-muted); }
.tbl-edit-badge { position: absolute; inset-block-start: 6px; inset-inline-end: 8px; font-size: .9rem; opacity: .6; }
.tbl-drag-badge { position: absolute; inset-block-end: 6px; inset-inline-end: 8px; font-size: 1rem; opacity: .45; }
.tbl-server { color: var(--text-muted); }
.tbl-server-badge { cursor: pointer; border: 0; font: inherit; }
body.tbl-editing .tbl-tile { border-style: dashed; cursor: grab; touch-action: none; }
body.tbl-editing .tbl-tile::after { display: none; }
.tbl-tile.is-dragging { z-index: 5; opacity: .9; cursor: grabbing; transition: none;
  box-shadow: var(--shadow-lg); }

/* Redesign 2026-08-08: tile context menu (quick status change). */
.tbl-ctx { z-index: 60; min-width: 180px; padding: 6px; display: flex; flex-direction: column;
  background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius);
  box-shadow: var(--shadow-lg); }
.tbl-ctx-item { text-align: start; padding: 9px 12px; border: 0; border-radius: var(--radius-sm);
  background: transparent; cursor: pointer; font: inherit; color: var(--text); }
.tbl-ctx-item:hover { background: var(--surface-2); }

/* Split-bill (item / custom) + reservation rows */
.seg.seg-wrap { display: flex; flex-wrap: wrap; }
.split-line { display: flex; align-items: center; justify-content: space-between; gap: 10px;
  padding: 6px 0; border-bottom: 1px solid var(--border); }
.res-row { padding: 8px 0; border-bottom: 1px solid var(--border); }
.chip-sm { min-width: 30px; padding: 4px 8px; font-size: .85rem; }
.btn-sm { padding: 4px 10px; font-size: .85rem; }
.btn-link { background: none; border: none; color: var(--primary); padding: 0; font-size: .8rem;
  text-decoration: underline; cursor: pointer; }
.text-danger { color: var(--danger); }
.text-success { color: var(--success); }

/* Phase 3 — reservation calendar, turn analytics, waitlist, QR */
.res-row.res-due { background: var(--warning-soft); border-radius: 8px; padding: 8px;
  border-bottom: 1px solid transparent; }
.res-daynav { align-items: center; padding: 4px 0 8px; }
.res-agenda { display: flex; flex-direction: column; gap: 2px; max-height: 50vh; overflow: auto; }
.res-time { font-size: 1.15rem; font-weight: 700; font-variant-numeric: tabular-nums; }

.tbl-metric-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px; }
.tbl-metric { background: var(--surface, var(--bg)); border: 1px solid var(--border);
  border-radius: var(--radius); padding: 12px; text-align: center; }
.tbl-metric-val { font-size: 1.4rem; font-weight: 800; line-height: 1.1; }
.tbl-metric-lbl { font-size: .78rem; margin-top: 4px; }
.tbl-metric-sub { font-size: .72rem; }
@media (max-width: 560px) { .tbl-metric-grid { grid-template-columns: repeat(2, 1fr); } }

.tbl-qr-box { display: grid; place-items: center; padding: 8px; background: #fff; border-radius: 12px; }
.tbl-qr-box img, .tbl-qr-box canvas { display: block; }
.tbl-qr-link { word-break: break-all; font-size: .75rem; color: var(--text-muted); max-width: 280px; }
.tbl-qr-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(120px, 1fr)); gap: 12px;
  max-height: 56vh; overflow: auto; }
.tbl-qr-cell { display: flex; flex-direction: column; align-items: center; gap: 6px; cursor: pointer;
  background: none; border: 1px solid var(--border); border-radius: 10px; padding: 10px; }
.tbl-qr-cell:hover { border-color: var(--primary); }
.tbl-qr-mini { background: #fff; border-radius: 6px; padding: 4px; display: grid; place-items: center;
  min-height: 96px; min-width: 96px; }
.tbl-qr-mini img, .tbl-qr-mini canvas { display: block; }

/* Full-screen per-table order overlay */
.tbl-screen-scrim { position: fixed; inset: 0; z-index: 60; background: rgba(0,0,0,.5);
  display: grid; place-items: stretch; padding: clamp(0px, 2vw, 24px); }
.tbl-screen { background: var(--bg); border-radius: var(--radius-lg); overflow: hidden;
  display: flex; flex-direction: column; width: 100%; max-width: 1100px; margin: auto;
  max-height: 100%; box-shadow: var(--shadow-lg); }
.tbl-screen-head { display: flex; align-items: center; justify-content: space-between; gap: 10px;
  padding: 12px 16px; border-bottom: 1px solid var(--border); background: var(--surface); flex-wrap: wrap; }
.tbl-screen-body { display: grid; grid-template-columns: 1fr; min-height: 0; flex: 1 1 auto; }
.tbl-screen-main { display: flex; flex-direction: column; min-height: 0; overflow: hidden; }
.tbl-screen-main .product-grid { overflow-y: auto; padding: 12px; }
.tbl-order { display: flex; flex-direction: column; min-height: 0; background: var(--surface);
  border-top: 1px solid var(--border); }
.tbl-order-items { flex: 1 1 auto; overflow-y: auto; padding: 8px 12px; min-height: 120px; }
.tbl-line { display: grid; grid-template-columns: 1fr auto auto; gap: 6px 10px; align-items: center;
  padding: 8px 0; border-bottom: 1px solid var(--border); }
.tbl-line .cl-name { font-weight: 600; }

/* Coursing controls in the table order panel */
.crs-row { display: flex; align-items: center; gap: 6px; padding: 4px 0 8px;
  border-bottom: 1px dashed var(--border); margin-bottom: 4px; }
.crs-row .chip { min-height: 32px; min-width: 36px; justify-content: center; }
.crs-badge { border: 1px solid var(--border-strong); background: var(--surface-2);
  color: var(--text-muted); font-weight: 800; font-size: .76rem; border-radius: var(--radius-pill);
  padding: 4px 10px; min-height: 30px; cursor: pointer; }
.crs-badge:hover { background: var(--surface-3, var(--surface-2)); color: var(--text); }
.tbl-order-actions { padding: 12px; display: grid; gap: 8px; border-top: 1px solid var(--border); }
@media (min-width: 860px) {
  .tbl-screen-body { grid-template-columns: 1.6fr 1fr; }
  .tbl-order { border-top: 0; border-inline-start: 1px solid var(--border); }
}

/* Utility */
.divider { height: 1px; background: var(--border); border: 0; }
.sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0,0,0,0); white-space: nowrap; border: 0; }
.skip-link { position: absolute; inset-block-start: -60px; inset-inline-start: 12px;
  background: var(--primary); color: var(--text-on-primary); padding: 10px 16px;
  border-radius: var(--radius); z-index: 999; transition: top .15s ease; }
.skip-link:focus { inset-block-start: 12px; }

/* ------------------------------ Settings --------------------------------- */
.set-layout { display: grid; grid-template-columns: 220px 1fr; gap: 20px; align-items: start; }
.set-nav { display: flex; flex-direction: column; gap: 4px; position: sticky; top: 16px; }
.set-nav-item { display: flex; align-items: center; gap: 10px; padding: 10px 12px; border: 0;
  background: transparent; border-radius: var(--radius); cursor: pointer; text-align: start;
  color: var(--text); font: inherit; font-weight: 600; width: 100%; }
.set-nav-item:hover { background: var(--surface-2); }
.set-nav-item.is-active { background: var(--primary-soft, var(--surface-3)); color: var(--primary); }
.set-nav-item:focus-visible { outline: none; box-shadow: var(--ring); }

.set-card { padding: 20px; margin-bottom: 18px; display: flex; flex-direction: column; gap: 16px; }
.set-card-head { display: flex; flex-direction: column; gap: 4px; }
.set-card-title { font-size: 1.15rem; }

.set-field { display: flex; flex-direction: column; gap: 6px; }
.set-label { font-weight: 600; font-size: .92rem; }
.set-chip-row { display: flex; gap: 8px; flex-wrap: wrap; }

.set-toggle { display: flex; align-items: center; justify-content: space-between; gap: 14px;
  padding: 8px 0; cursor: pointer; border-block-end: 1px solid var(--border); }
.set-toggle:last-child { border-block-end: 0; }
.set-toggle-text { display: flex; flex-direction: column; gap: 2px; }
.switch { position: relative; flex: none; }
.switch input { position: absolute; opacity: 0; width: 100%; height: 100%; margin: 0; cursor: pointer; }
.switch-track { display: block; width: 44px; height: 26px; border-radius: 999px;
  background: var(--border-strong); transition: background .15s ease; }
.switch-thumb { display: block; width: 20px; height: 20px; border-radius: 50%; background: #fff;
  position: absolute; top: 3px; inset-inline-start: 3px; transition: transform .15s ease; }
.switch input:checked + .switch-track { background: var(--primary); }
.switch input:checked + .switch-track .switch-thumb { transform: translateX(18px); }
[dir="rtl"] .switch input:checked + .switch-track .switch-thumb { transform: translateX(-18px); }
.switch input:focus-visible + .switch-track { box-shadow: var(--ring); }

.set-theme-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(110px, 1fr)); gap: 10px; }

.set-tax-list { display: flex; flex-direction: column; }
.set-tax-row { display: flex; align-items: center; justify-content: space-between; gap: 12px;
  padding: 12px 0; border-block-end: 1px solid var(--border); }
.set-tax-row:last-child { border-block-end: 0; }

.set-mode-wrap { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
.set-mode { display: flex; flex-direction: column; gap: 4px; text-align: start; padding: 12px;
  border: 2px solid var(--border); border-radius: var(--radius); background: var(--surface);
  cursor: pointer; color: var(--text); font: inherit; }
.set-mode.is-active { border-color: var(--primary); box-shadow: var(--ring); }
.set-mode:focus-visible { outline: none; box-shadow: var(--ring); }

.set-integration { display: flex; align-items: center; justify-content: space-between; gap: 12px;
  padding: 12px 0; border-block-end: 1px solid var(--border); }
.set-integration:last-child { border-block-end: 0; }

/* UI test 21 (settings): S3 a bare 1fr track is minmax(auto,1fr) — the tax
   row's min-content grew a 375 phone to 410px; rows wrap instead. S23 the
   last card clears the sync chip. */
.set-layout { grid-template-columns: 220px minmax(0, 1fr); }
.set-main { min-width: 0; }
.set-main:focus { outline: none; }
.set-page { padding-block-end: 88px; }
.set-tax-row { flex-wrap: wrap; }
.set-row-main { min-width: 0; flex: 1 1 12rem; }
.set-row-name { flex-wrap: wrap; }
.set-row-actions { flex-wrap: wrap; margin-inline-start: auto; }
.set-loc-add { flex-wrap: wrap; }
.set-loc-add .input { flex: 1 1 12rem; min-width: 0; }

@media (max-width: 720px) {
  .set-layout { grid-template-columns: minmax(0, 1fr); }
  /* S6: eleven sections wrapped into a 332px block above every setting, and
     each pick scrolled back up to it — one sideways strip now. */
  .set-nav { flex-direction: row; flex-wrap: nowrap; position: static; overflow-x: auto;
    scrollbar-width: none; }
  .set-nav::-webkit-scrollbar { display: none; }
  .set-nav-item { width: auto; flex: none; white-space: nowrap; }
  .set-mode-wrap { grid-template-columns: 1fr; }
}

/* ---------------------- Suppliers + Purchase Orders ---------------------- */
.sup-row, .po-row { display: grid; align-items: center; gap: 12px; padding: 12px 14px;
  border-block-end: 1px solid var(--border); cursor: pointer; }
/* All-minmax(0,fr) tracks so the header (empty last cell) and body rows (action
   buttons) share identical column widths — no drift. */
/* UI test 18: the actions track holds Edit + Archive side by side (they stacked into a 2-line row) */
.sup-row { grid-template-columns: minmax(0,2fr) minmax(0,1.4fr) minmax(0,1fr) minmax(0,.6fr) minmax(216px,1.4fr); }
.sup-row > div.sup-c-act { flex-wrap: nowrap; }
.po-row { grid-template-columns: minmax(0,1.4fr) minmax(0,1.4fr) minmax(0,1fr) minmax(0,.9fr) minmax(0,.9fr) minmax(0,1.3fr); }
.sup-row > *, .po-row > * { min-width: 0; }
.sup-row > :last-child, .po-row > :last-child { justify-content: flex-end; flex-wrap: wrap; }
.sup-row:hover, .po-row:hover { background: var(--surface-2); }
.sup-row:focus-visible, .po-row:focus-visible { outline: none; box-shadow: var(--ring); }
.sup-row-head, .po-row-head { font-weight: 700; color: var(--text-muted);
  background: var(--surface-2); cursor: default; }
.sup-row-head:hover, .po-row-head:hover { background: var(--surface-2); }
/* V15 (UI test 18): an archived row fades its words, not its Restore button,
   and says "Archived" in a tag. */
.sup-row.is-archived > :not(.sup-c-act) { opacity: .6; }

.po-status { padding: 3px 10px; border-radius: 999px; font-size: .78rem; font-weight: 700; white-space: nowrap; }
.po-status.s-draft { background: var(--border); color: var(--text-muted); }
.po-status.s-sent { background: color-mix(in srgb, var(--info, #1c7ed6) 14%, transparent); color: var(--info, #1c7ed6); }
.po-status.s-partial { background: color-mix(in srgb, var(--warning, #e8a013) 16%, transparent); color: var(--warning, #b07908); }
.po-status.s-received { background: color-mix(in srgb, var(--success, #2f9e44) 16%, transparent); color: var(--success, #2f9e44); }
.po-status.s-cancelled { background: color-mix(in srgb, var(--danger) 12%, transparent); color: var(--danger); text-decoration: line-through; }

.po-edit-row { display: flex; align-items: center; gap: 10px; padding: 6px 0; border-block-end: 1px solid var(--border); }
.po-mini { display: flex; flex-direction: column; gap: 2px; }
.po-items-head, .po-items-row { display: grid; grid-template-columns: 2fr 1fr 1fr 1fr 1fr; gap: 8px;
  align-items: center; padding: 8px 12px; }
.po-items-head { font-weight: 700; color: var(--text-muted); background: var(--surface-2); }
.po-items-row { border-block-end: 1px solid var(--border); }
.po-items-row .num, .po-items-head .num { text-align: end; }
.po-short { color: var(--danger); font-weight: 700; }
.po-recv-row { display: flex; align-items: center; gap: 12px; padding: 8px 0; border-block-end: 1px solid var(--border); }
.po-recv-row.scan-flash { animation: scanFlash .6s ease-out; }
/* A batch product's line: its lot # + expiry under the quantity (Option A). */
.po-recv-lot { border-block-end: 1px solid var(--border); padding-block-end: 8px; }
.po-recv-lot > .po-recv-row { border-block-end: 0; }
.po-recv-lot-fields { flex-wrap: wrap; }
.po-recv-lot-fields > .field { margin: 0; min-width: 140px; }
/* Phase 3: a variant line's per-colour / size boxes — scroll inside, never widen the dialog. */
.po-recv-cells-wrap { max-width: 100%; overflow-x: auto; }
.po-recv-cells th, .po-recv-cells td { padding: 2px 4px; text-align: center; }
.po-recv-cells .opt-num, .cap-matrix-grid .opt-num { width: 64px; }
@keyframes scanFlash { 0% { background: var(--success-soft); } 100% { background: transparent; } }
.po-share-text { white-space: pre-wrap; background: var(--surface-2); border-radius: var(--radius);
  padding: 12px; font-size: .85rem; max-height: 240px; overflow: auto; }

/* ---- UI test 18 (suppliers) ------------------------------------------ */
.sup-page { padding-block: 24px 88px; }   /* the fixed sync chip covered the last row */
.sup-tabs { margin: 12px 0 16px; }
/* V4/V5: every list cell names its column — for a screen reader above the
   phone cut (the header row is aria-hidden), and visibly on a phone card. */
.sup-lbl { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
.sup-row > div, .po-row > div { overflow-wrap: anywhere; }
.sup-archived-tag { margin-inline-start: 8px; font-size: .72rem; vertical-align: middle; font-weight: 600; }
/* V7: a failed read says so, with a Retry */
.sup-status { display: flex; align-items: center; gap: 10px; padding: 10px 14px; border-block-end: 1px solid var(--border);
  color: var(--danger); background: color-mix(in srgb, var(--danger) 7%, transparent); }
.sup-status[hidden] { display: none; }
.sup-status .btn { color: var(--text); }
/* V22: a recent PO in the supplier detail opens */
.sup-po-link { width: 100%; background: none; border: 0; border-block-end: 1px solid var(--border); padding: 8px 4px;
  font: inherit; color: inherit; text-align: start; cursor: pointer; gap: 8px; flex-wrap: wrap; min-height: 44px; }
.sup-po-link:hover { background: var(--surface-2); }
.sup-po-link:focus-visible { outline: none; box-shadow: var(--ring); border-radius: 4px; }
/* V17: the hint and the Manual / Scan switch wrap at 378px */
.sup-recv-top { flex-wrap: wrap; gap: 8px; }
.po-recv-row { flex-wrap: wrap; }
.po-recv-row > .grow { min-width: 0; overflow-wrap: anywhere; }

@media (max-width: 720px) {
  .sup-lbl { position: static; width: auto; height: auto; overflow: visible; clip: auto; white-space: normal;
    display: block; font-size: .72rem; font-weight: 600; color: var(--text-muted); }
  .sup-c-name .sup-lbl, .po-c-num .sup-lbl { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
  .sup-row-head, .po-row-head { display: none; }
  /* V5 ★ HIGH: supplier card — name · products / contact · terms / actions */
  .sup-row { grid-template-columns: minmax(0,1fr) minmax(0,1fr); row-gap: 8px;
    grid-template-areas: "name prod" "contact terms" "act act"; }
  .sup-row .sup-c-name { grid-area: name; } .sup-row .sup-c-contact { grid-area: contact; }
  .sup-row .sup-c-terms { grid-area: terms; justify-self: end; text-align: end; }
  .sup-row .sup-c-prod { grid-area: prod; justify-self: end; text-align: end; } .sup-row .sup-c-act { grid-area: act; }
  /* V4 ★ HIGH: PO card — number · status / supplier · value / expected · Receive */
  .po-row { grid-template-columns: minmax(0,1fr) minmax(0,1fr); row-gap: 8px;
    grid-template-areas: "num st" "sup val" "exp act"; }
  .po-row .po-c-num { grid-area: num; } .po-row .po-c-st { grid-area: st; justify-self: end; text-align: end; }
  .po-row .po-c-sup { grid-area: sup; } .po-row .po-c-val { grid-area: val; justify-self: end; text-align: end; }
  .po-row .po-c-exp { grid-area: exp; align-self: end; } .po-row .po-c-act { grid-area: act; }
  .po-row .po-c-act:empty { display: none; }
}
@media (max-width: 640px) {
  /* V13: the two tabs were a two-line pill on a phone → one strip */
  .sup-tabs.seg-wrap { flex-wrap: nowrap; overflow-x: auto; scrollbar-width: none; border-radius: var(--radius); }
  .sup-tabs.seg-wrap::-webkit-scrollbar { display: none; }
  .sup-tabs .seg-btn { flex: 1 1 0; min-width: 0; white-space: normal; line-height: 1.2; }
}

/* ---------------------- Ingredients & Recipes (02 §5) ------------------- */
/* N7 (UI test 10): three Georgian row buttons stacked every row to 129px on a
   desktop and 185px on a tablet or phone, and spilled over the cost column.
   The head and every row are a SUBGRID of .ing-panel (central menu M4), so the
   actions track is content-sized ONCE for all rows; the per-row template stays
   as the fallback where subgrid is unsupported. The Unit column is gone (Cost
   and Stock carry the unit). ≤720px a row is a card — see the media query. */
.ing-panel { display: grid; column-gap: 12px; grid-template-columns: minmax(0,1fr) max-content max-content max-content; }
.ing-panel > .ing-row-head, .ing-panel > .ing-rows { grid-column: 1 / -1; }
.ing-rows { display: grid; grid-template-columns: subgrid; }
.ing-rows > * { grid-column: 1 / -1; }
.ing-row { display: grid; align-items: center; column-gap: 12px; row-gap: 6px; padding: 12px 14px;
  grid-template-columns: minmax(0,1fr) max-content max-content max-content;
  border-block-end: 1px solid var(--border); cursor: pointer; }
.ing-panel > .ing-row-head, .ing-rows > .ing-row { grid-template-columns: subgrid; }
.ing-row > * { min-width: 0; }
.ing-actions { justify-content: flex-end; flex-wrap: nowrap; }
.ing-empty { margin: 0; padding: 20px 14px; }
/* N20 the page clears the fixed sync chip · N6 the failed-read line. */
.ing-page { padding-block-end: 88px; }
.ing-status { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; margin: 0 0 12px; color: var(--danger); }
.ing-status:empty { display: none; }
.ing-row:hover { background: var(--surface-2); }
.ing-row:focus-visible { outline: none; box-shadow: var(--ring); }
.ing-row-head { font-weight: 700; color: var(--text-muted); background: var(--surface-2); cursor: default; }
.ing-row-head:hover { background: var(--surface-2); }
.ing-row.is-archived { opacity: .5; }
.ing-col { white-space: nowrap; }
.ing-col.money { font-variant-numeric: tabular-nums; }
.ing-col.low { color: var(--danger); }

/* Recipe editor (inside the product form) */
.rec-editor { border-top: 1px dashed var(--border); padding-top: 12px; margin-top: 4px; }
.rec-row { display: flex; align-items: center; gap: 8px; }
.rec-qty { display: flex; align-items: center; gap: 6px; }
.rec-qty .opt-num { width: 84px; }
/* Batch editor rows: lot · qty · expiry · warehouse wrap on a phone. */
.cap-batch-row { flex-wrap: wrap; }
.cap-batch-row .opt-num { width: 84px; }
.cap-batch-row > .select { flex: 1 1 140px; min-width: 0; }
.rec-unit { color: var(--text-muted); font-size: .82rem; min-width: 34px; }
.rec-summary { display: flex; flex-wrap: wrap; gap: 16px; margin-top: 12px; padding: 10px 12px;
  background: var(--surface-2); border-radius: var(--radius); }
.rec-warn { color: var(--danger); }

/* Food-cost report table */
.rec-rep { border: 1px solid var(--border); border-radius: var(--radius); overflow: hidden; }
.rec-rep-row { display: grid; grid-template-columns: 2fr 1fr 1fr 0.8fr 1fr; gap: 10px;
  align-items: center; padding: 8px 12px; border-block-end: 1px solid var(--border); }
.rec-rep-row:last-child { border-block-end: none; }
.rec-rep-head { font-weight: 700; color: var(--text-muted); background: var(--surface-2); }
.rec-rep-row .num { text-align: end; font-variant-numeric: tabular-nums; }
@media (max-width: 720px) {
  /* N7: a row is a card — name (+ supplier) beside cost over stock, then the
     actions on their own line. The column head has nothing to head. */
  .ing-row-head { display: none; }
  .ing-panel, .ing-rows { display: block; }
  .ing-rows > .ing-row { grid-template-columns: minmax(0,1fr) auto;
    grid-template-areas: "name cost" "name stock" "act act"; }
  .ing-row > .ing-name { grid-area: name; }
  .ing-row > .ing-cost { grid-area: cost; justify-self: end; }
  .ing-row > .ing-stock { grid-area: stock; justify-self: end; }
  .ing-row > .ing-actions { grid-area: act; justify-content: flex-start; flex-wrap: wrap; }
  .rec-rep-row { grid-template-columns: 2fr 1fr 0.8fr; }
}

/* ------------------------------ Waste log -------------------------------- */
/* waste.html — spoilage / breakage entries .
   W4 (UI test 11): at 768px the Georgian heads ran together ("რაოდენობამიზეზი")
   and Cost spilled its fr track. The head and every row are a SUBGRID of
   .waste-panel (central menu M4 / ingredients N7), so each content column is
   sized ONCE for all rows; the per-row template is the fallback where subgrid
   is unsupported. ≤720px a row is a card — see the media query. */
.waste-panel { display: grid; column-gap: 12px;
  grid-template-columns: max-content minmax(0,1fr) max-content max-content max-content max-content; }
.waste-panel > .waste-row-head, .waste-panel > .waste-rows { grid-column: 1 / -1; }
.waste-rows { display: grid; grid-template-columns: subgrid; }
.waste-rows > * { grid-column: 1 / -1; }
.waste-row { display: grid; align-items: center; column-gap: 12px; row-gap: 4px; padding: 12px 14px;
  grid-template-columns: max-content minmax(0,1fr) max-content max-content max-content max-content;
  border-block-end: 1px solid var(--border); }
.waste-panel > .waste-row-head, .waste-rows > .waste-row { grid-template-columns: subgrid; }
.waste-row > * { min-width: 0; }
.waste-row:last-child { border-block-end: none; }
.waste-row:focus-visible { outline: none; box-shadow: var(--ring); }
.waste-row-head { font-weight: 700; color: var(--text-muted); background: var(--surface-2); }
.waste-row-head > .waste-cost { text-align: end; }
.waste-col { white-space: nowrap; }
.waste-col.money { text-align: end; font-variant-numeric: tabular-nums; color: var(--danger); }
.waste-actions { display: flex; flex-direction: column; align-items: flex-end; gap: 2px; }
/* W10: a voided entry put its stock back — its cost is not a loss. */
.waste-row.is-voided .waste-cost { color: var(--text-muted); text-decoration: line-through; }
.waste-empty { margin: 0; padding: 20px 14px; }
/* W23 the page clears the fixed sync chip · W22 the total badge wraps under the title. */
.waste-page { padding-block-end: 88px; }
.waste-head { flex-wrap: wrap; gap: 8px; }
.waste-head > .badge { white-space: nowrap; }
.waste-rep { border: 1px solid var(--border); border-radius: var(--radius); overflow: hidden; }
.waste-rep-row { display: grid; grid-template-columns: 2fr auto auto; gap: 10px;
  align-items: center; padding: 8px 12px; border-block-end: 1px solid var(--border); }
.waste-rep-row:last-child { border-block-end: none; }
.waste-rep-row .num { text-align: end; font-variant-numeric: tabular-nums; }
@media (max-width: 720px) {
  /* W3 (UI test 11): on a 375px phone the nowrap date ran over the item, the
     Void button hung 15px outside the panel, and Qty + Reason were hidden —
     a manager saw ₾3.30 lost but not what or how much. A row is a card:
     item · cost / qty · reason · date / action. */
  .waste-row-head { display: none; }
  .waste-panel, .waste-rows { display: block; }
  .waste-rows > .waste-row { grid-template-columns: max-content max-content minmax(0,1fr) max-content;
    grid-template-areas: "item item item cost" "qty reason date date" "act act act act"; }
  .waste-row > .waste-item { grid-area: item; }
  .waste-row > .waste-cost { grid-area: cost; align-self: start; }
  .waste-row > .waste-qty { grid-area: qty; }
  .waste-row > .waste-reason { grid-area: reason; }
  .waste-row > .waste-date { grid-area: date; justify-self: end; }
  .waste-row > .waste-actions { grid-area: act; align-items: flex-start; }
}
@media (max-width: 640px) {
  /* W22: the reason filter shares the tools toggle's row; search goes last. */
  .waste-page .inv-toolbar > #reasonFilter { flex: 1 1 120px; max-width: none !important; }
  .waste-page .inv-toolbar > .input-search { order: 1; }
}

/* ----------------------------- Warehouses ------------------------------- */
/* warehouses.html — multi-warehouse stock, summary cards, transfer matrix. */
.wh-cards { display: grid; gap: 14px; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); }
.wh-card { display: flex; flex-direction: column; gap: 12px; padding: 16px;
  background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); }
/* An archived warehouse that still holds stock (Option A, 2026-09-29). */
.wh-card-stranded { border-color: var(--warning); border-style: dashed; }
.wh-metrics { display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px;
  padding: 10px 0; border-block: 1px solid var(--border); }
.wh-metric { text-align: center; }
.wh-metric .num { font-size: 1.25rem; font-weight: 700; font-variant-numeric: tabular-nums; }
/* W12 (UI test 9): min-width:480px scrolled a phone sideways even with two
   warehouses that fit; the named region still scrolls when many do not. */
.wh-matrix { width: 100%; border-collapse: collapse; }
.wh-matrix th, .wh-matrix td { padding: 10px 12px; border-block-end: 1px solid var(--border); text-align: start; }
.wh-matrix th { color: var(--text-muted); font-weight: 700; background: var(--surface-2); }
.wh-matrix .num { text-align: end; font-variant-numeric: tabular-nums; }
.wh-matrix tbody tr:hover { background: var(--surface-2); }
/* UI test 9: W19 the page clears the fixed sync chip · W5 the failed-read line ·
   W18 the card head wraps its badges below the name · W17 20px checkboxes. */
.wh-page { padding-block-end: 88px; }
.wh-status { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; margin: 0 0 12px; color: var(--danger); }
.wh-status:empty { display: none; }
.wh-card-head { display: flex; flex-wrap: wrap; align-items: flex-start; justify-content: space-between; gap: 8px; }
.wh-card-head > div:first-child { flex: 1 1 140px; min-width: 0; }
.wh-card-head > .row { flex-wrap: wrap; }
.wh-flag { align-items: flex-start; min-height: 36px; cursor: pointer; }
.wh-flag input { width: 20px; height: 20px; accent-color: var(--primary); flex: 0 0 auto; margin-block-start: 2px; }
.wh-transfer-grid { display: grid; grid-template-columns: 1fr auto 1fr; gap: 12px; align-items: end; }
.wh-arrow { padding-block-end: 10px; font-size: 1.4rem; color: var(--text-muted); }
.wh-hist-row { display: flex; align-items: center; justify-content: space-between; gap: 12px;
  padding: 10px 12px; border: 1px solid var(--border); border-radius: var(--radius); }
@media (max-width: 560px) {
  .wh-transfer-grid { grid-template-columns: 1fr; }
  /* W12: Georgian headers + 12px padding still ran two warehouses 34px off a phone. */
  .wh-matrix th, .wh-matrix td { padding-inline: 6px; }
  .wh-matrix th { font-size: .84rem; }
  .wh-arrow { display: none; }
}

/* --------------------------- Guest self-order ---------------------------- */
/* selforder.html — touch-first menu a guest reaches by scanning a table QR. */
.so-body { background: var(--bg); min-height: 100vh; }

/* FINA-parity F5: self-service kiosk — attract screen + order-placed screen */
.so-kiosk-attract { position: fixed; inset: 0; z-index: 50; display: none;
  background: var(--bg); }
.so-kiosk.kiosk-attracting .so-kiosk-attract { display: grid; place-items: center; padding: 24px; }
.so-kiosk.kiosk-attracting .so-header,
.so-kiosk.kiosk-attracting .so-cart-bar { visibility: hidden; }
.kiosk-attract-inner { display: flex; flex-direction: column; align-items: center; gap: clamp(16px,4vh,40px); text-align: center; }
.kiosk-logo { font-size: clamp(2rem,7vw,4rem); font-weight: 800; letter-spacing: -.01em; }
.kiosk-tagline { font-size: clamp(1rem,2.6vw,1.5rem); color: var(--text-muted); }
.kiosk-start { font-size: clamp(1.2rem,3vw,1.8rem); padding: clamp(14px,2.4vh,26px) clamp(28px,6vw,64px);
  border-radius: 999px; animation: kioskPulse 2.4s ease-in-out infinite; }
@keyframes kioskPulse { 0%,100% { transform: scale(1); } 50% { transform: scale(1.04); } }
.kiosk-langs { display: flex; flex-wrap: wrap; gap: 10px; justify-content: center; }
.kiosk-done { display: flex; flex-direction: column; align-items: center; gap: 14px; text-align: center;
  padding: clamp(24px,6vh,64px) 16px; }
.kiosk-done-icon { font-size: clamp(3rem,10vw,5rem); }
.kiosk-ordernum { display: flex; flex-direction: column; align-items: center; gap: 4px; }
.kiosk-ordernum strong { font-size: clamp(2.4rem,9vw,4.4rem); font-weight: 800; font-variant-numeric: tabular-nums; color: var(--primary); }
.kiosk-pay { font-size: clamp(1.05rem,2.6vw,1.4rem); font-weight: 600; }
.kiosk-done-btn { margin-top: 8px; border-radius: 999px; min-width: 220px; }
/* sweep F19: the guest name called out at the counter, echoed on the done card */
.kiosk-name { font-size: clamp(1.1rem,3vw,1.6rem); font-weight: 700; margin: 0; }
.so-kiosk-name { font-size: 1.05rem; }
/* sweep F6: the kiosk LAUNCH gate — a tablet must redeem a JP-KIOSK- code
   before it can trade, so this is what an un-enrolled tablet shows instead of
   a menu whose orders the rules would refuse. */
.so-kiosk-setup-card { max-width: 460px; margin: 12vh auto; display: flex; flex-direction: column;
  gap: 14px; text-align: center; padding: 24px; }
.so-kiosk-setup-card h2 { margin: 0; font-size: clamp(1.3rem,3.4vw,1.9rem); }
.so-kiosk-setup-card .input { text-align: center; font-size: 1.15rem; letter-spacing: .06em; }
.so-kiosk-setup .so-welcome, .so-kiosk-setup .so-cart-bar { display: none; }


.so-header { position: sticky; inset-block-start: 0; z-index: 20;
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  padding: 12px 16px; background: var(--surface); border-block-end: 1px solid var(--border); }
.so-biz { display: flex; align-items: center; gap: 10px; min-width: 0; }
.so-biz-name { font-weight: 700; font-size: 1.15rem; color: var(--text); margin: 0; line-height: 1.3;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* SO6 (UI test 23): the way back to an order already placed. */
.so-track { margin: 0 0 12px; }
.so-confirm h2:focus { outline: none; }
.so-confirm h2:focus-visible { outline: 2px solid var(--primary); outline-offset: 4px; border-radius: 6px; }
.so-table-badge { background: var(--primary); color: #fff; }
.so-main { max-width: 760px; margin: 0 auto; padding: 16px 16px 96px; }
.so-welcome { color: var(--text-muted); margin: 0 0 12px; }
/* Module 25 F5/F6: why this QR cannot take an order — shown before any choice. */
.so-banner { margin: 0 0 12px; padding: 12px 14px; border-radius: var(--radius, 12px);
  background: var(--danger-soft); color: color-mix(in srgb, var(--danger) 60%, var(--text)); font-weight: 600; }
.so-cats { margin-bottom: 12px; }
.so-cats .chip-row { overflow-x: auto; }

/* Floating basket bar */
.so-cart-bar { position: fixed; inset-block-end: 16px; inset-inline: 16px; z-index: 30;
  max-width: 728px; margin: 0 auto; display: flex; align-items: center; gap: 12px;
  padding: 14px 18px; border: none; border-radius: var(--radius);
  background: var(--primary); color: #fff; font: inherit; font-weight: 600;
  cursor: pointer; box-shadow: 0 6px 20px rgba(0,0,0,.25); }
.so-cart-bar:focus-visible { outline: none; box-shadow: var(--ring); }
.so-bar-count { background: rgba(255,255,255,.22); padding: 3px 9px; border-radius: 999px; font-size: .85rem; }
.so-bar-label { flex: 1; text-align: center; }
.so-bar-total { font-size: 1.05rem; }

/* Basket sheet */
.so-sheet-body { display: flex; flex-direction: column; gap: 12px; }
.so-line, .so-status-row { display: flex; align-items: center; gap: 12px; }
.so-line-icon { font-size: 1.5rem; flex: none; }
.so-line-main { flex: 1; min-width: 0; display: flex; flex-direction: column; }
.so-line-name { font-weight: 600; color: var(--text); }
.so-line-price { color: var(--text-muted); font-size: .9rem; }
.so-line-note { color: var(--text-muted); font-size: .8rem; font-style: italic; }
.so-stepper { display: flex; align-items: center; gap: 6px; flex: none; }
.so-qty { min-width: 24px; text-align: center; font-weight: 700; }
.so-note { resize: vertical; }
.so-sheet-total { display: flex; align-items: center; justify-content: space-between;
  padding-block-start: 10px; border-block-start: 1px solid var(--border); font-size: 1.05rem; }
.so-sheet-hint { color: var(--text-muted); font-size: .85rem; margin: 0; }

/* Confirmation / live status view */
.so-confirm { text-align: center; padding: 24px 0 8px; }
.so-confirm-icon { font-size: 3rem; }
.so-confirm h2 { margin: 8px 0 4px; }
.so-confirm-sub { color: var(--text-muted); margin: 4px 0; }
.so-status-card { display: flex; flex-direction: column; gap: 10px;
  background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius);
  padding: 14px; margin: 16px 0; }
.so-status-badge { flex: none; padding: 4px 10px; border-radius: 999px; font-size: .8rem; font-weight: 600; white-space: nowrap; }
.so-status-badge.s-pending { background: color-mix(in srgb, var(--warning, #e8a013) 16%, transparent); color: var(--warning, #b07908); }
.so-status-badge.s-prep { background: color-mix(in srgb, var(--info, #1c7ed6) 14%, transparent); color: var(--info, #1c7ed6); }
.so-status-badge.s-ready { background: color-mix(in srgb, var(--success, #2f9e44) 16%, transparent); color: var(--success, #2f9e44); }
.so-status-badge.s-served { background: var(--border); color: var(--text-muted); }
.so-actions { display: flex; flex-direction: column; gap: 10px; margin-top: 16px; }

@media (max-width: 480px) {
  .so-main { padding: 12px 12px 96px; }
}

/* ------------------------------- Print ----------------------------------- */
/* When printing a receipt, hide everything except the .print-receipt node.  */
@media print {
  body * { visibility: hidden !important; }
  .print-receipt, .print-receipt * { visibility: visible !important; }
  .print-receipt {
    position: absolute; inset-block-start: 0; inset-inline-start: 0; width: 100%;
    color: #000; background: #fff;
  }
  .scrim { position: static !important; background: none !important; }
}

/* ===================== Staff & HR / Shifts  ============== */
.btn-success { --btn-bg: var(--success, #2f9e44); --btn-fg: #fff; }
.btn-success:hover { background: color-mix(in srgb, var(--success, #2f9e44) 85%, var(--text)); }

/* Shared form grid for dialogs + filter panels */
.form-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 12px 16px; }
.form-grid .col-2 { grid-column: 1 / -1; }
.field-label { font-size: 0.85rem; font-weight: 600; color: var(--text-muted); }
.staff-form-panel { padding: 16px; }

.role-badge { display: inline-block; padding: 2px 9px; border-radius: var(--radius-pill);
  background: var(--surface-2); border: 1px solid var(--border); font-size: .78rem; font-weight: 600; }

/* Generic staff list rows (grid templates per section) */
.staff-row, .staff-te-row, .staff-perf-row, .staff-shift-row, .staff-audit-row {
  display: grid; align-items: center; gap: 10px; padding: 11px 14px;
  border-block-end: 1px solid var(--border); }
.staff-row { grid-template-columns: minmax(0,2fr) minmax(0,1fr) minmax(0,1.2fr) minmax(0,.9fr) minmax(0,1.3fr); }
.staff-te-row { grid-template-columns: minmax(0,1.4fr) minmax(0,1.3fr) minmax(0,1.3fr) minmax(0,.7fr) minmax(0,.8fr) minmax(0,.8fr) minmax(0,1.2fr); }
.staff-row > *, .staff-te-row > * { min-width: 0; }
.staff-perf-row { grid-template-columns: 1.6fr 1fr .7fr 1fr 1fr .9fr; }
.staff-shift-row { grid-template-columns: 1.4fr 1.4fr 1fr .7fr 1fr; cursor: pointer; }
.staff-audit-row { grid-template-columns: 1.3fr 1.2fr .9fr 2fr .9fr; }
.staff-row-head, .staff-te-row-head, .staff-perf-row-head, .staff-shift-row-head, .staff-audit-row-head {
  color: var(--text-muted); font-weight: 700; font-size: .82rem; background: var(--surface-2); }
.staff-shift-row:hover, .staff-row:hover { background: var(--surface-2); }
.staff-row.is-inactive { opacity: .6; }
.staff-id strong { display: block; }
.staff-actions { display: flex; gap: 6px; justify-content: flex-end; }

/* Time clock cards */
.staff-clock-cards { display: grid; gap: 10px; grid-template-columns: repeat(auto-fill, minmax(230px, 1fr));
  margin-block: 12px; }
.staff-clock-card { display: flex; align-items: center; justify-content: space-between; gap: 12px;
  padding: 12px 14px; background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); }
.staff-clock-card.is-on { border-color: var(--success, #2f9e44); box-shadow: 0 0 0 1px var(--success, #2f9e44) inset; }

/* Metrics */
.staff-metric { text-align: center; padding: 10px; }
.staff-metric-val { font-size: 1.35rem; font-weight: 800; font-variant-numeric: tabular-nums; }
.staff-metric-lbl { font-size: .78rem; color: var(--text-muted); }

/* Payslip */
.staff-payslip { padding: 16px; margin-top: 14px; max-width: 460px; }
/* Payroll run (Session D): employee · hours · pay · tips · deductions · gross */
.staff-run-row, .staff-run-hist-row { display: grid; align-items: center; gap: 10px; padding: 9px 14px;
  border-block-end: 1px solid var(--border); }
/* Owner 2026-10-05: a delete refused for unpaid hours lists the periods to pay */
.staff-unpaid-period { display: flex; align-items: center; justify-content: space-between; gap: 10px; flex-wrap: wrap;
  padding: 8px 10px; border: 1px solid var(--border); border-radius: var(--radius); }
.staff-unpaid-period .field-hint { margin: 2px 0 0; }
.staff-run-row { grid-template-columns: minmax(0,1.5fr) minmax(0,1fr) minmax(0,1fr) minmax(0,.9fr) minmax(0,.9fr) minmax(0,1fr); }
.staff-run-hist-row { grid-template-columns: 1.2fr .8fr 1.5fr .6fr .9fr 1.2fr; }
.staff-run-row > *, .staff-run-hist-row > * { min-width: 0; }
.staff-run-row-head, .staff-run-hist-row-head { color: var(--text-muted); font-weight: 700; font-size: .82rem; background: var(--surface-2); }
.staff-run-total { font-weight: 800; border-block-end: 0; border-top: 2px solid var(--border); }
.staff-run-row .staff-perm-num { max-width: 110px; }
.staff-pay-row { padding: 7px 0; border-block-end: 1px dashed var(--border); }
.staff-pay-total { padding-top: 12px; margin-top: 6px; border-top: 2px solid var(--border);
  font-size: 1.15rem; font-weight: 800; }

/* Permission toggles */
.staff-perm-row { padding: 11px 2px; border-block-end: 1px solid var(--border); }
.staff-perm-num { max-width: 90px; text-align: end; }

/* Audit action badges */
.action-badge { display: inline-block; padding: 2px 8px; border-radius: var(--radius-pill);
  font-size: .72rem; font-weight: 700; background: var(--surface-2); border: 1px solid var(--border); }
.action-badge.act-void { color: var(--danger); border-color: var(--danger); }
.action-badge.act-refund { color: var(--warning, #e8590c); border-color: var(--warning, #e8590c); }
.action-badge.act-discount { color: var(--primary); border-color: var(--primary); }
/* Session B (D7/H11): a platform operator's presence and a rewritten time entry
   are the two rows a tenant most needs to notice in their own audit log. */
.action-badge.act-impersonate { color: var(--danger); border-color: var(--danger); font-weight: 600; }
.action-badge.act-time_entry { color: var(--warning, #e8590c); border-color: var(--warning, #e8590c); }
/* offline parity: a payroll run parked in the outbox / one that clashed at replay */
.action-badge.act-payroll-pending { color: var(--warning, #e8590c); border-color: var(--warning, #e8590c); margin-left: 6px; }
.action-badge.act-payroll-conflict { color: var(--danger); border-color: var(--danger); margin-left: 6px; }
.staff-run-hist-row-conflict { opacity: .75; }
.staff-run-conflict { margin: 0 14px 10px; display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }

/* ==================== Staff Scheduling  ============ */
.sched-toolbar { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; margin-block: 12px; }
.sched-weeknav { display: flex; align-items: center; gap: 8px; }
.sched-weeklabel { font-weight: 700; font-variant-numeric: tabular-nums; min-width: 168px; text-align: center; }

/* Horizontal-scroll wrapper keeps the 7-col grid usable on phones */
.sched-grid-wrap { overflow-x: auto; -webkit-overflow-scrolling: touch;
  border: 1px solid var(--border); border-radius: var(--radius); background: var(--surface); }
.sched-grid, .sched-foot {
  display: grid; grid-template-columns: 150px repeat(7, minmax(116px, 1fr)); min-width: 760px; }
.sched-colhead { background: var(--surface-2); font-weight: 700; font-size: .8rem; text-align: center;
  padding: 8px 6px; border-block-end: 1px solid var(--border); border-inline-end: 1px solid var(--border); }
.sched-colhead.is-today { color: var(--primary); }
.sched-colhead-dow { display: block; font-size: .72rem; color: var(--text-muted); font-weight: 600; }
.sched-rowhead { display: flex; flex-direction: column; justify-content: center; gap: 2px; padding: 8px 10px;
  background: var(--surface-2); font-weight: 600; font-size: .82rem;
  border-block-end: 1px solid var(--border); border-inline-end: 1px solid var(--border); }
.sched-rowhead .muted { font-weight: 400; }
/* An archived member still owns the shifts already on the roster (audit
   2026-08-30, M10) — dimmed so the row reads as a leftover, not a hire. */
.sched-rowhead.is-archived { opacity: .62; }
.sched-cell { border-block-end: 1px solid var(--border); border-inline-end: 1px solid var(--border);
  padding: 6px; min-height: 66px; }
.sched-cell.is-today { background: color-mix(in srgb, var(--primary) 6%, transparent); }
.sched-cell.is-off { background: var(--surface-2); opacity: .8; }
.sched-add { width: 100%; border: 1px dashed var(--border); background: transparent; color: var(--text-muted);
  border-radius: 6px; padding: 5px; cursor: pointer; font-size: 1rem; line-height: 1; }
.sched-add:hover { border-color: var(--primary); color: var(--primary); }
.sched-off-badge { font-size: .72rem; color: var(--text-muted); display: block; padding: 2px; }

.sched-chip { display: block; width: 100%; text-align: start; border: none;
  border-inline-start: 3px solid var(--primary); background: var(--surface-2); border-radius: 6px;
  padding: 4px 7px; margin-block-end: 4px; cursor: pointer; font-size: .78rem; line-height: 1.3; }
.sched-chip:hover { filter: brightness(.97); }
.sched-chip-time { font-weight: 700; font-variant-numeric: tabular-nums; }
.sched-chip-role { color: var(--text-muted); font-size: .72rem; }

.sched-foot { border-block-start: 2px solid var(--border); background: var(--surface-2); font-size: .73rem; }
.sched-foot-cell { padding: 6px; text-align: center; border-inline-end: 1px solid var(--border); }
.sched-foot-lbl { padding: 6px 10px; font-weight: 700; border-inline-end: 1px solid var(--border); }
.sched-foot-cell strong { display: block; font-variant-numeric: tabular-nums; }

.sched-summary { display: flex; flex-wrap: wrap; gap: 10px; margin-block: 14px; }
.sched-summary .staff-metric { background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius); min-width: 120px; flex: 1; }
.sched-pct.is-green { color: var(--success, #2f9e44); }
.sched-pct.is-amber { color: var(--warning, #e8590c); }
.sched-pct.is-red { color: var(--danger); }

/* Adherence dialog */
.sched-adh-row { display: grid; grid-template-columns: 1.4fr 1fr .8fr .8fr 1fr; gap: 8px; align-items: center;
  padding: 9px 4px; border-block-end: 1px solid var(--border); }
.sched-adh-row-head { color: var(--text-muted); font-weight: 700; font-size: .8rem; }
.sched-status { display: inline-block; padding: 2px 8px; border-radius: var(--radius-pill); font-size: .72rem;
  font-weight: 700; border: 1px solid var(--border); }
.sched-status.st-on-time { color: var(--success, #2f9e44); border-color: var(--success, #2f9e44); }
.sched-status.st-late { color: var(--warning, #e8590c); border-color: var(--warning, #e8590c); }
.sched-status.st-absent { color: var(--danger); border-color: var(--danger); }
.sched-status.st-overtime { color: var(--primary); border-color: var(--primary); }

@media (max-width: 760px) {
  .sched-rowhead { font-size: .78rem; padding: 6px 8px; }
  .sched-weeklabel { min-width: 120px; font-size: .85rem; }
}

@media (max-width: 640px) {
  .form-grid { grid-template-columns: 1fr; }
  .staff-row { grid-template-columns: minmax(0,2fr) minmax(0,.8fr) minmax(0,1.2fr); }
  .staff-te-row { grid-template-columns: minmax(0,1.2fr) minmax(0,1.2fr) minmax(0,1.2fr) minmax(0,.7fr) minmax(0,1fr); }
  .staff-perf-row { grid-template-columns: 1.6fr 1fr .7fr .9fr; }
  .staff-run-row { grid-template-columns: minmax(0,1.4fr) minmax(0,1fr) minmax(0,1fr) minmax(0,1fr); }
  .staff-run-hist-row { grid-template-columns: 1.2fr .8fr 1.5fr 1fr; }
  .staff-shift-row { grid-template-columns: 1.4fr 1fr 1fr; }
  .staff-audit-row { grid-template-columns: 1.3fr 1.1fr 1fr; }
}

/* ---- UI test 17 (staff & shifts) ---------------------------------------- */
.staff-page { padding-block-end: 88px; }   /* S21: the fixed sync chip covered the last row */
/* S7: fr tracks without minmax(0) grew to their content (perf 432px at 375) */
.staff-perf-row { grid-template-columns: minmax(0,1.6fr) minmax(0,1fr) minmax(0,.7fr) minmax(0,1fr) minmax(0,1fr) minmax(0,.9fr); }
.staff-shift-row { grid-template-columns: minmax(0,1.4fr) minmax(0,1.4fr) minmax(0,1fr) minmax(0,.7fr) minmax(0,1fr); }
.staff-audit-row { grid-template-columns: minmax(0,1.3fr) minmax(0,1.2fr) minmax(0,.9fr) minmax(0,2fr) minmax(0,.9fr); }
.staff-perf-row > *, .staff-shift-row > *, .staff-audit-row > * { min-width: 0; overflow-wrap: anywhere; }
/* S5–S7: every list cell names its column — for a screen reader above the
   phone cut (the header row is for eyes), and visibly on a phone card. */
.staff-lbl { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
.staff-c-who { min-width: 0; overflow-wrap: anywhere; }
/* S11: a shift row opens by keyboard too */
.staff-rowlink { background: none; border: 0; padding: 0; margin: 0; font: inherit; color: inherit; text-align: start; cursor: pointer; }
.staff-rowlink:focus-visible { outline: none; box-shadow: var(--ring); border-radius: 4px; }
.staff-status { margin-block: 8px; align-items: center; }
/* S24: a role's individual exceptions, under its switches */
.staff-perm-exc { padding: 10px 2px 4px; border-block-end: 1px solid var(--border); }
.staff-perm-exc-list { margin: 4px 0 0; padding-inline-start: 18px; display: grid; gap: 4px; font-size: .9rem; }
/* 2026-09-29: each exception carries "use the role's settings"; on a phone the button drops under the text. */
.staff-perm-exc-item { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 6px 12px; }
.staff-perm-exc-item > span { min-width: 0; overflow-wrap: anywhere; }
.staff-perm-exc-item .btn-sm { min-height: 44px; }
/* S17: the invite form's fields carry labels */
.staff-invite { display: flex; gap: 8px; flex-wrap: wrap; align-items: flex-end; }
.staff-invite .field { flex: 1 1 200px; min-width: 0; margin: 0; }
/* S27: the roster's names stay in view while the week scrolls sideways */
.sched-rowhead, .sched-grid > .sched-colhead:first-child, .sched-foot-lbl { position: sticky; inset-inline-start: 0; z-index: 1; }
.sched-foot-lbl { background: var(--surface-2); }

@media (max-width: 767.98px) {
  .staff-lbl { position: static; width: auto; height: auto; overflow: visible; clip: auto; white-space: normal;
    display: block; font-size: .72rem; font-weight: 600; color: var(--text-muted); }
  .staff-roster .staff-lbl { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
  .staff-row-head, .staff-te-row-head, .staff-perf-row-head, .staff-shift-row-head,
  .staff-audit-row-head, .staff-run-row-head, .staff-run-hist-row-head { display: none; }

  /* S4 ★ HIGH: roster card — name · status / role · pay / actions */
  .staff-roster .staff-row { grid-template-columns: minmax(0,1fr) auto; row-gap: 8px;
    grid-template-areas: "id status" "role pay" "act act"; }
  .staff-roster .staff-id { grid-area: id; }
  .staff-roster .staff-c-status { grid-area: status; justify-self: end; }
  .staff-roster .staff-c-role { grid-area: role; }
  .staff-roster .staff-c-pay { grid-area: pay; justify-self: end; }
  .staff-roster .staff-actions { grid-area: act; justify-content: flex-start; flex-wrap: wrap; }

  /* S5 ★ HIGH: time entry card — who · Edit / in · out / regular · overtime · break */
  .staff-te-row { grid-template-columns: repeat(6, minmax(0,1fr)); row-gap: 6px;
    grid-template-areas: "who who who who act act" "in in in out out out" "reg reg ot ot brk brk"; }
  .staff-te-row .staff-c-who { grid-area: who; } .staff-te-row .staff-c-act { grid-area: act; justify-self: end; }
  .staff-te-row .staff-c-in { grid-area: in; } .staff-te-row .staff-c-out { grid-area: out; }
  .staff-te-row .staff-c-reg { grid-area: reg; } .staff-te-row .staff-c-ot { grid-area: ot; }
  .staff-te-row .staff-c-brk { grid-area: brk; }

  /* S7: performance card */
  .staff-perf-row { grid-template-columns: repeat(3, minmax(0,1fr)); row-gap: 6px;
    grid-template-areas: "who who rev" "txn avg disc" "ref ref ref"; }
  .staff-perf-row .staff-c-who { grid-area: who; } .staff-perf-row .staff-c-rev { grid-area: rev; justify-self: end; text-align: end; }
  .staff-perf-row .staff-c-txn { grid-area: txn; } .staff-perf-row .staff-c-avg { grid-area: avg; }
  .staff-perf-row .staff-c-disc { grid-area: disc; } .staff-perf-row .staff-c-ref { grid-area: ref; }

  /* S11: shift card — who · variance / opened · revenue / transactions */
  .staff-shift-row { grid-template-columns: repeat(2, minmax(0,1fr)); row-gap: 6px;
    grid-template-areas: "who var" "when rev" "txn txn"; }
  .staff-shift-row .staff-c-who { grid-area: who; } .staff-shift-row .staff-c-var { grid-area: var; justify-self: end; text-align: end; }
  .staff-shift-row .staff-c-when { grid-area: when; } .staff-shift-row .staff-c-rev { grid-area: rev; justify-self: end; text-align: end; }
  .staff-shift-row .staff-c-txn { grid-area: txn; }

  /* S12: audit card — when · amount / who · action / details */
  .staff-audit-row { grid-template-columns: repeat(2, minmax(0,1fr)); row-gap: 6px;
    grid-template-areas: "when amt" "who act" "det det"; }
  .staff-audit-row .staff-c-when { grid-area: when; } .staff-audit-row .staff-c-amt { grid-area: amt; justify-self: end; text-align: end; }
  .staff-audit-row .staff-c-who { grid-area: who; } .staff-audit-row .staff-c-act { grid-area: act; justify-self: end; text-align: end; }
  .staff-audit-row .staff-c-det { grid-area: det; }

  /* S6 ★ HIGH: payroll run card — who · gross / hours · pay / tips · deductions */
  .staff-run-row { grid-template-columns: repeat(2, minmax(0,1fr)); row-gap: 6px;
    grid-template-areas: "who gross" "hrs pay" "tips ded"; }
  .staff-run-row .staff-c-who { grid-area: who; } .staff-run-row .staff-c-gross { grid-area: gross; justify-self: end; text-align: end; }
  .staff-run-row .staff-c-hrs { grid-area: hrs; } .staff-run-row .staff-c-pay { grid-area: pay; justify-self: end; text-align: end; }
  .staff-run-row .staff-c-tips { grid-area: tips; } .staff-run-row .staff-c-ded { grid-area: ded; justify-self: end; text-align: end; }
  .staff-run-row .staff-perm-num { max-width: none; width: 100%; }
  .staff-run-total { grid-template-areas: "who gross"; }
  .staff-run-gap { display: none; }
  .staff-run-hist-row { grid-template-columns: repeat(2, minmax(0,1fr)); row-gap: 6px;
    grid-template-areas: "when kind" "period gross" "count by"; }
  .staff-run-hist-row .staff-c-when { grid-area: when; } .staff-run-hist-row .staff-c-kind { grid-area: kind; justify-self: end; }
  .staff-run-hist-row .staff-c-period { grid-area: period; } .staff-run-hist-row .staff-c-gross { grid-area: gross; justify-self: end; text-align: end; }
  .staff-run-hist-row .staff-c-count { grid-area: count; } .staff-run-hist-row .staff-c-by { grid-area: by; justify-self: end; text-align: end; }
}
@media (max-width: 640px) {
  /* S26: nine tabs wrapped into a 208px pill on a phone → one sideways strip */
  .staff-tabs.seg-wrap { flex-wrap: nowrap; overflow-x: auto; scrollbar-width: none; border-radius: var(--radius);
    -webkit-overflow-scrolling: touch; }
  .staff-tabs.seg-wrap::-webkit-scrollbar { display: none; }
  .staff-tabs .seg-btn { flex: none; white-space: nowrap; }
  .sched-grid, .sched-foot { grid-template-columns: 112px repeat(7, minmax(116px, 1fr)); }
}

/* ==================== Admin Panel  ================== */
/* Operator-only screens. .admin-* namespace; banners also render on tenant
   manager panels (announcements / maintenance / impersonation).             */

/* Tenant list */
.admin-row { display: grid; align-items: center; gap: 10px; padding: 11px 14px;
  grid-template-columns: 2.2fr 1fr 1fr 1.1fr .8fr; cursor: pointer;
  border-block-end: 1px solid var(--border); }
.admin-row:hover { background: var(--surface-2); }
.admin-row-head { color: var(--text-muted); font-weight: 700; font-size: .82rem;
  background: var(--surface-2); cursor: default; }
.admin-row.is-deleted { opacity: .55; }
/* Let fr tracks shrink past their content instead of forcing horizontal
   overflow, and keep long business names / emails inside their cell. */
.admin-row > * { min-width: 0; }
.admin-biz { min-width: 0; }
.admin-biz strong, .admin-biz > span { display: block; overflow: hidden;
  text-overflow: ellipsis; white-space: nowrap; }

/* Status badges */
.admin-status { display: inline-block; padding: 2px 9px; border-radius: var(--radius-pill);
  font-size: .74rem; font-weight: 700; background: var(--surface-2); border: 1px solid var(--border); }
.admin-status.s-active { color: var(--success, #2f9e44); border-color: var(--success, #2f9e44); }
.admin-status.s-trial { color: var(--primary); border-color: var(--primary); }
.admin-status.s-suspended { color: var(--warning, #e8590c); border-color: var(--warning, #e8590c); }
.admin-status.s-churned, .admin-status.s-deleted { color: var(--danger); border-color: var(--danger); }

/* Dashboard metric cards */
@media (max-width: 640px) { .admin-metrics { grid-template-columns: repeat(2, 1fr) !important; } }
.admin-metrics { display: grid; gap: 10px; grid-template-columns: repeat(auto-fill, minmax(170px, 1fr));
  margin-block: 12px; }
.admin-metric { text-align: center; padding: 14px 10px; background: var(--surface);
  border: 1px solid var(--border); border-radius: var(--radius); }
.admin-metric-val { font-size: 1.4rem; font-weight: 800; font-variant-numeric: tabular-nums; }
.admin-metric-lbl { font-size: .78rem; color: var(--text-muted); margin-top: 2px; }
.admin-metric.is-danger .admin-metric-val { color: var(--danger); }
.admin-metric.is-warn .admin-metric-val { color: var(--warning, #e8590c); }

/* Lightweight CSS bar charts (tenant counts are small; no chart lib needed) */
.admin-bars { display: flex; flex-direction: column; gap: 8px; padding: 4px 0; }
.admin-bar { display: grid; grid-template-columns: 130px 1fr 56px; gap: 10px; align-items: center; }
.admin-bar-label { font-size: .82rem; color: var(--text-muted); overflow: hidden;
  text-overflow: ellipsis; white-space: nowrap; }
.admin-bar-track { background: var(--surface-2); border-radius: var(--radius-pill); height: 14px; overflow: hidden; }
.admin-bar-fill { background: var(--primary); height: 100%; border-radius: var(--radius-pill);
  min-width: 2px; transition: width .25s ease; }
.admin-bar-val { font-size: .82rem; font-weight: 700; font-variant-numeric: tabular-nums; text-align: end; }

/* Tenant detail */
.admin-kv { display: grid; grid-template-columns: auto 1fr; gap: 4px 14px; font-size: .9rem; margin: 0; }
.admin-kv dt { color: var(--text-muted); }
.admin-kv dd { margin: 0; font-weight: 600; }
.admin-actions-grid { display: grid; gap: 8px; grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); }
.admin-note { padding: 9px 0; border-block-end: 1px dashed var(--border); font-size: .88rem; }
.admin-note-meta { color: var(--text-muted); font-size: .76rem; margin-top: 2px; }
.admin-bill-row { display: grid; grid-template-columns: 1fr 1fr .8fr .8fr; gap: 10px;
  padding: 8px 0; border-block-end: 1px dashed var(--border); font-size: .88rem; }
.admin-act-row { padding: 7px 0; border-block-end: 1px dashed var(--border); font-size: .85rem; }

/* Tenant detail — sectioned profile (redesign 2026-08-12). Replaces the flat
   dump with an identity hero + titled section cards. Token-driven (works across
   all admin-hud themes + light/dark); responsive 320–1440. */
.admin-detail { display: flex; flex-direction: column; gap: 14px; min-width: 0; }
.admin-hero { display: flex; flex-wrap: wrap; gap: 14px 20px; align-items: flex-start;
  justify-content: space-between; padding: 16px 18px; border: 1px solid var(--border);
  border-radius: var(--radius); background: var(--surface-2); }
.admin-hero-main { min-width: 0; flex: 1 1 240px; display: flex; flex-direction: column; gap: 6px; }
.admin-hero-name { margin: 0; font-size: 1.3rem; font-weight: 800; line-height: 1.15; overflow-wrap: anywhere; }
.admin-hero-sub { font-size: .86rem; color: var(--text); overflow-wrap: anywhere; }
.admin-hero-sub .muted { color: var(--text-muted); }
.admin-hero-badges { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 4px; }
.admin-plan-chip { display: inline-block; padding: 2px 10px; border-radius: var(--radius-pill);
  font-size: .74rem; font-weight: 700; background: var(--surface); border: 1px solid var(--primary);
  color: var(--primary); }
.admin-hero-meta { display: flex; flex-wrap: wrap; gap: 10px 18px; align-items: flex-start; }
.admin-hero-stat { display: flex; flex-direction: column; gap: 1px; min-width: 78px; }
.admin-hero-stat-lbl { font-size: .68rem; text-transform: uppercase; letter-spacing: .04em; color: var(--text-muted); }
.admin-hero-stat-val { font-size: .95rem; font-weight: 700; font-variant-numeric: tabular-nums; }

.admin-detail-section { border: 1px solid var(--border); border-radius: var(--radius);
  background: var(--surface); overflow: hidden; min-width: 0; }
.admin-detail-head { padding: 9px 14px; border-block-end: 1px solid var(--border); background: var(--surface-2); }
.admin-detail-head h3 { margin: 0; font-size: .78rem; font-weight: 700; text-transform: uppercase;
  letter-spacing: .05em; color: var(--text-muted); }
.admin-detail-body { padding: 12px 14px; }
/* Wrapped legacy blocks keep their own inline h3 — style it as the same header. */
.admin-detail-plain { padding: 12px 14px; }
.admin-detail-plain > div > h3:first-child { margin: 0 0 10px; padding-block-end: 8px;
  border-block-end: 1px solid var(--border); font-size: .78rem; font-weight: 700;
  text-transform: uppercase; letter-spacing: .05em; color: var(--text-muted); }
.admin-detail-plain .admin-metrics { margin-block: 4px 0; }
.admin-detail-plain .admin-flag-row:last-child { border-block-end: 0; }

/* Feature flags */
.admin-flag-row { display: flex; align-items: center; gap: 12px; padding: 11px 4px;
  border-block-end: 1px solid var(--border); }
/* Per-business module switches (NEXT_TASKS 2026-10-04 #4): icon · name · state
   · control; on a phone the control drops to its own full-width line. */
.admin-mod-name { display: flex; flex-direction: column; min-width: 0; }
.admin-mod-row .admin-mod-ctl { flex: 0 0 auto; max-width: 260px; }
.admin-mod-row .badge { flex: 0 0 auto; }
@media (max-width: 600px) {
  .admin-mod-row { flex-wrap: wrap; }
  .admin-mod-row .admin-mod-ctl { flex: 1 1 100%; max-width: none; }
}

/* Platform banners injected on tenant manager panels */
.admin-banner { display: flex; align-items: center; gap: 10px; padding: 10px 14px;
  border-radius: var(--radius); border: 1px solid var(--primary); background: var(--surface);
  margin-bottom: 12px; font-size: .92rem; }
.admin-banner.b-warning { border-color: var(--warning, #e8590c); }
.admin-banner.b-danger { border-color: var(--danger); background: var(--danger-soft, var(--surface)); }
.admin-banner.b-maintenance { border-color: var(--danger);
  box-shadow: 0 0 0 1px var(--danger) inset; }
.admin-banner.b-impersonate { border-color: var(--warning, #e8590c); border-style: dashed; }
.admin-banner .grow { flex: 1; }
/* Pay info 2026-10-07: the fee banner carries the amount + how to pay */
.pay-banner { align-items: flex-start; }
.pay-banner .grow { display: flex; flex-direction: column; gap: 4px; min-width: 0; }
.pay-banner-wrap { padding-block-start: 16px; }
.pay-banner-wrap .admin-banner { margin-bottom: 0; }
.pay-amount { font-weight: 600; }
.pay-details { display: flex; flex-direction: column; gap: 2px; font-size: .88rem; }
.pay-iban-row { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; }
.pay-iban { font-family: ui-monospace, Menlo, Consolas, monospace; overflow-wrap: anywhere; user-select: all; }
/* Owner 2026-10-08: past the 7-day grace the manager pages lock */
.pay-grace { font-weight: 600; }
.pay-lock .pay-details { align-items: center; text-align: center; }
.pay-lock .pay-iban-row { justify-content: center; }
main.pay-locked > :not(#payLock):not(#platformBanners) { display: none !important; }
.set-sub-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); gap: 12px; }
.set-sub-grid > div { display: flex; flex-direction: column; gap: 2px; }
.set-sub-grid .badge { align-self: flex-start; }
.set-sub-body { display: flex; flex-direction: column; gap: 10px; }
.set-sub-body > h3 { margin: 10px 0 0; }
.set-sub-hist { width: 100%; border-collapse: collapse; font-size: .9rem; }
.set-sub-hist th, .set-sub-hist td { text-align: start; padding: 6px 8px; border-bottom: 1px solid var(--border); }
@media (max-width: 600px) {
  .set-sub-hist thead { display: none; }
  .set-sub-hist tr { display: flex; flex-wrap: wrap; gap: 4px 12px; padding: 8px 0; border-bottom: 1px solid var(--border); }
  .set-sub-hist td { border: 0; padding: 0; }
}

/* ============== Plan gating  =============== */
.plan-lock { display: flex; flex-direction: column; align-items: center; gap: 12px;
  text-align: center; padding: 56px 20px; max-width: 560px; margin: 8vh auto 0; }
.plan-lock-icon { font-size: 3rem; line-height: 1; }
.plan-lock h2 { margin: 0; }
.plan-lock p { margin: 0; max-width: 44ch; }
.plan-lock-actions { margin-top: 10px; justify-content: center; flex-wrap: wrap; }
.plan-row { align-items: center; gap: 10px; padding: 10px 12px;
  border: 1px solid var(--border); border-radius: var(--radius); }
.plan-row.is-current { border-color: var(--primary); background: var(--surface-2); }
.card.is-plan-locked { opacity: .72; }
.card.is-plan-locked:hover { opacity: 1; }

/* Tablet: tighten the 5-column tenant/registration grid before it cramps. */
@media (max-width: 860px) {
  .admin-row { grid-template-columns: 2fr 1fr 1fr .9fr .7fr; gap: 8px; padding-inline: 12px; }
}
@media (max-width: 640px) {
  /* Phone: every .admin-row (and its head) carries 5 cells. Drop the two
     least-critical middle columns (2 = plan / phone·city, 4 = created /
     last-active) so the remaining Business | Status | MRR·Action land in a
     clean 3-track grid that stays aligned with the header — no wrapped
     second line. Mirrors the .exp-row pattern. */
  .admin-row { grid-template-columns: minmax(0, 2fr) auto auto; }
  .admin-row > :nth-child(2), .admin-row > :nth-child(4) { display: none; }
  .admin-bar { grid-template-columns: 90px 1fr 44px; }
  .admin-kv { grid-template-columns: 1fr; }
  .admin-bill-row { grid-template-columns: 1fr 1fr; gap: 6px 10px; }
}
/* UI test 22: the page clears the sync chip; on a phone the eight section
   tabs are one scrolling row (they wrapped into four rows above every tab's
   content) and the decorative header stamp, which squeezed the title onto
   two lines, gives way — the dashboard carries the same numbers. */
.admin-page { padding-block: 24px 88px; }
@media (max-width: 640px) {
  .admin-tabs.seg-wrap { flex-wrap: nowrap; overflow-x: auto; scrollbar-width: none; max-width: 100%; }
  .admin-tabs.seg-wrap::-webkit-scrollbar { display: none; }
  .admin-tabs .seg-btn { flex: none; white-space: nowrap; }
  .admin-stamp { display: none; }
}
#adminView:focus { outline: none; }

/* ======================== Expenses  ============================ */
.exp-page { padding-block: 24px 88px; }   /* the fixed sync chip covered the last row */
.exp-row { display: grid; align-items: center; gap: 10px; padding: 11px 14px;
  grid-template-columns: minmax(0,.9fr) minmax(0,2.2fr) minmax(0,1.2fr) minmax(0,.8fr) minmax(0,.8fr); cursor: pointer;
  border-block-end: 1px solid var(--border); }
.exp-row > * { min-width: 0; }
.exp-row:hover { background: var(--surface-2); }
.exp-row:focus-visible { outline: none; box-shadow: var(--ring); }
.exp-row-head { color: var(--text-muted); font-weight: 700; font-size: .82rem;
  background: var(--surface-2); cursor: default; }
.exp-row-head > :last-child { text-align: end; }
.exp-desc strong { display: block; overflow-wrap: anywhere; }
.exp-badge { margin-inline-start: 6px; font-weight: 400; }
.exp-cat-tag { display: inline-block; max-width: 100%; padding: 2px 9px; border-radius: var(--radius-pill);
  background: var(--surface-2); border: 1px solid var(--border); font-size: .78rem;
  font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; vertical-align: middle; }
.exp-c-amt { text-align: end; font-variant-numeric: tabular-nums; }
/* E3: every cell names its column — visually hidden above the phone cut */
.exp-lbl { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
.exp-receipt-thumb { max-width: 90px; max-height: 90px; border-radius: var(--radius);
  border: 1px solid var(--border); }
.exp-receipt-thumb[hidden] { display: none; }
.exp-receipt-img { max-width: 100%; max-height: 320px; border-radius: var(--radius);
  border: 1px solid var(--border); align-self: flex-start; }
/* E15: a failed / stale read, with Retry. E14: what a filter left */
.exp-status { display: flex; align-items: center; gap: 10px; padding: 10px 14px; margin-bottom: 12px;
  border-radius: var(--radius); color: var(--danger); background: color-mix(in srgb, var(--danger) 8%, transparent); }
.exp-status.is-info { color: var(--text); background: var(--surface-2); }
.exp-status[hidden], .exp-shown[hidden] { display: none; }
.exp-status .btn { color: var(--text); }
.exp-shown { margin: 0 0 8px; }
/* E4: the report's bars fit their amounts */
.exp-bars .admin-bar { grid-template-columns: minmax(0, 180px) minmax(0, 1fr) auto; }
.exp-cat-add > .input:not([style]) { flex: 1 1 auto; min-width: 0; }

@media (max-width: 640px) {
  /* E3 ★ HIGH: a card — description · amount / category · date · method.
     The date and method were hidden and the category cut to two letters. */
  .exp-row-head { display: none; }
  .exp-row { grid-template-columns: minmax(0,1fr) auto auto; row-gap: 6px;
    grid-template-areas: "desc desc amt" "cat date method"; }
  .exp-row .exp-c-desc { grid-area: desc; }
  .exp-row .exp-c-amt { grid-area: amt; align-self: start; }
  .exp-row .exp-c-cat { grid-area: cat; }
  .exp-row .exp-c-date { grid-area: date; font-size: .82rem; }
  .exp-row .exp-c-method { grid-area: method; font-size: .82rem; text-align: end; }
  .exp-row .exp-cat-tag { white-space: normal; }
  /* E20: the toolbar took 338px before the first number — two per row */
  .exp-page .inv-toolbar > .btn { flex: 1 1 calc(50% - 5px); justify-content: center; }
  .exp-page .inv-toolbar > .grow { display: none; }
  .exp-page .inv-toolbar > #addBtn { flex-basis: 100%; }   /* the primary on its own row; the rest pair up */
  .exp-page .inv-toolbar > .select, .exp-page .inv-toolbar > #monthFilter { flex: 1 1 calc(50% - 5px); min-width: 0; max-width: none !important; }
  .exp-page .inv-toolbar > .input-search { flex: 1 1 100%; }
  /* E4 ★: the label over its bar, the amount beside the label */
  .exp-bars .admin-bar { grid-template-columns: minmax(0,1fr) auto; row-gap: 4px;
    grid-template-areas: "lbl val" "track track"; }
  .exp-bars .admin-bar-label { grid-area: lbl; white-space: normal; }
  .exp-bars .admin-bar-val { grid-area: val; }
  .exp-bars .admin-bar-track { grid-area: track; }
}

/* ===================== Analytics & Reporting  ================== */
.ana-period { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; margin-top: 12px; }
.ana-custom { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.ana-custom .input { max-width: 160px; }
.ana-period-err { color: var(--danger); }
.ana-stack { display: flex; flex-direction: column; gap: 16px; }
.ana-stack-sm { display: flex; flex-direction: column; gap: 10px; }
.ana-grid2 { display: grid; gap: 16px; grid-template-columns: 1fr 1fr; align-items: start; }
.ana-grid2 > * { min-width: 0; }   /* a wide table scrolls in its wrap, not the page (375px) */
.ana-panel { padding: 16px; }
.ana-h { margin: 0 0 10px; font-size: 1rem; }
.ana-sub { margin: 10px 0 4px; font-size: .82rem; color: var(--text-muted);
  text-transform: uppercase; letter-spacing: .04em; }

/* Chart canvas holders (fixed height → no layout shift while Chart.js draws) */
.ana-chart { position: relative; height: 220px; }
.ana-chart-tall { height: 260px; }

/* Simple key/value lists (dashboard low-stock, top products, at-risk) */
.ana-list { display: flex; flex-direction: column; }
.ana-list-row { display: flex; align-items: center; justify-content: space-between; gap: 12px;
  padding: 9px 2px; border-block-end: 1px dashed var(--border); font-size: .92rem; }
.ana-list-row:last-child { border-block-end: 0; }

.ana-badge { display: inline-block; min-width: 26px; text-align: center; padding: 2px 9px;
  border-radius: var(--radius-pill); background: var(--surface-2); border: 1px solid var(--border);
  font-size: .78rem; font-weight: 700; }
.ana-badge.is-warn { color: var(--warning, #e8590c); border-color: var(--warning, #e8590c); }
.ana-delta { font-size: .74rem; color: var(--text-muted); margin-top: 4px; }
.ana-delta.is-up { color: var(--success, #2f9e44); }
.ana-delta.is-down { color: var(--danger); }

/* Data tables (charts always ship a readable table companion — a11y) */
.ana-table-wrap { overflow-x: auto; }
.ana-table { width: 100%; border-collapse: collapse; font-size: .9rem; }
.ana-table th { text-align: start; color: var(--text-muted); font-size: .76rem;
  text-transform: uppercase; letter-spacing: .04em; padding: 8px 10px;
  border-block-end: 1px solid var(--border); white-space: nowrap; }
.ana-table td { padding: 9px 10px; border-block-end: 1px dashed var(--border); }
.ana-table tbody tr:last-child td { border-block-end: 0; }
.ana-table th.num, .ana-table td.num { text-align: end; font-variant-numeric: tabular-nums;
  white-space: nowrap; }
.ana-table td.muted { color: var(--text-muted); }
.ana-danger { color: var(--danger); }
.ana-warn { color: var(--warning, #e8590c); }
.ana-success { color: var(--success, #2f9e44); }

/* ABC classification badges (color + letter — never color alone) */
.ana-abc { display: inline-block; min-width: 24px; text-align: center; padding: 2px 8px;
  border-radius: var(--radius-pill); font-size: .78rem; font-weight: 800; }
.ana-abc-a { background: var(--success-soft, #e2f5ea); color: var(--success, #2f9e44); }
.ana-abc-b { background: var(--warning-soft, #fcf1da); color: var(--warning, #e8590c); }
.ana-abc-c { background: var(--danger-soft, #fbe3e5); color: var(--danger); }
.ana-abc-chips { display: flex; flex-wrap: wrap; gap: 8px; }
.ana-abc-chips .ana-abc { padding: 4px 12px; font-size: .84rem; }
.ana-spark { display: inline-flex; align-items: flex-end; gap: 1px; height: 22px; width: 72px; vertical-align: middle; }
.ana-spark i { flex: 1 1 0; min-width: 1px; max-width: 8px; background: var(--primary); border-radius: 1px; opacity: .85; }

/* P&L / cash-flow statement rows */
.ana-pl { display: flex; flex-direction: column; font-variant-numeric: tabular-nums; }
.ana-pl-row { display: flex; align-items: baseline; justify-content: space-between; gap: 12px;
  padding: 7px 2px; border-block-end: 1px dashed var(--border); font-size: .92rem; }
.ana-pl-row.indent { padding-inline-start: 18px; color: var(--text-muted); }
.ana-pl-row.indent strong { font-weight: 600; color: var(--text); }
.ana-pl-row.total { border-block-start: 1px solid var(--border); border-block-end: 0;
  font-weight: 700; margin-top: 2px; }
.ana-pl-row.grand { border-block-start: 2px solid var(--border-strong, var(--border));
  border-block-end: 0; margin-top: 6px; padding-block: 10px; font-size: 1.02rem; font-weight: 800; }
.ana-pl-row.grand.ana-success strong { color: var(--success, #2f9e44); }
.ana-pl-row.grand.ana-danger strong { color: var(--danger); }

.ana-controls { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; }
/* UI test 20: the page clears the sync chip (A18); on a phone the section tabs
   and the periods are one scrolling row each — three wrapped rows of pills
   each pushed the first number below the fold (A16). */
.ana-page { padding-block-end: 88px; }
@media (max-width: 640px) {
  .ana-tabs.seg-wrap, .ana-periods.seg-wrap { flex-wrap: nowrap; overflow-x: auto; scrollbar-width: none;
    border-radius: var(--radius); max-width: 100%; }
  .ana-tabs.seg-wrap::-webkit-scrollbar, .ana-periods.seg-wrap::-webkit-scrollbar { display: none; }
  .ana-tabs .seg-btn, .ana-periods .seg-btn { flex: none; white-space: nowrap; }
  .ana-period { margin-top: 8px; }
}
.ana-table-wrap:focus-visible { outline: 2px solid var(--primary); outline-offset: 2px; }

@media (max-width: 760px) {
  .ana-grid2 { grid-template-columns: 1fr; }
  .ana-chart, .ana-chart-tall { height: 200px; }
  .ana-table { font-size: .84rem; }
  .ana-table th, .ana-table td { padding: 7px 6px; }
}

/* ---------- Promo codes & campaigns  ---------- */
.pr-row { display: grid;
  grid-template-columns: minmax(0,1.6fr) minmax(0,1fr) minmax(0,.9fr) minmax(0,1.1fr) minmax(0,1fr);
  gap: 10px; align-items: center; padding: 12px 14px;
  border-block-end: 1px solid var(--border); width: 100%; }
.pr-row.pr-head { font-weight: 700; color: var(--text-muted); background: var(--surface-2);
  font-size: .85rem; }
button.pr-row { background: none; border-inline: 0; border-block-start: 0; text-align: start;
  font: inherit; color: inherit; cursor: pointer; }
button.pr-row:hover { background: var(--surface-2); }
button.pr-row:focus-visible, .pr-camp-row:focus-visible { outline: none; box-shadow: var(--ring); }
/* P13 (UI test 16): "Status" sat at its column's start while every badge sat
   at the end. */
.pr-row.pr-head > :last-child { justify-self: end; }
.pr-row.pr-head > span { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.promo-page { padding-block-end: 88px; }   /* P27: the fixed sync chip covered the last row */
/* P6 (UI test 16): a failed read says so, with Retry. */
.pr-status-line { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; margin: 0 0 12px; color: var(--danger); }
.pr-status-line:empty { display: none; }
.pr-auto-acts { display: flex; align-items: center; gap: 8px; flex: 0 0 auto; }
.pr-col { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.pr-code { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  letter-spacing: .04em; }
.pr-status { display: inline-block; padding: 3px 10px; border-radius: 999px;
  font-size: .76rem; font-weight: 700; white-space: nowrap; justify-self: end;
  background: var(--surface-2); color: var(--text-muted); }
/* P14 (UI test 16): the raw status colour on its own tint was 2.9–3.9:1 in
   the light themes (sync chip af0c96a, gift cards G25) — the words mix
   toward the text colour. */
.pr-status.s-active { background: color-mix(in srgb, var(--success) 16%, transparent);
  color: color-mix(in srgb, var(--success) 60%, var(--text)); }
.pr-status.s-scheduled { background: color-mix(in srgb, var(--info) 14%, transparent);
  color: color-mix(in srgb, var(--info) 60%, var(--text)); }
.pr-status.s-paused { background: color-mix(in srgb, var(--warning) 16%, transparent);
  color: color-mix(in srgb, var(--warning) 60%, var(--text)); }
.pr-status.s-expired, .pr-status.s-depleted { background: color-mix(in srgb, var(--danger) 12%, transparent);
  color: color-mix(in srgb, var(--danger) 60%, var(--text)); }
.pr-bar { display: block; height: 5px; border-radius: 999px; background: var(--surface-2);
  overflow: hidden; max-width: 110px; }
.pr-bar i { display: block; height: 100%; border-radius: inherit; background: var(--primary); }
.pr-bar i.is-full { background: var(--danger); }
.pr-bar-wide { max-width: 260px; margin-top: 3px; }
.pr-auto-row { display: flex; align-items: center; gap: 12px; padding: 12px 14px;
  border-block-end: 1px solid var(--border); }
.pr-auto-icon { font-size: 1.5rem; flex: 0 0 auto; }
.pr-camp-row { display: flex; align-items: center; gap: 14px; padding: 14px;
  border-block-end: 1px solid var(--border); width: 100%; background: none;
  border-inline: 0; border-block-start: 0; text-align: start; font: inherit;
  color: inherit; cursor: pointer; }
.pr-camp-row:hover { background: var(--surface-2); }
.pr-camp-stats { align-items: center; text-align: center; flex: 0 0 auto; min-width: 72px; }

/* P4 (UI test 16) ★ HIGH: ≤767 a row hid the discount, the usage and the
   validity — a promo list that never said what a code gives. A row is a
   card: code + status, discount + usage, validity. */
@media (max-width: 767px) {
  .pr-row.pr-head { display: none; }
  .pr-row { grid-template-columns: minmax(0,1fr) auto; row-gap: 6px;
    grid-template-areas: "code status" "type use" "valid valid"; }
  .pr-row > .pr-cell-code { grid-area: code; }
  .pr-row > .pr-cell-status { grid-area: status; }
  .pr-row > .pr-cell-type { grid-area: type; font-weight: 700; }
  .pr-row > .pr-cell-use { grid-area: use; justify-self: end; align-items: flex-end; }
  .pr-row > .pr-cell-use .pr-bar { width: 90px; }
  .pr-row > .pr-cell-valid { grid-area: valid; }
}
@media (max-width: 640px) {
  /* P22: an auto rule wrapped into an icon alone on a line and its archive
     button alone on another (≈280px a rule). */
  .pr-auto-row { display: grid; grid-template-columns: auto minmax(0,1fr) auto; column-gap: 10px; row-gap: 8px;
    grid-template-areas: "icon text status" "acts acts acts"; }
  .pr-auto-row > .pr-auto-icon { grid-area: icon; align-self: start; }
  .pr-auto-row > .pr-auto-text { grid-area: text; }
  .pr-auto-row > .pr-status { grid-area: status; align-self: start; }
  .pr-auto-row > .pr-auto-acts { grid-area: acts; justify-content: flex-end; }
  .pr-camp-row { flex-wrap: wrap; }
  /* P28: search and the status filter share one row. */
  .promo-page .inv-toolbar > .input-search { flex: 1 1 150px; min-width: 0; max-width: none !important; }
  .promo-page .inv-toolbar > .select { flex: 0 1 auto; min-width: 0; }
}


/* ==== All Locations (UI test 8) =========================================== */
/* A12: the fixed sync chip sits at the bottom-left — the last row scrolls clear. */
.alloc-page { padding-block: 24px 88px; }
/* A3: the head wraps — a nowrap .spread laid a 375px phone out 435px wide. */
.alloc-head { display: flex; flex-wrap: wrap; align-items: flex-start; justify-content: space-between;
  gap: 12px 16px; margin-bottom: 4px; }
.alloc-head > div:first-child { min-width: 0; }
.alloc-range { display: flex; flex-wrap: wrap; gap: 8px; }
.alloc-range .btn { white-space: nowrap; }
#allocPeriod { margin: 8px 0 0; }
.alloc-note { padding: 10px 14px; margin: 8px 0; border-inline-start: 3px solid var(--accent);
  display: flex; flex-wrap: wrap; align-items: center; gap: 8px 12px; justify-content: space-between; }
/* A11: --warn never existed — the edge was one hard-coded yellow in every theme. */
.alloc-note.is-warn { border-inline-start-color: var(--warning); }
.alloc-totals { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 140px), 1fr));
  gap: 8px; margin: 12px 0 20px; }
.alloc-total { padding: 14px 16px; min-width: 0; }
.alloc-total-v { font-size: 1.5rem; font-weight: 700; font-variant-numeric: tabular-nums; overflow-wrap: anywhere; }
.alloc-h2 { margin-top: 24px; }
.alloc-bar { height: 6px; border-radius: 3px; background: var(--accent); min-width: 2px; margin-top: 4px; }
@media (max-width: 480px) {
  .alloc-range .btn { padding-inline: 12px; }
  .alloc-page .ana-table th, .alloc-page .ana-table td { padding-inline: 6px; }
  .alloc-page .ana-table th { white-space: normal; }
}

/* "Install JelPOS" (js/sw-register.js renderInstall): empty slot = nothing.
   In the dashboard header the label folds away on a phone (icon + aria-label). */
.install-slot:empty { display: none; }
.install-slot-auth { display: flex; justify-content: center; margin: -6px 0 16px; }
.install-btn { gap: 6px; }
@media (max-width: 600px) {
  .install-slot-bar .install-label { display: none; }
  .install-slot-bar .install-btn { padding-inline: 10px; }
}

/* Notification bell panel (manager.js renderNotifPanel). NEXT_TASKS 2026-10-04
   #1: anchored to the bell it opened toward the start edge and on a phone half
   of it hung off-screen. Below 600px it is pinned to the viewport instead, just
   under the bell (--notif-top is set from the bell's position), and scrolls. */
#notifPanel {
  position: absolute; top: calc(100% + 8px); inset-inline-end: 0;
  width: 330px; max-width: 86vw; max-height: 70vh; overflow: auto;
  z-index: 60; box-shadow: 0 12px 32px rgba(0,0,0,.22); padding: 0;
}
@media (max-width: 600px) {
  #notifPanel {
    position: fixed; top: var(--notif-top, 72px); inset-inline: 12px;
    width: auto; max-width: none;
    max-height: calc(100dvh - var(--notif-top, 72px) - 12px - env(safe-area-inset-bottom, 0px));
    overscroll-behavior: contain;
  }
}
