/* Boutique POS — interface styles
   Warm charcoal and brass. Sized for a shop counter: large targets, high contrast. */

:root{
  --ink:#f4efe8; --ink-dim:#b3a89c; --ink-faint:#7d7268;
  --bg:#141210; --bg-2:#1c1917; --bg-3:#252120; --bg-4:#302b28;
  --line:#38322e; --line-soft:#2a2523;
  --brass:#c8a15c; --brass-dim:#8c6f3f; --brass-ink:#241b0c;
  --green:#6fbf8b; --red:#e2705f; --amber:#e0aa4a; --blue:#7fa8d6;
  --r:12px; --r-sm:8px; --r-lg:18px;
  --shadow:0 10px 30px rgba(0,0,0,.45);
  --font:-apple-system,BlinkMacSystemFont,"Segoe UI",Inter,Roboto,Helvetica,Arial,sans-serif;
  --mono:ui-monospace,SFMono-Regular,"SF Mono",Menlo,Consolas,monospace;
}

*,*::before,*::after{box-sizing:border-box}
/* hidden means hidden, whatever else the element's class says. A class that
   sets display beats the browser's own [hidden] rule, and three things fell
   into that one at a time: the barcode-attach banner the till had until 1.3.23
   (display:flex) stood open on every page load, the camera buttons (.btn is inline-flex)
   stood open on phones that cannot use them, and the cap pill on every basket
   line (.pill is inline-block) showed as an empty 18x6 lozenge. Each was
   fixed with a rule of its own; this is the one rule instead, so the next
   element with a display of its own cannot fall into it. Nothing in the POS
   shows an element by giving it a class or a style while it still carries
   hidden — every one is shown by taking hidden off — so !important costs
   nothing here. */
[hidden]{display:none!important}
html,body{height:100%}
body{
  margin:0;background:var(--bg);color:var(--ink);font-family:var(--font);
  font-size:15px;line-height:1.5;-webkit-font-smoothing:antialiased;
  -webkit-text-size-adjust:100%;
}
a{color:inherit;text-decoration:none}
button,input,select,textarea{font:inherit;color:inherit}
h1,h2,h3,h4{margin:0;font-weight:600;letter-spacing:-.01em}
h1{font-size:1.5rem}h2{font-size:1.15rem}h3{font-size:1rem}
small{font-size:.8em}

.shell{min-height:100%;display:flex;flex-direction:column}

/* ------------------------------------------------------------- top bar */
.topbar{
  display:flex;align-items:center;gap:18px;padding:10px 18px;
  background:var(--bg-2);border-bottom:1px solid var(--line);
  position:sticky;top:0;z-index:40;flex-wrap:wrap;
}
.brand{display:flex;align-items:center;gap:10px;font-weight:600}
.brand-mark{
  width:34px;height:34px;border-radius:10px;display:grid;place-items:center;
  background:linear-gradient(140deg,var(--brass),var(--brass-dim));
  color:var(--brass-ink);font-weight:700;font-size:17px;
}
.brand-name{max-width:190px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.nav{display:flex;gap:4px;flex:1;flex-wrap:wrap}
.nav-link{
  padding:8px 14px;border-radius:var(--r);color:var(--ink-dim);
  display:flex;align-items:center;gap:7px;font-weight:500;
  transition:background .15s,color .15s;
}
.nav-link:hover{background:var(--bg-3);color:var(--ink)}
.nav-link.is-active{background:var(--bg-4);color:var(--ink);box-shadow:inset 0 0 0 1px var(--line)}
.nav-icon{font-size:15px;opacity:.9}
.topbar-user{display:flex;align-items:center;gap:12px}
.who{display:flex;flex-direction:column;line-height:1.15;text-align:right}
.who small{color:var(--ink-faint)}

.main{flex:1;padding:20px;max-width:1500px;width:100%;margin:0 auto}
.page-pos .main{max-width:none;padding:0}

/* -------------------------------------------------------------- pieces */
.flash{
  margin:12px 18px 0;padding:11px 16px;border-radius:var(--r);font-weight:500;
  background:rgba(111,191,139,.13);color:var(--green);border:1px solid rgba(111,191,139,.3);
}
.flash-error{background:rgba(226,112,95,.13);color:var(--red);border-color:rgba(226,112,95,.32)}
.flash-warn{background:rgba(224,170,74,.13);color:var(--amber);border-color:rgba(224,170,74,.32)}

.card{background:var(--bg-2);border:1px solid var(--line-soft);border-radius:var(--r-lg);padding:18px}
.card + .card{margin-top:16px}
.card-head{display:flex;align-items:center;justify-content:space-between;gap:12px;margin-bottom:14px;flex-wrap:wrap}
.muted{color:var(--ink-dim)}
.faint{color:var(--ink-faint)}
.mono{font-family:var(--mono);font-variant-numeric:tabular-nums}
.num{font-variant-numeric:tabular-nums}
.right{text-align:right}
.center{text-align:center}
.row{display:flex;gap:12px;flex-wrap:wrap;align-items:center}
.spread{display:flex;justify-content:space-between;align-items:center;gap:12px}
.stack{display:flex;flex-direction:column;gap:14px}
.grow{flex:1}
.pagehead{display:flex;justify-content:space-between;align-items:flex-end;gap:16px;margin-bottom:18px;flex-wrap:wrap}
.pagehead p{margin:4px 0 0;color:var(--ink-dim)}

/* ------------------------------------------------------------- buttons */
.btn{
  display:inline-flex;align-items:center;justify-content:center;gap:8px;
  padding:11px 18px;border-radius:var(--r);border:1px solid var(--line);
  background:var(--bg-3);color:var(--ink);font-weight:600;cursor:pointer;
  transition:background .15s,border-color .15s,transform .05s;white-space:nowrap;
}
.btn:hover{background:var(--bg-4)}
.btn:active{transform:translateY(1px)}
.btn:disabled{opacity:.42;cursor:not-allowed}
.btn-primary{background:linear-gradient(140deg,var(--brass),var(--brass-dim));color:var(--brass-ink);border-color:transparent}
.btn-primary:hover{filter:brightness(1.07);background:linear-gradient(140deg,var(--brass),var(--brass-dim))}
.btn-danger{background:rgba(226,112,95,.14);color:var(--red);border-color:rgba(226,112,95,.35)}
.btn-danger:hover{background:rgba(226,112,95,.22)}
.btn-ghost{background:transparent}
.btn-ghost:hover{background:var(--bg-3)}
.btn-sm{padding:7px 12px;font-size:.88rem;border-radius:var(--r-sm)}
.btn-lg{padding:16px 24px;font-size:1.05rem}
.btn-block{width:100%}

/* -------------------------------------------------------------- forms */
label{display:block;font-size:.85rem;color:var(--ink-dim);margin-bottom:6px;font-weight:500}
.field{margin-bottom:14px}
input[type=text],input[type=password],input[type=number],input[type=date],input[type=email],
input[type=search],input[type=tel],select,textarea{
  width:100%;padding:11px 13px;background:var(--bg);border:1px solid var(--line);
  border-radius:var(--r-sm);outline:none;transition:border-color .15s,box-shadow .15s;
}
input:focus,select:focus,textarea:focus{border-color:var(--brass-dim);box-shadow:0 0 0 3px rgba(200,161,92,.16)}
input[type=checkbox]{width:17px;height:17px;accent-color:var(--brass);vertical-align:-3px}
textarea{min-height:80px;resize:vertical}
select{appearance:none;background-image:linear-gradient(45deg,transparent 50%,var(--ink-faint) 50%),linear-gradient(135deg,var(--ink-faint) 50%,transparent 50%);
  background-position:calc(100% - 18px) 50%,calc(100% - 13px) 50%;background-size:5px 5px,5px 5px;background-repeat:no-repeat;padding-right:36px}
.grid{display:grid;gap:14px}
.grid-2{grid-template-columns:repeat(2,minmax(0,1fr))}
.grid-3{grid-template-columns:repeat(3,minmax(0,1fr))}
/* A wide column with a narrow one beside it: the details of something on the
   left, its prices and its buttons on the right. Written as a class rather
   than in a style attribute on the form, because an inline width cannot be
   undone by the stacking rule further down and so stayed two columns on a
   phone — the right-hand card came out 116px wide at 390px. */
.grid-split{grid-template-columns:minmax(0,2fr) minmax(0,1fr);align-items:start}
.inline-label{display:flex;align-items:center;gap:8px;color:var(--ink);margin:0}

/* -------------------------------------------------------------- tables */
.table-wrap{overflow-x:auto;border-radius:var(--r);border:1px solid var(--line-soft)}
table{width:100%;border-collapse:collapse;font-size:.93rem}
th{
  text-align:left;padding:11px 14px;background:var(--bg-3);color:var(--ink-dim);
  font-weight:600;font-size:.78rem;text-transform:uppercase;letter-spacing:.05em;white-space:nowrap;
}
td{padding:11px 14px;border-top:1px solid var(--line-soft);vertical-align:middle}
tbody tr:hover{background:rgba(255,255,255,.022)}
tfoot td{border-top:2px solid var(--line);font-weight:600;background:var(--bg-3)}
.empty{padding:44px 20px;text-align:center;color:var(--ink-faint)}

.pill{
  display:inline-block;padding:3px 9px;border-radius:999px;font-size:.76rem;font-weight:600;
  background:var(--bg-4);color:var(--ink-dim);
}
.pill-ok{background:rgba(111,191,139,.16);color:var(--green)}
.pill-bad{background:rgba(226,112,95,.16);color:var(--red)}
.pill-warn{background:rgba(224,170,74,.16);color:var(--amber)}
.pill-info{background:rgba(127,168,214,.16);color:var(--blue)}

/* ---------------------------------------------------------- stat tiles */
.stats{display:grid;gap:12px;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));margin-bottom:18px}
.stat{background:var(--bg-2);border:1px solid var(--line-soft);border-radius:var(--r-lg);padding:16px 18px}
.stat-label{font-size:.78rem;text-transform:uppercase;letter-spacing:.05em;color:var(--ink-faint);font-weight:600}
.stat-value{font-size:1.7rem;font-weight:650;margin-top:6px;font-variant-numeric:tabular-nums;letter-spacing:-.02em}
.stat-sub{color:var(--ink-dim);font-size:.85rem;margin-top:2px}
.stat-brass .stat-value{color:var(--brass)}
.stat-green .stat-value{color:var(--green)}
.stat-red .stat-value{color:var(--red)}

/* ---------------------------------------------------------------- login */
.login-wrap{min-height:100vh;display:grid;place-items:center;padding:24px;
  background:radial-gradient(1100px 600px at 50% -10%,#241f1a,var(--bg))}
.login-card{width:100%;max-width:390px;background:var(--bg-2);border:1px solid var(--line);
  border-radius:22px;padding:34px;box-shadow:var(--shadow)}
.login-logo{width:56px;height:56px;border-radius:16px;display:grid;place-items:center;margin:0 auto 18px;
  background:linear-gradient(140deg,var(--brass),var(--brass-dim));color:var(--brass-ink);font-size:26px;font-weight:700}

/* ------------------------------------------------------------ till view */

/* The till is a frame the size of the screen, not a page that grows.
   It used to be neither: .pos was calc(100vh - 57px) — a guess at the top
   bar's height — and its two columns were free to grow past that, because a
   grid item's automatic minimum size is its content. So a basket of more than
   three or four lines pushed the totals and the Charge button off the bottom
   and the whole page scrolled, which is what the counter complained about on
   1 October 2026. Nothing shrank the product grid either; it simply ran on
   down the page.

   Now the height is the shell's and no number here guesses the top bar, the
   grid row is capped with minmax(0,1fr), and both columns carry min-height:0,
   which is what lets the two scrollers inside them (.products and .basket)
   actually scroll instead of spilling.

   100dvh rather than 100vh, with 100vh left above it for a browser that has
   never heard of dvh: this runs on a touchscreen, and an on-screen keyboard
   takes about half the height away. 100vh does not notice that and would put
   the Charge button underneath the keyboard. */
.page-pos .shell{height:100vh;height:100dvh;overflow:hidden}
.page-pos .topbar{flex:none}
.page-pos .main{min-height:0;overflow:hidden}

.pos{display:grid;grid-template-columns:minmax(0,1fr) 400px;grid-template-rows:minmax(0,1fr);height:100%}
.pos-left{display:flex;flex-direction:column;min-width:0;min-height:0;border-right:1px solid var(--line)}
.pos-search{padding:14px 18px;border-bottom:1px solid var(--line);display:flex;gap:10px;align-items:center}
.pos-search input{font-size:1.05rem;padding:13px 15px}
/* What the last scan did. */
.scan-note{padding:9px 18px;font-size:.88rem;color:var(--ink-dim);border-bottom:1px solid var(--line);
  background:var(--bg-3)}
.scan-note-warn{color:var(--amber);background:rgba(224,170,74,.1)}
/* The same line on the refund screen, where it sits inside a card rather than
   against the edge of the till, and the row a scan is talking about. */
#refund-scan-note{border-bottom:0;border-radius:8px;margin-top:10px;padding:9px 12px}
tr.scan-hit>td{background:rgba(200,161,92,.14)}
tr.scan-hit>td:first-child{box-shadow:inset 3px 0 0 var(--brass)}
.cats{display:flex;gap:8px;padding:12px 18px;overflow-x:auto;border-bottom:1px solid var(--line);scrollbar-width:thin}
.cat{padding:8px 15px;border-radius:999px;background:var(--bg-3);border:1px solid var(--line);
  color:var(--ink-dim);cursor:pointer;white-space:nowrap;font-weight:500;font-size:.9rem}
.cat.is-active{background:var(--brass);color:var(--brass-ink);border-color:transparent;font-weight:600}
.products{
  flex:1;min-height:0;overflow-y:auto;overscroll-behavior:contain;
  padding:16px 18px 24px;display:grid;gap:12px;
  grid-template-columns:repeat(auto-fill,minmax(155px,1fr));align-content:start;
}
.tile{
  background:var(--bg-2);border:1px solid var(--line-soft);border-radius:var(--r);
  padding:13px;cursor:pointer;text-align:left;display:flex;flex-direction:column;gap:5px;
  min-height:112px;transition:border-color .15s,background .15s,transform .06s;
}
.tile:hover{border-color:var(--brass-dim);background:var(--bg-3)}
.tile:active{transform:scale(.985)}
.tile-name{font-weight:600;line-height:1.3;font-size:.95rem}
.tile-meta{color:var(--ink-faint);font-size:.79rem}
.tile-foot{margin-top:auto;display:flex;justify-content:space-between;align-items:flex-end;gap:4px 8px;flex-wrap:wrap}
.tile-price{color:var(--brass);font-weight:700;font-variant-numeric:tabular-nums}
.tile-stock{font-size:.75rem;color:var(--ink-faint);white-space:nowrap}
.tile.low .tile-stock{color:var(--amber)}

/* The basket column, top to bottom: its heading, the lines, the totals and the
   buttons. Only the lines are allowed to grow or shrink — everything else
   keeps its own height — so whatever is in the basket, the totals and Charge
   are on the screen and the lines scroll in their own box. A row added
   between the lines and the totals (a discount on the whole basket, say)
   needs nothing here: being flex:none it simply takes its height and the
   lines give it up. overscroll-behavior keeps a swipe that reaches the end of
   the lines from being handed on to whatever is behind. */
.pos-right{display:flex;flex-direction:column;background:var(--bg-2);min-width:0;min-height:0}
.basket-head{padding:14px 18px;border-bottom:1px solid var(--line);display:flex;justify-content:space-between;align-items:center;flex:none}
.basket{flex:1 1 auto;min-height:0;overflow-y:auto;overscroll-behavior:contain;padding:8px 0}
.basket-empty{padding:56px 24px;text-align:center;color:var(--ink-faint)}
.bline{display:grid;grid-template-columns:1fr auto;gap:4px 10px;padding:11px 18px;border-bottom:1px solid var(--line-soft)}
.bline-name{font-weight:600;font-size:.94rem}
.bline-meta{color:var(--ink-faint);font-size:.8rem}
.bline-total{font-weight:650;font-variant-numeric:tabular-nums;text-align:right}
.bline-controls{grid-column:1/-1;display:flex;align-items:center;gap:8px;margin-top:5px}
.qty-btn{width:30px;height:30px;border-radius:8px;border:1px solid var(--line);background:var(--bg-3);
  cursor:pointer;display:grid;place-items:center;font-size:1.05rem;line-height:1;font-weight:600}
.qty-btn:hover{background:var(--bg-4)}
.qty-val{min-width:30px;text-align:center;font-weight:650;font-variant-numeric:tabular-nums}
.bline-rm{margin-left:auto;color:var(--red);background:none;border:none;cursor:pointer;font-size:.85rem;padding:5px}
.bline-disc-wrap{position:relative;display:inline-flex;align-items:center;flex:0 0 auto}
.bline-disc{width:56px;padding:5px 16px 5px 6px;font-size:.85rem;border-radius:7px}
.bline-disc-pct{position:absolute;right:6px;top:50%;transform:translateY(-50%);font-size:.72rem;
  color:var(--ink-faint);pointer-events:none}
.bline-disc-eur{font-size:.72rem;color:var(--ink-faint);white-space:nowrap;min-width:44px;
  text-align:right;font-variant-numeric:tabular-nums}
/* A discount past what a cashier may give away on their own. */
.bline-disc.over-cap{border-color:var(--red);box-shadow:0 0 0 2px rgba(226,112,95,.22)}

.totals{padding:14px 18px;border-top:1px solid var(--line);background:var(--bg-3);flex:none}
.trow{display:flex;justify-content:space-between;padding:3px 0;color:var(--ink-dim);font-size:.92rem}
.trow.big{color:var(--ink);font-size:1.42rem;font-weight:700;padding-top:9px;margin-top:7px;border-top:1px solid var(--line)}
.pos-actions{padding:14px 18px;display:grid;gap:9px;border-top:1px solid var(--line);flex:none}
.pay-grid{display:grid;grid-template-columns:1fr 1fr;gap:9px}

/* --------------------------------------------------------------- modal */
.modal-back{position:fixed;inset:0;background:rgba(8,6,5,.72);backdrop-filter:blur(3px);
  display:none;place-items:center;z-index:80;padding:16px}
.modal-back.open{display:grid}
/* 92vh was 92% of a viewport the on-screen keyboard does not shrink, so a
   window could stand taller than the room left above the keyboard. dvh is
   measured after the keyboard has taken its half; the vh line above it is the
   fallback for an older browser, where the old behaviour is what is left. */
.modal{background:var(--bg-2);border:1px solid var(--line);border-radius:20px;padding:24px;
  width:100%;max-width:460px;box-shadow:var(--shadow);overflow-y:auto;
  max-height:92vh;max-height:calc(100dvh - 32px)}
.modal h2{margin-bottom:16px}

/* A window whose own buttons must stay under the thumb. The head and the foot
   keep their height and only the middle scrolls, so "Amount due" and
   "Complete sale" are on the screen however short the screen is and however
   much of it a keyboard has taken. Without it the payment window was 859px of
   content at 1366x768 and the two buttons sat 99px below the bottom edge. */
.modal-fit{display:flex;flex-direction:column;padding:0;overflow:hidden}
.modal-fit > .modal-head{flex:none;padding:22px 24px 0}
.modal-fit > .modal-body{flex:1 1 auto;min-height:0;overflow-y:auto;overscroll-behavior:contain;padding:0 24px}
.modal-fit > .modal-foot{flex:none;padding:14px 24px 20px;border-top:1px solid var(--line-soft)}

.keypad{display:grid;grid-template-columns:repeat(3,1fr);gap:9px;margin:14px 0}
.keypad button{padding:17px;font-size:1.2rem;font-weight:600;border-radius:var(--r);
  border:1px solid var(--line);background:var(--bg-3);cursor:pointer}
.keypad button:hover{background:var(--bg-4)}
.amount-display{font-size:2.2rem;font-weight:700;text-align:right;padding:14px 16px;background:var(--bg);
  border-radius:var(--r);font-variant-numeric:tabular-nums;border:1px solid var(--line)}
.seg{display:flex;gap:0;border:1px solid var(--line);border-radius:var(--r);overflow:hidden}
.seg button{flex:1;padding:12px;background:var(--bg-3);border:none;cursor:pointer;font-weight:600;color:var(--ink-dim)}
.seg button.on{background:var(--brass);color:var(--brass-ink)}
/* The payment window's own spacing, as classes rather than style attributes,
   so the short-screen rules further down can take it in. */
.pay-due-row{margin-bottom:14px}
.pay-due{font-size:1.5rem}
.pay-methods{margin-bottom:14px}
.pay-quick{margin-bottom:14px}
.pay-change-row{margin-bottom:16px}
.pay-change{font-size:1.3rem;color:var(--green)}

/* ------------------------------------------------------- Z / receipts */
.doc{background:#fff;color:#15120f;border-radius:var(--r);padding:34px 38px;max-width:800px;margin:0 auto;
  box-shadow:var(--shadow);font-size:14px}
.doc h1,.doc h2,.doc h3{color:#15120f}
.doc table{color:#15120f}
.doc th{background:#f2ede6;color:#5b5148}
.doc td{border-top:1px solid #e6ded4}
.doc tfoot td{background:#f7f3ee;border-top:2px solid #cfc4b6}
.doc-head{text-align:center;border-bottom:2px solid #15120f;padding-bottom:16px;margin-bottom:18px}
.doc-head h1{font-size:1.35rem;letter-spacing:.02em}
.doc-head .co-line{font-size:.86rem;color:#5b5148;margin-top:3px}
.doc-title{text-align:center;font-size:1.05rem;font-weight:700;letter-spacing:.16em;text-transform:uppercase;margin:16px 0}
.doc-kv{display:grid;grid-template-columns:auto 1fr;gap:5px 18px;font-size:.9rem}
.doc-kv dt{color:#5b5148}
.doc-kv dd{margin:0;text-align:right;font-variant-numeric:tabular-nums}
.doc-total{display:flex;justify-content:space-between;font-size:1.2rem;font-weight:700;
  border-top:2px solid #15120f;border-bottom:2px solid #15120f;padding:11px 0;margin:16px 0}
.doc-sec{margin-top:22px}
.doc-sec h3{font-size:.8rem;text-transform:uppercase;letter-spacing:.1em;color:#5b5148;
  border-bottom:1px solid #d9cfc2;padding-bottom:5px;margin-bottom:9px}
.doc-foot{text-align:center;color:#5b5148;font-size:.82rem;margin-top:26px;border-top:1px dashed #cfc4b6;padding-top:14px}

/* 80mm thermal receipt */
.receipt{width:72mm;margin:0 auto;background:#fff;color:#000;padding:6mm 3mm;font-family:var(--mono);
  font-size:11.5px;line-height:1.42;border-radius:6px}
.receipt .r-center{text-align:center}
.receipt .r-name{font-size:14px;font-weight:700;letter-spacing:.04em}
.receipt .r-test{font-size:14px;font-weight:700;margin:2px 0 4px}
.receipt hr{border:none;border-top:1px dashed #000;margin:6px 0}
.receipt .r-row{display:flex;justify-content:space-between;gap:8px}
.receipt .r-item{margin:3px 0}
.receipt .r-item .r-desc{word-break:break-word}
.receipt .r-total{font-size:14px;font-weight:700;border-top:1px solid #000;border-bottom:1px solid #000;
  padding:4px 0;margin:5px 0;display:flex;justify-content:space-between}
.receipt table{font-size:10.5px;width:100%}
.receipt table th,.receipt table td{padding:1px 0;border:none;background:none;color:#000;
  text-transform:none;letter-spacing:0;font-size:10.5px}
.print-bar{max-width:800px;margin:0 auto 16px;display:flex;gap:10px;justify-content:flex-end;flex-wrap:wrap}

@media print{
  body{background:#fff}
  .topbar,.print-bar,.flash,.no-print{display:none!important}
  .main{padding:0;max-width:none}
  .doc{box-shadow:none;border-radius:0;max-width:none;padding:0;font-size:12px}
  /* Every .receipt is on a print-receipt page, whose own rule below sets the
     72mm width; only the padding and the corners come off here. */
  .receipt{padding:0;border-radius:0}
  @page{margin:10mm}
}
@media print{
  body.print-receipt{width:72mm}
  body.print-receipt .receipt{width:72mm}
  /* Page margin: see assets/css/print-receipt.css. @page cannot be scoped here. */
}

/* --------------------------------------------------------- responsive */

/* Short screens, which is what the shop actually has: the counter runs a
   Windows laptop driving a touchscreen, and that class of screen is commonly
   1366x768. Every pixel below comes out of whitespace and type size. Nothing
   anybody presses gets smaller: the Charge button, the quantity and discount
   boxes, the keypad and the payment buttons keep their size, and the keypad
   keys stay at roughly 48px, which is still a finger. What gives instead is
   the number of basket lines on the screen at once. */
@media (max-height:820px){
  .basket-head{padding:10px 18px}
  .totals{padding:10px 18px}
  .trow{padding:1px 0}
  .trow.big{font-size:1.25rem;padding-top:7px;margin-top:5px}
  .pos-actions{padding:10px 18px;gap:8px}
}

/* The payment window is the tallest thing in the till and needs the same
   treatment a little sooner: at full spacing it wants about 900px, so a 900px
   screen is already the edge. Everything here is whitespace and type size.
   The keypad keys come down to about 48px, which is still comfortably a
   finger — nothing else anybody presses changes at all. */
@media (max-height:900px){
  .modal h2{margin-bottom:12px}
  .modal-fit > .modal-head{padding:16px 22px 0}
  .modal-fit > .modal-body{padding:0 22px}
  .modal-fit > .modal-foot{padding:12px 22px 16px}
  .keypad{gap:7px;margin:10px 0}
  .keypad button{padding:13px;font-size:1.15rem}
  .amount-display{font-size:1.7rem;padding:9px 14px}
  .seg button{padding:11px}
  .field{margin-bottom:10px}
  .pay-due-row,.pay-methods,.pay-quick{margin-bottom:9px}
  .pay-change-row{margin-bottom:10px}
  .pay-change{font-size:1.15rem}
}

/* Half a screen left, which is what an on-screen keyboard leaves behind when
   a cashier taps the search box on a 768px laptop. Something has to give, and
   the order it gives in is: the basket lines first (they scroll), then the
   Items / Net / VAT / Discount breakdown, and never the Total or anything
   anybody presses. The breakdown is on the receipt and on the Z report; the
   Charge button is not to be hunted for. */
@media (max-height:600px){
  .trow:not(.big){display:none}
  .trow.big{padding-top:0;margin-top:0;border-top:none}
}

/* The last few pixels, for the 1366x768 laptop the counter actually uses.
   This is the step that takes the payment window from fifteen pixels of
   overflow to none, and it is all margin. */
@media (max-height:780px){
  .keypad{margin:8px 0}
  .field{margin-bottom:8px}
  .pay-change-row{margin-bottom:6px}
  .modal-fit > .modal-foot{padding:10px 22px 14px}
}

@media (max-width:1080px){
  .pos{grid-template-columns:minmax(0,1fr) 340px}
}
@media (max-width:860px){
  /* Stacked, the till is a page again and must be free to grow, so the frame
     set up at the top of the till section is handed back here. */
  .page-pos .shell{height:auto;min-height:100%;overflow:visible}
  .page-pos .main{overflow:visible}
  .topbar{gap:10px;padding:9px 12px}
  .brand-name{display:none}
  .nav-link{padding:8px 10px}
  .who{display:none}
  .main{padding:14px}
  /* Stack the till: products flow down the page, the basket follows below. */
  .pos{grid-template-columns:1fr;grid-template-rows:auto;height:auto}
  .pos-left{border-right:none;border-bottom:1px solid var(--line);display:block}
  .products{grid-template-columns:repeat(auto-fill,minmax(135px,1fr));overflow:visible;flex:none}
  .basket{overflow:visible;flex:none;min-height:70px}
  .basket-empty{padding:26px 24px}
  .grid-2,.grid-3{grid-template-columns:1fr}
  /* minmax(0,1fr) rather than a plain 1fr, because a grid column's automatic
     minimum is its own content: the New product form's twelve-row table is
     wider than a phone, and a plain 1fr let it push the whole page sideways
     — 443px of page in a 390px screen. The same trap the till's frame hit. */
  .grid-split{grid-template-columns:minmax(0,1fr)}
  .doc{padding:20px}
}
@media (max-width:560px){
  /* A table of input boxes — the New product form's twelve colour-and-size
     rows — spends a third of a phone's width on cell padding alone, and the
     column that goes short is the last one, which is the Barcode. It still
     scrolls sideways inside its own box on a screen this narrow; this simply
     means rather less of that is needed. */
  td,th{padding:8px}
  /* Icons only. .nav-label is the span the layout now wraps the word in — it
     used to be a bare text node, which no selector can reach, so this rule
     matched nothing and a phone got seven wrapped labels instead of a row of
     icons. The older selector is kept beside it for anything else in a link. */
  .nav-label,.nav-link span:not(.nav-icon){display:none}
  .nav-icon{font-size:19px}
}

/* The stock check's "Which one are you holding?" buttons hold a whole sentence
   — the colour, the piece and what the till says — and .btn does not wrap, so
   on a 390px phone the stock figure ran off the right edge of the window.
   display:block rather than .btn's own inline-flex, or the colour, the piece
   and the stock figure become three flex items that wrap one at a time and
   read as three columns instead of a sentence. */
#which-list .btn{display:block;white-space:normal;text-align:left}

/* ------------------------------------------- scanning with a phone camera */
/* The viewfinder. It fills the phone, because holding a phone at a garment tag
   and reading a 200px picture of it is not a thing anybody can do. dvh for the
   same reason the till uses it: vh is the viewport as if no on-screen keyboard
   existed, and "Done" must stay under the thumb.

   z-index 70 is deliberately **below** .modal-back's 80. On the stock check a
   code that is on four sizes opens the "Which one are you holding?" window, and
   that window has to come up over the picture rather than behind it — the brand
   prints one barcode for a whole style, so that is the common case, not the odd
   one. The camera keeps running underneath while the size is tapped. */
.camscan{
  position:fixed;inset:0;z-index:70;background:#000;
  display:flex;flex-direction:column;height:100vh;height:100dvh;
}
.camscan-head{
  flex:none;display:flex;align-items:center;justify-content:space-between;gap:12px;
  padding:12px 16px;background:var(--bg-2);border-bottom:1px solid var(--line);
}
.camscan-title{font-weight:650;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.camscan-stage{flex:1 1 auto;min-height:0;position:relative;overflow:hidden;background:#000}
.camscan-stage video{width:100%;height:100%;object-fit:cover;display:block}
/* Somewhere to aim. It is only a guide: the detector reads the whole picture,
   so a tag a little outside it still scans. */
.camscan-aim{
  position:absolute;left:8%;right:8%;top:50%;transform:translateY(-50%);height:34%;
  border:2px solid var(--brass);border-radius:14px;box-shadow:0 0 0 2000px rgba(0,0,0,.34);
  pointer-events:none;
}
/* What the last read did, in the shop's words, over the picture — a camera
   gives none of the certainty a scanner's beep does, so this and the buzz are
   the whole of the feedback. */
.camscan-note{
  flex:none;padding:14px 16px;background:var(--bg-2);border-top:1px solid var(--line);
  color:var(--ink);font-size:.95rem;min-height:56px;
}
.camscan-note-warn{color:var(--amber)}

/* ------------------------------------------------- post-sale confirmation */
/* Under the till's windows (.modal-back is 80) and the camera's viewfinder
   (70), not over them. It sits at the foot of the screen, which is exactly
   where the next payment window's Complete sale button is, and at 90 it stood
   on top of that button for the twelve seconds it was up: a tap meant for
   Complete sale landed on Print again or Open. pos.js also takes it down the
   moment a till window opens; this is for anything that opens one some other
   way. */
.toast{
  position:fixed;left:50%;bottom:26px;transform:translate(-50%,140%);
  display:flex;align-items:center;gap:14px;z-index:60;
  padding:13px 16px;border-radius:var(--r-lg);background:var(--bg-3);
  border:1px solid var(--line);box-shadow:var(--shadow);
  max-width:min(660px,calc(100vw - 32px));
  opacity:0;transition:transform .25s ease,opacity .25s ease;pointer-events:none;
}
.toast.show{transform:translate(-50%,0);opacity:1;pointer-events:auto}
.toast-mark{
  width:34px;height:34px;border-radius:50%;display:grid;place-items:center;flex:none;
  background:rgba(111,191,139,.16);color:var(--green);font-size:16px;font-weight:700;
}
.toast[data-state="failed"] .toast-mark{background:rgba(226,112,95,.16);color:var(--red)}
.toast-body{min-width:0}
.toast-head{font-weight:650}
.toast-sub{color:var(--ink-dim);font-size:.84rem;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.toast[data-state="failed"] .toast-sub{color:var(--red)}
/* The server recorded a different total from the one the till showed. Said in
   red, and the notice stays up until it is dismissed. */
.toast.toast-differs{border-color:var(--red)}
.toast-warn{color:var(--red);font-weight:600;font-size:.86rem;margin-top:4px;white-space:normal}
.toast-actions{display:flex;gap:7px;margin-left:6px;flex:none}
@media (max-width:640px){
  .toast{left:12px;right:12px;bottom:12px;transform:translateY(140%);max-width:none;flex-wrap:wrap}
  .toast.show{transform:translateY(0)}
  .toast-actions{margin-left:auto}
}
@media print{ .toast{display:none!important} }

/* One amount off the whole basket, between the lines and the totals. It keeps
   its own height so the basket lines give the room up for it, which is what
   lets the Charge button stay on screen on a short till. The padding is the
   totals' own 14px/18px, so the row lines up with everything else in this
   column instead of running to the edges. */
.basket-off{flex:none;display:grid;grid-template-columns:1fr auto;gap:6px 12px;align-items:center;
  padding:12px 18px;border-top:1px solid var(--line);background:var(--bg-3)}
.basket-off label{color:var(--ink-dim);font-size:.92rem}
/* Narrower than the base input rule's full width, and right-aligned, because it
   holds a short amount of money rather than a sentence. Written black on a pale
   box at the owner's asking: it is the one thing in this column that is typed
   into rather than read, and on a dark till that reads as a field rather than a
   figure. Chrome paints its own colours over an autofilled box, so those are
   overridden too -- the box refuses autofill anyway, and this is the belt. */
.basket-off input[type=text]{width:116px;padding:9px 11px;text-align:right;
  font-variant-numeric:tabular-nums;color:#11100f;background:#f4f1ec;
  border:1px solid var(--line);caret-color:#11100f}
.basket-off input[type=text]::placeholder{color:#6d665e;opacity:1}
.basket-off input[type=text]:-webkit-autofill,
.basket-off input[type=text]:-webkit-autofill:focus{
  -webkit-text-fill-color:#11100f;-webkit-box-shadow:0 0 0 1000px #f4f1ec inset}
.basket-off-note{grid-column:1/-1;font-size:.78rem;color:var(--red);margin:0}
