/*
 * XECO RMS styles.
 *
 * Written for the environment this actually runs in: a glass tablet held by
 * someone moving fast, in a room that is either dim or glaring, often with wet
 * or gloved hands.
 *
 *  - Dark by default. A white screen at a dim table is a lighthouse.
 *  - 44px minimum touch target, everywhere, no exceptions.
 *  - High contrast type. Menu items are read at arm's length.
 *  - No hover-only affordances: there is no cursor on a tablet.
 *  - Colour is never the sole signal -- table states and kitchen alerts carry
 *    text as well, because roughly one in twelve men cannot separate the reds
 *    from the greens.
 */

:root {
  color-scheme: dark;
  --bg: #12141a;
  --bg-raised: #1a1d26;
  --bg-sunken: #0d0f14;
  --line: #2a2f3d;
  --text: #eef1f7;
  --text-dim: #9aa3b8;

  --primary: #3d7ea6;
  --primary-ink: #ffffff;
  --accent: #e8b33a;
  --accent-ink: #1a1d26;
  --ok: #4f9d69;
  --warn: #d99a2b;
  --danger: #cf5a4e;
  --danger-ink: #ffffff;

  /* Tints of the colours above, for fills behind a state that is already
     carried by a border or by text. Spelled out rather than mixed with
     color-mix(): the oldest tills run a Chrome that predates it. Every theme
     in themes.css restates these alongside the colours they are tints of. */
  --primary-soft: rgba(61, 126, 166, 0.14);
  --primary-tint: rgba(61, 126, 166, 0.2);
  --ok-soft: rgba(79, 157, 105, 0.12);
  --accent-soft: rgba(232, 179, 58, 0.14);
  --warn-soft: rgba(217, 154, 43, 0.12);
  --danger-soft: rgba(207, 90, 78, 0.14);
  --danger-glow: rgba(207, 90, 78, 0.35);
  --danger-text: #ffc9c3;
  --scrim: rgba(0, 0, 0, 0.6);
  --shadow: 0 8px 24px rgba(0, 0, 0, 0.4);
  --grid-line: rgba(154, 163, 184, 0.10);

  --radius: 10px;
  --radius-lg: 16px;
  --touch: 44px;
  --gap: 12px;

  /* Sizes the desktop-density tiers near the end of this file turn down on a
     small or heavily scaled screen. The values here are the tablet ones. */
  --font-size: 16px;
  --btn-lg: 56px;
  --key-h: 60px;
  --page-pad: 16px;
  --ticket-w: 380px;
  --tile-min: 150px;
  --tile-h: 108px;
  --tile-pad: 10px;
  --floor-row: 60px;

  --font: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue",
    Arial, sans-serif;
  /* The wordmark and headings. The same face as the body here; a theme gives
     them a voice of their own without touching a line of running text. */
  --font-display: var(--font);
  --mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
}

* { box-sizing: border-box; }

html, body {
  margin: 0;
  padding: 0;
  height: 100%;
  background: var(--bg);
  color: var(--text);
  font-family: var(--font);
  font-size: var(--font-size);
  line-height: 1.4;
  /* Stop the pull-to-refresh gesture wiping a half-built ticket. */
  overscroll-behavior: none;
  -webkit-text-size-adjust: 100%;
}

body {
  display: flex;
  flex-direction: column;
  /* Respect notches and home indicators on tablets used as tills. */
  padding: env(safe-area-inset-top) env(safe-area-inset-right)
           env(safe-area-inset-bottom) env(safe-area-inset-left);
}

h1, h2, h3, h4 { margin: 0 0 0.5em; font-family: var(--font-display); font-weight: 650;
                 letter-spacing: -0.01em; }
p { margin: 0 0 0.75em; }
a { color: inherit; text-decoration: none; }

.muted { color: var(--text-dim); font-size: 0.875rem; min-width: 20px;}
.warn { color: var(--warn); }
.empty { color: var(--text-dim); padding: 2rem; text-align: center; }
.empty--big { font-size: 1.5rem; padding: 4rem 2rem; }
.tiny { font-size: 0.7rem; color: var(--text-dim); }

/* ---------------------------------------------------------------- topbar */

.topbar {
  display: flex;
  align-items: center;
  gap: var(--gap);
  padding: 8px 14px;
  background: var(--bg-raised);
  border-bottom: 1px solid var(--line);
  flex-shrink: 0;
}

.topbar__brand { display: flex; align-items: baseline; gap: 10px; }
.topbar__logo { font-family: var(--font-display); font-weight: 800; letter-spacing: 0.08em;
                color: var(--accent); }
.topbar__outlet { font-size: 0.85rem; color: var(--text-dim); }

.topbar__nav { display: flex; gap: 4px; margin-left: auto; }

.navlink {
  padding: 10px 16px;
  border-radius: var(--radius);
  color: var(--text-dim);
  font-weight: 600;
  min-height: var(--touch);
  display: flex;
  align-items: center;
}
.navlink--active { background: var(--bg-sunken); color: var(--text); }

.topbar__right { display: flex; align-items: center; gap: 10px; margin-left: auto; }
.topbar__user { font-size: 0.875rem; color: var(--text-dim); }

.main { flex: 1; min-height: 0; overflow: auto; }

/* --------------------------------------------------------- status pill */

.pill {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 6px 12px;
  border-radius: 999px;
  font-size: 0.8rem;
  font-weight: 600;
  background: var(--bg-sunken);
  border: 1px solid var(--line);
}
.pill__dot { width: 8px; height: 8px; border-radius: 50%; background: currentColor; }
.pill--ok { color: var(--ok); }
.pill--warn { color: var(--warn); }
.pill--bad { color: var(--danger); border-color: var(--danger); }

/* ------------------------------------------------------------- buttons */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  min-height: var(--touch);
  padding: 10px 18px;
  border: 1px solid transparent;
  border-radius: var(--radius);
  background: var(--bg-sunken);
  color: var(--text);
  font: inherit;
  font-weight: 600;
  cursor: pointer;
  /* Kills the 300ms tap delay and the grey flash on iOS. */
  touch-action: manipulation;
  -webkit-tap-highlight-color: transparent;
}
.btn:active { transform: translateY(1px); }
.btn:disabled { opacity: 0.5; cursor: not-allowed; }
.btn--primary { background: var(--primary); color: var(--primary-ink); }
.btn--accent { background: var(--accent); color: var(--accent-ink); }
.btn--danger { background: var(--danger); color: var(--danger-ink); }
.btn--ghost { background: transparent; border-color: var(--line); color: var(--text-dim); }
.btn--sm { min-height: 36px; padding: 6px 12px; font-size: 0.85rem; }
.btn--lg { min-height: var(--btn-lg); font-size: 1.05rem; }
.btn--wide { width: 100%; }

.iconbtn {
  min-width: var(--touch);
  min-height: var(--touch);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--bg-sunken);
  color: var(--text);
  font-size: 1.1rem;
  font-weight: 700;
  cursor: pointer;
  touch-action: manipulation;
}
.iconbtn--danger { color: var(--danger); border-color: var(--danger); }
/* Sits inside a line of text without competing with it -- no box, no fill --
   while still being the full 44px to hit. Quiet, not small: the rule at the
   top of this file has no exceptions, least of all for a button that changes
   what somebody is charged. */
.iconbtn--quiet { border-color: transparent; background: none; color: var(--text-dim);
                  font-size: 1rem; font-weight: 400; }

/* Chips wrap onto a new row rather than running off the right-hand edge.
   They used to scroll horizontally with the scrollbar hidden, which on a
   touch screen means a category exists but nothing on screen says so.
   Capped so a long category list cannot squeeze the tile grid to nothing --
   the grid below is `flex: 1`, so it yields whatever this takes. */
.chiprow {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  padding: 4px 0;
  max-height: 33vh;
  overflow-y: auto;
}

.chip {
  flex: 0 0 auto;
  min-height: var(--touch);
  display: inline-flex;
  align-items: center;
  padding: 8px 16px;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: var(--bg-raised);
  color: var(--text-dim);
  font-weight: 600;
  cursor: pointer;
  white-space: nowrap;
}
.chip--active { background: var(--primary); border-color: var(--primary); color: var(--primary-ink); }
/* "+ Custom item": dashed, because it opens a form rather than a category. */
.chip--custom { border-style: dashed; color: var(--primary); }

/* --------------------------------------------------------------- login */

.screen-login { justify-content: center; align-items: center; }
.login { display: grid; place-items: center; width: 100%; padding: 24px; min-height: 100vh;}
.login__card {
  width: min(420px, 100%);
  background: var(--bg-raised);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  padding: 28px;
}
.login__brand { text-align: center; letter-spacing: 0.1em; color: var(--accent); }
.login__brand span { color: var(--text-dim); margin-left: 6px; }
.login__error {
  background: var(--danger-soft);
  border: 1px solid var(--danger);
  color: var(--danger-text);
  padding: 10px 14px;
  border-radius: var(--radius);
}

.field { display: block; margin-bottom: 14px; }
.field span { display: block; font-size: 0.8rem; color: var(--text-dim); margin-bottom: 6px; }
.field input {
  width: 100%;
  min-height: var(--touch);
  padding: 12px 14px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--bg-sunken);
  color: var(--text);
  font: inherit;
  font-size: 1.25rem;
  letter-spacing: 0.15em;
}

/* Words rather than figures: the wide tracking above is there to make an
   amount readable digit by digit, and it makes a name look typed by a
   ransom note. */
/* A whole-number stepper inside a form sheet: - 1 +. */
.qtystep { display: flex; align-items: center; gap: 12px; }
.qtystep strong { min-width: 2.5ch; text-align: center; font-size: 1.2rem; font-variant-numeric: tabular-nums; }
.qtystep .btn { min-width: var(--touch); }
.field--text input, .field--text textarea {
  width: 100%;
  min-height: var(--touch);
  padding: 12px 14px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--bg-sunken);
  color: var(--text);
  font: inherit;
  font-size: 1rem;
  letter-spacing: normal;
  resize: vertical;
}

/* One line of plain English above an optional form, so nobody hunts for the
   required field that is not there. */
.sheet__hint {
  margin: 0 0 14px; font-size: 0.85rem; color: var(--text-dim);
}
/* Under the phone box on the customer sheet: pulled up to sit with the box it
   describes rather than floating between it and the next one. */
.sheet__match { margin-top: -8px; color: var(--ok); }

.keypad { display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px; margin-top: 16px; }
.key {
  min-height: var(--key-h);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--bg-sunken);
  color: var(--text);
  font-size: 1.5rem;
  font-weight: 600;
  cursor: pointer;
  touch-action: manipulation;
}
.key--muted { color: var(--text-dim); }
.key--go { background: var(--primary); color: var(--primary-ink); }

/* --------------------------------------------------------------- floor */

.floor { padding: var(--page-pad); }
.floor__bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--gap);
  margin-bottom: 16px;
  flex-wrap: wrap;
}
.floor__stat { color: var(--text-dim); }
.floor__stat strong { color: var(--text); font-size: 1.2rem; }
.floor__actions { display: flex; gap: 8px; }

.area { margin-bottom: 28px; }
.area__name { color: var(--text-dim); font-size: 0.85rem; text-transform: uppercase;
              letter-spacing: 0.08em; }

/* Tables are placed on a grid from their stored pos_x/pos_y, so the on-screen
   layout matches the room.

   The column count is the *room's* (--floor-cols), never the viewport's. It
   used to be auto-fill, which meant the number of columns came from however
   wide the browser happened to be -- so a table dragged into the far corner of
   the till's plan wrapped onto the next line on a handset, and one set of
   coordinates drew two different rooms. Columns hold at 76px and the plan
   scrolls sideways instead, which is the one behaviour that keeps the shape of
   the room the same on every screen. */
.area__plan { overflow-x: auto; overscroll-behavior-x: contain; }
.area__grid {
  display: grid;
  grid-template-columns: repeat(var(--floor-cols, 12), minmax(76px, 1fr));
  grid-template-rows: repeat(var(--floor-rows, 8), var(--floor-row));
  grid-auto-rows: var(--floor-row);
  gap: 8px;
}

/* A floor tile. "table" is also the class on every data <table> in the app,
   so the tile rules are kept off real tables: a <table> made display:flex
   stops being a table at all -- its header row and its body lay out as two
   unrelated boxes, the headings drift off their columns and the figures
   spill out of the panel. :where() adds no specificity, so the state and
   shape rules below still override this one exactly as they always did. */
.table:where(:not(table)) {
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  gap: 2px;
  border: 2px solid var(--line);
  border-radius: var(--radius);
  background: var(--bg-raised);
  padding: 6px;
  text-align: center;
  min-height: var(--touch);
}
.table__code { font-size: 1.15rem; font-weight: 700; }
.table__meta { font-size: 0.72rem; color: var(--text-dim); }
.table__server { font-size: 0.68rem; color: var(--text-dim); }

/* Status is carried by border colour *and* by the text inside, never colour
   alone. */
/* Shape is what the room looks like from the door, and it is the fastest way
   to find the table you were sent to. Carried by the tile, not by an icon. */
.table--circle { border-radius: 50%; }
.table--bar { border-radius: calc(var(--radius) / 2); border-style: dashed; }

.table--free { border-color: var(--line); opacity: 0.75; }
.table--seated { border-color: var(--primary); background: var(--primary-soft); }
.table--occupied { border-color: var(--ok); background: var(--ok-soft); }
.table--bill_printed { border-color: var(--accent); background: var(--accent-soft); }
.table--part_paid { border-color: var(--warn); background: var(--warn-soft); }

.ticketlist { display: grid; grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); gap: 8px; }
.ticketcard {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: 12px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--bg-raised);
}
.ticketcard__number { font-weight: 700; }
.ticketcard__type { font-size: 0.75rem; color: var(--text-dim); }
.ticketcard__total { font-weight: 600; color: var(--accent); }

/* --------------------------------------------------------------- order */

.screen-order .main { overflow: hidden; }

.order {
  display: grid;
  grid-template-columns: 1fr var(--ticket-w);
  height: 100%;
  min-height: 0;
}

.order__menu { display: flex; flex-direction: column; min-height: 0; padding: 12px; gap: 10px; }
.order__search input {
  width: 100%;
  min-height: var(--touch);
  padding: 10px 14px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--bg-sunken);
  color: var(--text);
  font: inherit;
}

.tilegrid {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(var(--tile-min), 1fr));
  /* Tall enough for the worst tile a menu throws at it: two lines of name,
     a SKU and a price. Names longer than that are clamped, not spilled. */
  grid-auto-rows: var(--tile-h);
  gap: 10px;
  padding-bottom: 24px;
  -webkit-overflow-scrolling: touch;
}

.tile {
  --tile-accent: var(--line);
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 2px;
  padding: var(--tile-pad);
  border: 1px solid var(--line);
  border-left: 4px solid var(--tile-accent);
  border-radius: var(--radius);
  background: var(--bg-raised);
  color: var(--text);
  font: inherit;
  text-align: left;
  cursor: pointer;
  touch-action: manipulation;
  /* Rows are a fixed height, so anything longer than the tile is cut off
     inside its own borders rather than run over the one below it. */
  overflow: hidden;
}
.tile__name {
  font-weight: 600;
  font-size: 0.95rem;
  line-height: 1.2;
  /* Two lines, then an ellipsis. A four-word item name used to run the price
     off the bottom of the tile; the full name is on the tile's title. */
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  overflow: hidden;
  /* A single unbroken word (a long SKU-ish name) breaks rather than widening
     the tile past its grid track. */
  overflow-wrap: anywhere;
  width: 100%;
  flex: 0 0 auto;
}
.tile__sku {
  font-size: 0.66rem;
  color: var(--text-dim);
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.04em;
  /* The name above wraps, the code below must not: a broken SKU is not
     a code any more, and there is no room across the tile for it. */
  width: 100%;
  flex: 0 0 auto;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.tile__price {
  /* Pinned to the bottom while the name and SKU stack from the top: the
     tile is a fixed height, and the price is the line that must never be the
     one that falls off it. */
  margin-top: auto;
  flex: 0 0 auto;
  font-variant-numeric: tabular-nums;
  color: var(--accent);
  font-weight: 700;
}
.tile__flag {
  font-size: 0.68rem;
  color: var(--danger);
  /* The reason is the point of an unavailable tile, so it gets its own line
     and the name gives up one to pay for it. */
  width: 100%;
  flex: 0 0 auto;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.tile--unavailable { opacity: 0.45; }
.tile--unavailable .tile__name { -webkit-line-clamp: 1; line-clamp: 1; }

.order__ticket {
  display: flex;
  flex-direction: column;
  min-height: 0;
  border-left: 1px solid var(--line);
  background: var(--bg-raised);
}
.ticket__head { display: flex; align-items: center; gap: 10px; padding: 10px 12px;
                border-bottom: 1px solid var(--line); }
.ticket__head h2 { margin: 0; font-size: 1rem; }

/* Who the ticket is credited to. Quiet by design: it has to be readable at a
   glance from arm's length but must never compete with the ticket itself. */
.ticketstaff {
  display: flex; align-items: center; justify-content: space-between; gap: 8px;
  padding: 6px 12px; border-bottom: 1px solid var(--line);
  font-size: 0.78rem; color: var(--text-dim);
}
.ticketstaff__pair { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* The rider dropdown: a till picks from it a dozen times an hour, so it is
   the height of a tap target, but it stays in the row's quiet type. */
.ticketstaff__select {
  min-width: 9rem; max-width: 60%; min-height: 32px; padding: 2px 6px;
  border: 1px solid var(--line); border-radius: 6px;
  background: var(--bg-sunken); color: var(--text); font: inherit;
}
.linkbtn {
  border: 0; background: none; padding: 4px 2px; cursor: pointer;
  font: inherit; color: var(--accent); text-decoration: underline;
  /* Comfortably tappable without taking the height of a real button: this
     row sits between the ticket number and the first item. */
  min-height: 28px;
}

.ticket__lines { flex: 1; min-height: 0; overflow-y: auto; padding: 8px; }

.cartline {
  display: grid;
  /* The count column widens for its box while that is open, and only then. */
  grid-template-columns: minmax(32px, auto) 1fr auto;
  grid-template-areas: "qty body total" "act act act";
  gap: 6px 8px;
  padding: 8px;
  border-bottom: 1px solid var(--line);
}
.cartline__qty { grid-area: qty; font-weight: 700; font-variant-numeric: tabular-nums; }
/* The count is a button that looks like the count: tapping it opens the box
   for typing a quantity, so twelve naan is "12" and not twelve taps on +. */
.cartline__qtybtn {
  border: 0; background: none; padding: 2px 0; cursor: pointer; min-height: 28px; min-width: 32px;
  font: inherit; color: inherit; text-align: left;
  text-decoration: underline dotted; text-underline-offset: 3px;
}
.cartline__qtybox {
  width: 56px;
  min-height: var(--touch);
  padding: 6px;
  border: 1px solid var(--primary);
  border-radius: var(--radius);
  background: var(--bg-sunken);
  color: var(--text);
  font: inherit;
  font-variant-numeric: tabular-nums;
  text-align: center;
}
.cartline__qtybox:focus { outline: 2px solid var(--primary); outline-offset: 1px; }
.cartline__body { grid-area: body; min-width: 0; }
.cartline__name { font-weight: 600; }
.cartline__mods, .cartline__note { font-size: 0.78rem; color: var(--text-dim); }
.cartline__note { font-style: italic; }
.cartline__total { grid-area: total; font-variant-numeric: tabular-nums; font-weight: 600;
                   display: flex; flex-direction: row-reverse; align-items: center; justify-content: flex-end; gap: 2px; }
/* The price box, opened by the pencil in front of the figure. Full height like
   everything else here -- it is typed into on the same glass tablet, with the
   same wet thumb. */
.cartline__price {
  width: 92px;
  min-height: var(--touch);
  padding: 6px 8px;
  border: 1px solid var(--primary);
  border-radius: var(--radius);
  background: var(--bg-sunken);
  color: var(--text);
  font: inherit;
  font-variant-numeric: tabular-nums;
  text-align: right;
}
.cartline__price:focus { outline: 2px solid var(--primary); outline-offset: 1px; }
/* The pin beside the box: faded while the figure is for this line only, lit
   and ringed once it will stand for the whole hall. An emoji ignores `color`,
   so the state is carried by opacity and the ring rather than by tint. */
.cartline__keep { opacity: 0.4; filter: grayscale(1); }
.cartline__keep[aria-pressed="true"] { opacity: 1; filter: none; border-color: var(--primary);
                                       background: var(--bg-sunken); }
.cartline__actions { grid-area: act; display: flex; gap: 6px; justify-content: flex-end;
                     align-items: center; }
/* A sent line is visually distinct: changing it is a kitchen event, not an edit. */
.cartline--sent { border-left: 3px solid var(--ok); padding-left: 5px; }
/* Handed over, and nothing left to do about it. Stated quietly rather than
   with a button, so the only thing that looks pressable on a sent line is the
   one action still open. */
.cartline--served .cartline__name { color: var(--text-dim); }
.cartline__done { font-size: 0.72rem; text-transform: uppercase; letter-spacing: 0.04em;
                  color: var(--ok); font-weight: 700; }

.ticket__totals { padding: 10px 14px; border-top: 1px solid var(--line); }
.totals__row { display: flex; justify-content: space-between; padding: 3px 0;
               font-variant-numeric: tabular-nums; color: var(--text-dim); }
.totals__row--grand { color: var(--text); font-size: 1.3rem; font-weight: 700;
                      border-top: 1px solid var(--line); margin-top: 6px; padding-top: 8px; }
.totals__row--paid { color: var(--ok); }
.totals__row--due { color: var(--accent); font-weight: 700; }
.totals__row--fee { color: var(--warn); }
.totals__row--fee-pending { font-style: italic; opacity: 0.85; }
.totals__row--change { color: var(--accent); font-weight: 700; font-size: 1.15rem;
                       border-top: 1px dashed var(--line); margin-top: 6px; padding-top: 8px; }

/* ------------------------------------------------------------- settling */

.paysum { margin-bottom: 14px; }
.paysum__row { display: flex; justify-content: space-between; align-items: baseline;
               padding: 4px 0; color: var(--text-dim);
               font-variant-numeric: tabular-nums; }
.paysum__row--due { color: var(--text); font-size: 1.7rem; font-weight: 700;
                    border-top: 1px solid var(--line); padding-top: 10px; }
/* The method's own charge, and what it makes the customer hand over. Set
   apart from the amount due above it: they are different questions, and a
   cashier reading one figure for the other short-changes the till. */
.paysum__row--fee { color: var(--warn); }
.paysum__row--charged { color: var(--text); font-size: 1.25rem; font-weight: 700; }

.payfield { display: block; margin: 12px 0; }
.payfield span { display: block; font-size: 0.75rem; color: var(--text-dim);
                 text-transform: uppercase; letter-spacing: 0.05em; margin-bottom: 4px; }
/* Money is typed on a tablet in a hurry, so the box is big and the digits
   line up under each other. */
.payfield .input { width: 100%; font-size: 1.35rem; min-height: 52px;
                   font-variant-numeric: tabular-nums; }

.chiprow--quick { margin: -4px 0 4px; }
.chiprow--quick .chip { font-variant-numeric: tabular-nums; }

.paychange { margin: 10px 0 0; min-height: 1.4em; font-weight: 700;
             font-variant-numeric: tabular-nums; }
.paychange--change { color: var(--accent); font-size: 1.25rem; }
.paychange--part { color: var(--warn); font-weight: 600; }
.paychange--fee { color: var(--text-dim); font-weight: 600; }

@media (max-width: 520px) {
  .modal__panel--pay .modal__foot { flex-direction: column-reverse; }
  .modal__panel--pay .modal__foot .btn { width: 100%; }
}

.ticket__actions { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; padding: 10px; }
/* Secondary full-width row under the two primary buttons. */
.ticket__actions .btn--wide { grid-column: 1 / -1; }

/* --------------------------------------------------------------- modal */

.modal { position: fixed; inset: 0; display: none; place-items: center;
         background: var(--scrim); z-index: 50; padding: 16px; }
.modal--open { display: grid; }
.modal__panel {
  width: min(560px, 100%);
  max-height: 88vh;
  display: flex;
  flex-direction: column;
  background: var(--bg-raised);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  overflow: hidden;
}
.modal__head { display: flex; align-items: center; justify-content: space-between;
               padding: 14px 16px; border-bottom: 1px solid var(--line); }
.modal__head h3 { margin: 0; }
.modal__body { flex: 1; overflow-y: auto; padding: 14px 16px; }
.modal__foot { display: flex; gap: 10px; justify-content: flex-end;
               padding: 12px 16px; border-top: 1px solid var(--line); }

.modgroup { margin-bottom: 18px; }
.modgroup h4 { font-size: 0.85rem; color: var(--text-dim); text-transform: uppercase;
               letter-spacing: 0.06em; }
.req { color: var(--danger); font-size: 0.72rem; }
.modgroup__opts { display: grid; grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); gap: 8px; }

.opt {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 8px;
  min-height: var(--touch);
  padding: 10px 12px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--bg-sunken);
  color: var(--text);
  font: inherit;
  cursor: pointer;
  text-align: left;
}
.opt em { font-style: normal; color: var(--accent); font-size: 0.8rem; }
.opt--on { border-color: var(--primary); background: var(--primary-tint); }

/* ----------------------------------------------------------------- kds */

.kds { padding: 12px; }
.kds__bar { display: flex; align-items: center; justify-content: space-between;
            gap: var(--gap); margin-bottom: 12px; }
.kds__grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); gap: 12px; }

.kticket {
  border: 1px solid var(--line);
  border-top: 4px solid var(--primary);
  border-radius: var(--radius);
  background: var(--bg-raised);
  overflow: hidden;
}
/* Late tickets pulse: a kitchen screen is watched from across the room and
   peripheral vision detects motion long before it detects colour. */
.kticket--late { border-top-color: var(--danger); animation: nudge 2s ease-in-out infinite; }
@keyframes nudge {
  0%, 100% { box-shadow: 0 0 0 0 transparent; }
  50% { box-shadow: 0 0 0 4px var(--danger-glow); }
}
@media (prefers-reduced-motion: reduce) {
  .kticket--late { animation: none; border-top-width: 8px; }
}

.kticket__head { display: flex; align-items: center; gap: 8px; padding: 10px 12px;
                 border-bottom: 1px solid var(--line); }
.kticket__number { font-weight: 800; font-size: 1.1rem; }
.kticket__where { color: var(--text-dim); font-size: 0.85rem; }
.kticket__age { margin-left: auto; font-variant-numeric: tabular-nums; font-weight: 700; }

.kticket__lines { list-style: none; margin: 0; padding: 0; }
/* Clearing the whole card is the tap a cook makes with a pan in the other
   hand, so it is a bar across the bottom rather than a button to be aimed at,
   and it is the widest target on the card by some distance. */
.kticket__bumpall {
  display: flex;
  border-radius: 0;
  border-top: 1px solid var(--line);
  padding: 12px;
  font-size: 0.95rem;
}
.kticket__reprint {
  display: block;
  width: 100%;
  padding: 6px;
  border: 0;
  border-top: 1px solid var(--line);
  background: transparent;
  color: var(--text-dim);
  font: inherit;
  font-size: 0.78rem;
  cursor: pointer;
}
.kticket__reprint:hover { color: var(--text); background: var(--bg); }
/* display:block above beats the UA's [hidden] rule, so say it again -- a
   terminal with no printer should not be offering to reprint to it. */
.kticket__reprint[hidden] { display: none; }
.kline { display: flex; align-items: flex-start; gap: 10px; padding: 10px 12px;
         border-bottom: 1px solid var(--line); }
.kline:last-child { border-bottom: 0; }
.kline__qty { font-weight: 800; font-size: 1.15rem; min-width: 28px; }
.kline__name { flex: 1; font-size: 1.05rem; }
.kline__mod, .kline__note { display: block; font-style: normal; font-size: 0.82rem;
                            color: var(--accent); }
.kline__note { color: var(--warn); }
.kline--ready { opacity: 0.6; }

/* --------------------------------------------------- kitchen printing */

.kds__tools { display: flex; align-items: center; gap: 8px; }

/* Arming the alarm and silencing it are both things somebody does at arm's
   length with flour on their hands, so they are full-width bars rather than
   buttons to be aimed at. */
.kds__arm, .kds__ack {
  display: block;
  width: 100%;
  font: inherit;
  text-align: center;
  cursor: pointer;
  margin-bottom: 12px;
  border-radius: var(--radius);
}
/* These are <button>s, so [hidden] loses to the display above unless it is
   said again here -- and a "tap to stop the alarm" bar that will not go away
   is worse than no bar at all. */
.kds__arm[hidden], .kds__ack[hidden], .kds__setup[hidden] { display: none; }

.kds__arm {
  padding: 12px 14px;
  border: 1px solid var(--warn);
  background: transparent;
  color: var(--warn);
  font-weight: 600;
}
.kds__ack {
  padding: 16px;
  border: 0;
  background: var(--danger);
  color: var(--danger-ink);
  font-size: 1.3rem;
  font-weight: 800;
  letter-spacing: 0.04em;
  animation: nudge 1s ease-in-out infinite;
}
@media (prefers-reduced-motion: reduce) { .kds__ack { animation: none; } }

.kds__setup {
  display: grid;
  gap: 8px;
  justify-items: start;
  padding: 14px 16px;
  margin-bottom: 12px;
  background: var(--bg-raised);
  border: 1px solid var(--line);
  border-radius: var(--radius);
}
.kds__setup label { display: flex; align-items: center; gap: 8px; cursor: pointer; }
.kds__setup-title { margin: 0; font-weight: 700; }
.kds__setup-note { margin: 4px 0 0; font-size: 0.82rem; max-width: 52ch; }
.kds__setup code { font-family: var(--mono); }

/* ------------------------------------------------------------- panels */

.panel { max-width: 720px; margin: 24px auto; padding: 24px; background: var(--bg-raised);
         border: 1px solid var(--line); border-radius: var(--radius-lg); }
.panel--wide { max-width: 1100px; }
.panel--center { text-align: center; }
.panel__head { display: flex; justify-content: space-between; align-items: flex-start;
               gap: var(--gap); flex-wrap: wrap; margin-bottom: 18px; }
.panel__nav { display: flex; gap: 6px; flex-wrap: wrap; }
.stack { display: grid; gap: 10px; }

.statrow { display: grid; grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
           gap: 10px; margin-bottom: 22px; }
.stat { background: var(--bg-sunken); border: 1px solid var(--line); border-radius: var(--radius);
        padding: 12px; }
.stat span { display: block; font-size: 0.75rem; color: var(--text-dim); }
.stat strong { font-size: 1.5rem; font-variant-numeric: tabular-nums; }

.grid2 { display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 22px; }
/* Columns of wide figure tables (the staff report's six): each needs about
   420px before its numbers crowd, so three share a row only on a screen with
   room for three, and a phone gets one. min() keeps a narrow panel from being
   forced wider than itself. */
.grid2--tables { grid-template-columns: repeat(auto-fit, minmax(min(100%, 420px), 1fr)); }
.grid2 > * { min-width: 0; }
table.table { width: 100%; border-collapse: collapse; }
table.table td { vertical-align: middle; }
/* Figures never break across a line -- "172123.08" split in two is two
   numbers -- and a name gives up its room first instead. */
table.table td.num, table.table th.num { white-space: nowrap; }
/* A data table in a column narrower than itself scrolls inside its own box
   rather than running over the next column or out of the panel. */
.table-scroll { overflow-x: auto; overscroll-behavior-x: contain; max-width: 100%; }
table.table td { padding: 7px 4px; border-bottom: 1px solid var(--line);
                 font-variant-numeric: tabular-nums; }
table.table td:last-child { text-align: right; }
table.table tr.grand td { font-weight: 700; font-size: 1.1rem; border-bottom: 0; }

/* ------------------------------------------------------------- receipt */

.receipt-page { padding: 20px; display: grid; justify-items: center; gap: 16px; }
.receipt-page__actions { display: flex; gap: 10px; }

.receipt {
  /* The full 80mm roll, with the 4mm of hardware margin the TM-T81 cannot
     reach held *inside* the box. Border-box is the whole trick: as a
     content-box this was 72mm of text plus 8mm of padding = 80mm of ink on a
     roll that only takes 72mm, so every line started 4mm too far left and the
     right-hand end of the widest ones was printed off the paper. What is on
     screen is now the same 72mm band the printer actually inks. */
  box-sizing: border-box;
  width: 80mm;
  background: #fff;
  color: #000;
  padding: 4mm;
  font-family: var(--mono);
  font-size: 11px;
  line-height: 1.35;
}
.receipt * { box-sizing: border-box; }
.receipt h1 { font-size: 14px; text-align: center; margin: 6px 0; letter-spacing: 0.1em; }
.receipt pre { font-family: inherit; white-space: pre-wrap; margin: 0; text-align: center; }
/* The outlet's name, the first line of receipt_header (see receipt/receipt.html).
   Left inline rather than display:block: the newline after </strong> inside the
   <pre> is already the line break, and a block box here would print a second. */
.receipt__store { font-weight: 700; font-size: 13px; letter-spacing: 0.06em; }
.receipt__meta { margin: 8px 0; border-top: 1px dashed #000; border-bottom: 1px dashed #000;
                 padding: 6px 0; }
.receipt__meta div { display: flex; justify-content: space-between; }
/* The delivery customer (see receipt/receipt.html). The address is free text
   typed at the counter, often two or three lines, so it gets the full width
   under its label instead of being squeezed into the right-hand column. */
.receipt__customer { margin: 0 0 8px; padding-bottom: 6px; border-bottom: 1px dashed #000; }
.receipt__customer div { display: flex; justify-content: space-between; gap: 3mm; }
.receipt__customer span:last-child { text-align: right; overflow-wrap: anywhere; font-weight: 700; }
.receipt__customer .receipt__address { display: block; }
.receipt__customer .receipt__address span:last-child { display: block; text-align: left; white-space: pre-line; }
/* Fixed layout with a wrapping name column: otherwise a long item name
   widens the table and takes the amounts off the right of the paper. */
.receipt__lines { width: 100%; table-layout: fixed; border-collapse: collapse; margin: 6px 0; }
.receipt__lines .name { overflow-wrap: break-word; word-wrap: break-word; }
.receipt__lines td { vertical-align: top; padding: 2px 0; }
.receipt__lines .qty { width: 8mm; padding-right: 1mm; }
.receipt__lines .amt { width: 20mm; text-align: right; white-space: nowrap; }
.receipt__lines .mod { padding-left: 8px; font-size: 10px; }
/* Collapsed, and that is not cosmetic: with separate borders the default
   border-spacing puts a 2px gap in the middle of the rule above TOTAL, and
   a thermal head renders that as a line that visibly stops halfway. */
.receipt__totals { width: 100%; border-collapse: collapse; border-top: 1px dashed #000;
                   margin-top: 6px; padding-top: 4px; }
.receipt__totals td { padding: 2px 0; }
.receipt__totals td:last-child { text-align: right; white-space: nowrap; }
.receipt__totals tr.grand td { font-weight: 700; font-size: 13px; border-top: 1px solid #000; }
/* What is already inside the figures above -- the discount given, anything
   comped, the tax. Set smaller and without the solid rule so it cannot be read
   as another column to add up. */
.receipt__totals--note { border-top: none; margin-top: 2px; font-size: 10px; }
.receipt__totals--note td { color: #000; }
.receipt__footer { margin-top: 10px; text-align: center; border-top: 1px dashed #000; padding-top: 6px; }
.receipt__footer .tiny { color: #555; }

/* Who built the till. On every printed document except the kitchen slips --
   a cook has ten seconds to read a ticket and does not need a phone number in
   them. #000 rather than a grey: a thermal head dithers grey into something
   unreadable at this size. */
.print-only { display: none; }
.brand-footer { margin-top: 8px; padding-top: 6px; border-top: 1px dashed #000;
                text-align: center; color: #000; line-height: 1.4; }
/* The "XECO-RMS" wordmark, ASCII art in figlet's "Terrace" (see
   _brand_footer.html). The art is a fixed 91 columns, so its size is arithmetic
   rather than a guess: --logo-width of drawn width, over 91 columns, over a
   monospace advance of 0.62em -- the widest any of the fallbacks below runs.
   Hard-coding a px size instead is what made this illegible the first time; it
   has to be derived from the width it is drawn into.
   42mm is a deliberate step back from the 66mm that fitted the band edge to
   edge (66 -> 53 -> 42, two 20% cuts): a till logo wants to sit inside the
   receipt, not span it. Nothing but taste sets this -- turn --logo-width and
   the arithmetic re-fits the 91 columns on its own.
   line-height:1 is load-bearing: the block glyphs fill their em box, and any
   leading at all opens white seams straight through the letters. */
pre.brand-footer__logo { --logo-width: 21mm;
                         display: block; margin: 0 auto 4px; width: max-content;
                         font-family: "DejaVu Sans Mono", "Liberation Mono", var(--mono), monospace;
                         font-size: calc(var(--logo-width) / 91 / 0.62); line-height: 1;
                         letter-spacing: 0; white-space: pre; text-align: left; }
.brand-footer span { display: block; }

@media print {
  /* Print the receipt alone: no chrome, no buttons, no page background. */
  body { background: #fff; padding: 0; }
  .topbar, .no-print, .toast-host, .panel__nav { display: none !important; }
  .print-only { display: block !important; }
  /* No grid and no centring: on a roll the page *is* the receipt, and a grid
     container here is one more box to get the width of wrong. */
  .receipt-page { display: block; padding: 0; }
  /* On screen .receipt is the whole 80mm roll with 4mm of padding standing in
     for the margin, because that is what a customer's paper looks like. On
     paper that box is wrong, and it is the bug that keeps coming back.

     Two failures, and each fix caused the other. width:auto/padding:0 put the
     text hard against the left edge. The fix for that was the 80mm box with
     4mm of padding, but padding sits *inside* a border-box: the box still
     spans all 80mm while the head can only reach 72mm, so the right-hand end
     of the longest lines fell off the paper.

     Both go away by printing the band the head can actually ink and taking the
     gutter out of it, not out of paper that is not there: a 72mm box with 2mm
     of padding of its own. Nothing can clip, because the box is the band.

     Where the band *sits* is the other half, and it is the half that does not
     travel between printers. `@page margin: 0` says the page starts at the
     paper's edge, but a Windows thermal driver hands the browser its printable
     area, not the roll: CSS x=0 is already the head's first dot. The 4mm inset
     added on top of that is the dead strip counted twice -- the band lands at
     8..80mm against a head that stops at 76mm, and the right-hand end of every
     line is pushed off the paper. That is measurable on a photo of the bill:
     an 8mm left gutter where this rule asks for 6mm, and ink running past the
     right edge of the roll.

     So the inset is 0 by default and the band alone does the work: 0..72mm in
     page terms, which is the strip the head inks, with 2mm of gutter taken out
     of it. A driver that really does give the whole roll prints this 2mm in
     from the dead strip instead of over it, which is a gutter, not a clip.

     Two numbers to turn if a printer disagrees, and they are the same two the
     kitchen slip carries. --roll-band is the printable width, not the paper
     width: 80mm roll = 72mm band, 58mm roll = 48mm. --roll-inset is how far
     the band has to be pushed in *on top of* what the driver already allows,
     which is 0 wherever the page is the printable area. Clipped on the left
     means raise the inset; clipped on the right means lower the band, and
     check the inset is not paying for the margin twice. */
  .receipt { --roll-band: 72mm; --roll-inset: 0mm;
             width: var(--roll-band); padding: 0 2mm;
             margin: 0 0 0 var(--roll-inset); }
  .panel { border: 0; margin: 0; background: #fff; color: #000; }
  table.table td { border-color: #ccc; }
  .brand-footer { color: #000; }
}

/* -------------------------------------------------------------- toasts */

.toast-host { position: fixed; bottom: 16px; left: 50%; transform: translateX(-50%);
              display: grid; gap: 8px; z-index: 100; pointer-events: none; }
.toast {
  padding: 12px 20px;
  border-radius: 999px;
  background: var(--bg-raised);
  border: 1px solid var(--line);
  color: var(--text);
  font-weight: 600;
  box-shadow: var(--shadow);
  animation: rise 0.2s ease-out;
}
.toast--ok { border-color: var(--ok); }
.toast--warn { border-color: var(--warn); }
.toast--error { border-color: var(--danger); }
.toast--leaving { opacity: 0; transition: opacity 0.3s; }
@keyframes rise { from { opacity: 0; transform: translateY(8px); } }

/* --------------------------------------------------------------- stock */

.stock { padding: calc(var(--page-pad) + 4px); max-width: 1600px; width: 100%; margin: 0 auto; box-sizing: border-box; }
.stock__head h1 { margin: 0 0 2px; font-size: 1.4rem; }
.stock h2 { font-size: 1rem; margin: 26px 0 10px; color: var(--text-dim);
            text-transform: uppercase; letter-spacing: 0.06em; }

.stock__totals { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 16px; margin-bottom: 16px; }
.stock__totals .stat { min-width: 220px; }
.stat small { display: block; margin-top: 2px; font-size: 0.7rem; }

/* The net of the movements listed, in the same column as the amounts it adds. */
table.table tfoot th { border-top: 1px solid var(--line); border-bottom: 0;
                       padding-top: 8px; font-variant-numeric: tabular-nums; }

/* One tab per shelf. Deliberately quiet: on a branch with a store room and
   three sections this is navigation, not the point of the screen, and the
   numbers under it are what somebody came here to read. */
.stock__tabs { display: flex; flex-wrap: wrap; gap: 4px; margin-top: 16px;
               border-bottom: 1px solid var(--line); }
.stock__tab { display: flex; align-items: baseline; gap: 6px; padding: 8px 14px;
              border: 1px solid transparent; border-bottom: 0;
              border-radius: var(--radius) var(--radius) 0 0;
              color: var(--text-dim); text-decoration: none; font-size: 0.9rem;
              margin-bottom: -1px; }
.stock__tab:hover { color: var(--text); background: var(--bg-sunken); }
.stock__tab--on { color: var(--text); background: var(--bg-sunken);
                  border-color: var(--line); border-bottom-color: var(--bg-sunken); }
.stock__tab small { font-size: 0.65rem; text-transform: uppercase; letter-spacing: 0.06em; }
/* How many ingredients the shelf is holding. The count is what somebody is
   actually scanning the strip for, so it reads as a number; "empty" is said
   quietly, because eight empty sections is the normal state of a kitchen. */
.stock__tab-count { font-size: 0.7rem; font-variant-numeric: tabular-nums;
                    padding: 1px 6px; border-radius: 999px;
                    background: var(--bg-raised); color: var(--text); }
.stock__tab-count--none { background: none; padding: 0; color: var(--text-dim);
                          opacity: 0.65; }

/* Search. Full width on its own line: it narrows the table under it, and a
   box tucked beside the totals reads as filtering them instead. */
.stock__search { display: flex; flex-wrap: wrap; align-items: center; gap: 8px;
                 margin-top: 16px; }
.stock__search input[type="search"] { flex: 1 1 260px; min-width: 0;
                                      min-height: var(--touch); }

/* The link out of a split's notice, to the shelf the stock landed on. */
.banner__link { margin-left: 8px; color: inherit; font-weight: 600; }

.stock__alerts { margin-top: 16px; }
.stock__chips { display: flex; flex-wrap: wrap; gap: 8px; list-style: none; padding: 0; margin: 0; }
.chip--warn { border-color: var(--warn); color: var(--warn); }
.chip--bad { border-color: var(--danger); color: var(--danger); }

.banner { border-radius: var(--radius); padding: 10px 14px; margin: 14px 0; }
.banner--ok { border: 1px solid var(--ok); color: var(--ok); }
.banner--bad { border: 1px solid var(--danger); color: var(--danger); }
/* Used by the kitchen board's "another terminal has the printer" and "offline"
   bars, and by the Printers screen. It had no rule at all, so both of those
   rendered as plain paragraphs -- which on the board is a warning about the
   kitchen not printing, drawn to look like prose. */
.banner--warn { border: 1px solid var(--warn); color: var(--warn); }

/* A line of explanation under a field: why a setting behaves the way it does,
   in the place somebody reads it rather than in a manual nobody has. */
.hint { color: var(--text-dim); font-size: 0.82rem; margin: -8px 0 16px; }

/* The data-table rules above right-align only the last cell, which is wrong
   once a row ends in a form. Quantities opt in with .num instead. */
table.table th { text-align: left; padding: 7px 4px; font-size: 0.75rem;
                 color: var(--text-dim); text-transform: uppercase;
                 letter-spacing: 0.05em; border-bottom: 1px solid var(--line); }
table.table td.num, table.table th.num { text-align: right; }
/* Scoped to this screen only: the cash-up report still wants its last column
   right-aligned, because there the last column is always money. */
.stock table.table td:last-child { text-align: left; }
table.table--compact td { padding: 4px; font-size: 0.85rem; }
table.table .pos { color: var(--ok); }
table.table .neg { color: var(--danger); }
table.table tr.row--warn td { background: var(--warn-soft); }
table.table tr.row--bad td { background: var(--danger-soft); }

.stock__form { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 8px; }
/* The buttons take a line of their own (flex-basis 100% forces the break),
   with the book-in/book-out pair pushed to the left and the split to the
   right. Each pair keeps its own gap, so In and Waste read as two buttons
   rather than one wide one. */
.stock__actions { flex: 1 0 100%; display: flex; flex-wrap: wrap; align-items: center;
                  justify-content: space-between; gap: 8px 16px; }
.stock__btns, .stock__split { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
.stock__split:empty { display: none; }
/* The reason box takes whatever the first line has left, so that line ends
   where the buttons' line does and the split sits under the boxes, not out
   past them. */
.stock__form input[name="reason"] { flex: 1 1 130px; min-width: 120px; max-width: none; }
/* The row form now also asks how a delivery was paid for. Selects need more
   room than the number boxes beside them or the answer reads as an ellipsis. */
.stock__form select.input--sm { max-width: 190px; min-width: 100px;}
/* Wide enough for the boxes to sit two rows deep rather than four, but no
   wider than the screen can pay for: a flat 560px, plus seven columns of
   figures, is more than a 1093px till has, and pushed the page sideways. */
[data-region="levels"] td:last-child { min-width: min(560px, 42vw); }

/* The levels table is read across a row -- this ingredient, this much, book
   it here -- so the figures get room to be told apart and the booking form
   is ruled off from them: without the rule, "4  0.00" ran straight into the
   quantity box and read as one number. */
table.table.stock__levels th, table.table.stock__levels td { padding: 8px 10px; }
table.stock__levels td:first-child { font-weight: 600; }
table.stock__levels td.num small { margin-left: 2px; }
table.table.stock__levels .stock__book { border-left: 1px solid var(--line); padding-left: 14px; }
/* A store room runs to a hundred ingredients; the column names stay in view
   while somebody scrolls down to the one they are booking. A shadow stands
   in for the bottom border, which a collapsed table does not carry along
   with a sticky cell. */
table.table.stock__levels thead th { position: sticky; top: 0; z-index: 1; background: var(--bg);
                          border-bottom: 0; box-shadow: inset 0 -1px 0 var(--line); }
table.table.stock__levels tbody tr:not(.row--warn):not(.row--bad):hover td { background: var(--primary-soft); }

.stock__vendor { display: flex; flex-wrap: nowrap; align-items: center; gap: 6px; }
.stock__vendor input.input--sm { max-width: 160px; min-width: 100px; }
.stock__vendor-view {
  background: none; border: none; padding: 0; margin: 0; cursor: pointer;
  color: var(--text-dim); font-size: 0.875rem; font: inherit; text-align: left;
}
.stock__vendor-view:hover, .stock__vendor-view:focus-visible { color: var(--text); text-decoration: underline; }

/* The store's standing answers, kept above the table rather than in the back
   office: the person who changes how deliveries are paid for is the person
   standing at this screen booking them. */
.stock__settings { display: flex; flex-wrap: wrap; align-items: center; gap: 8px;
                   margin-top: 16px; padding: 10px 12px; border: 1px solid var(--line);
                   border-radius: var(--radius); background: var(--bg-sunken); }
.stock__settings select.input--sm { max-width: 240px; }
.input {
  background: var(--bg-sunken);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  color: var(--text);
  font: inherit;
  padding: 8px 10px;
  min-height: 40px;
}
.input:focus { outline: 2px solid var(--primary); outline-offset: 1px; }
.input--sm { min-height: 34px; padding: 5px 8px; font-size: 0.85rem; max-width: 130px; }
/* Native selects draw their arrow on top of the text once the label is
   longer than the box, which reads as broken rather than truncated. Clear
   room for the arrow and let the label ellipsize before it gets there. */
select.input {
  appearance: none;
  -webkit-appearance: none;
  padding-right: 26px;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 8'%3E%3Cpath fill='none' stroke='%239aa3b8' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round' d='M1.5 2 6 6.2 10.5 2'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 8px center;
  background-size: 11px;
  text-overflow: ellipsis;
  white-space: nowrap;
  overflow: hidden;
}

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

/* Below 900px the ticket panel stops being a sidebar and becomes a sheet at
   the bottom, which is the only shape that works one-handed on a phone. */
@media (max-width: 900px) {
  .order { grid-template-columns: 1fr; grid-template-rows: 1fr auto; }
  .order__ticket { border-left: 0; border-top: 1px solid var(--line); max-height: 52vh; }
  .topbar__nav { order: 3; width: 100%; margin-left: 0; overflow-x: auto; }
  .topbar { flex-wrap: wrap; }
  /* On a phone the status pill, the name and three buttons are wider than
     the screen; wrapped, they take a second line instead of pushing
     "Sign out" off the right-hand edge and the whole page sideways. */
  .topbar__right { flex-wrap: wrap; justify-content: flex-end; min-width: 0; }
}

@media (max-width: 520px) {
  .tilegrid { grid-template-columns: repeat(auto-fill, minmax(112px, 1fr)); }
  /* The plan keeps its column count here and scrolls -- see .area__grid. A
     narrower repeat would reflow the room, which is the thing that made the
     same floor plan a different shape on a phone. */
  .area__grid { grid-template-columns: repeat(var(--floor-cols, 12), 68px); }
}

/* --- COA ---------------------------------------------------------------- */
/* One card per kind of cost. Each is a short stacked form rather than a row
   of inline inputs like the stock screen: these are keyed occasionally and
   deliberately, not forty times a shift, so labels are worth the space. */
.coa__card {
  background: var(--bg-sunken);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 16px;
}
.coa__card h3 { margin: 0 0 4px; font-size: 1rem; }
.coa__card > .muted { margin: 0 0 12px; font-size: 0.8rem; }

/* Six cards, evenly sized, read as a deliberate 3x2 grid on a desktop
   screen rather than however many auto-fit columns happen to fit. */
@media (min-width: 901px) {
  .coa__grid { grid-template-columns: repeat(3, 1fr); }
}

.coa__form { display: flex; flex-direction: column; gap: 10px; }
.coa__form label {
  display: flex;
  flex-direction: column;
  gap: 4px;
  font-size: 0.78rem;
  color: var(--text-dim);
}
.coa__form .btn { align-self: flex-start; margin-top: 4px; }

/* The month-on-month movement under the current balance, where there is no
   surrounding table to inherit the ledger colours from. */
.stat .pos { color: var(--ok); }
.stat .neg { color: var(--danger); }

/* ------------------------------------------------------- bill adjustments */

/*
 * The discount / comp / void sheet, reached from the ticket footer and from
 * the payment sheet. Rows are deliberately tall and the destructive action is
 * the only red thing on screen: this is used at the counter with a customer
 * watching, and a mis-tap here is a bill that has to be explained.
 */

.modal__panel--adjust { width: min(620px, 100%); }

.adjust__group { margin-bottom: 20px; }
.adjust__group h4 {
  margin: 0 0 8px;
  font-size: 0.85rem;
  color: var(--text-dim);
  text-transform: uppercase;
  letter-spacing: 0.06em;
}

.adjust__row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 10px 0;
  border-bottom: 1px solid var(--line);
  flex-wrap: wrap;
}
.adjust__row:last-child { border-bottom: 0; }
.adjust__what { min-width: 0; flex: 1 1 220px; }
.adjust__name { display: block; font-weight: 600; }
.adjust__meta {
  display: block;
  font-size: 0.8rem;
  color: var(--text-dim);
  font-variant-numeric: tabular-nums;
}
.adjust__acts { display: flex; gap: 6px; flex-wrap: wrap; justify-content: flex-end; }

.adjust__target { color: var(--text-dim); margin: 0 0 12px; }
.adjust__impact {
  margin: 4px 0 14px;
  font-variant-numeric: tabular-nums;
  color: var(--accent);
  min-height: 1.2em;
}
/* The same line says why a number cannot be applied; see problem() in app.js. */
.adjust__impact--bad { color: var(--danger); }

.adjust__summary {
  display: flex;
  align-items: baseline;
  gap: 10px;
  padding-top: 10px;
  border-top: 1px solid var(--line);
  font-variant-numeric: tabular-nums;
}
.adjust__summary strong { font-size: 1.25rem; }
.adjust__summary-note { margin-left: auto; color: var(--text-dim); font-size: 0.8rem; }

/* Small state flags on a row. Text, not colour alone -- see the file header. */
.tag {
  display: inline-block;
  padding: 1px 6px;
  margin-left: 6px;
  border: 1px solid var(--line);
  border-radius: 999px;
  font-size: 0.7rem;
  color: var(--text-dim);
}
.tag--comp { border-color: var(--ok); color: var(--ok); }
.tag--discount { border-color: var(--accent); color: var(--accent); }

.segmented { display: flex; gap: 0; margin-bottom: 8px; }
.segmented__btn {
  flex: 1;
  min-height: var(--touch);
  background: transparent;
  border: 1px solid var(--line);
  color: var(--text-dim);
  font: inherit;
  cursor: pointer;
}
.segmented__btn:first-child { border-radius: var(--radius) 0 0 var(--radius); }
.segmented__btn:last-child { border-radius: 0 var(--radius) var(--radius) 0; }
.segmented__btn.is-on {
  background: var(--primary);
  border-color: var(--primary);
  color: var(--primary-ink);
}

/* The manager-approval panel. */
.approve {
  border: 1px solid var(--warn);
  border-radius: var(--radius);
  padding: 12px;
  margin-bottom: 12px;
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.approve strong { color: var(--warn); }
.approve span { font-size: 0.85rem; color: var(--text-dim); }
.approve--ok { border-color: var(--ok); }
.approve--ok strong { color: var(--ok); }
.approve__offline, .approve__sent, .approve__pick { margin: 0; font-size: 0.85rem;
                                                    color: var(--text-dim); }
.approve__people { display: flex; flex-wrap: wrap; gap: 6px; }
.approve__contact { font-size: 0.72rem; color: var(--text-dim); }

.approve__code { display: flex; gap: 8px; }
.approve__code input {
  flex: 1;
  min-height: var(--touch);
  padding: 0 12px;
  background: var(--bg-sunken);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  color: var(--text);
  font: 600 1.4rem/1 var(--mono);
  letter-spacing: 0.3em;
  text-align: center;
}

/* A comped line stays on the ticket, marked, never hidden. */
.cartline--comp { border-left: 3px solid var(--ok); padding-left: 5px; }
.cartline__flag { font-size: 0.75rem; color: var(--ok); font-weight: 600; }

/* --- Floor plan editor --------------------------------------------------- */
/* Only ever on screen for a role holding admin.floor, and only while they are
   actually rearranging. Everything here is sized for the same hand the rest of
   the app is: the drag handle *is* the tile, and the resize grip is a 44px
   target sat in the corner rather than a 6px hairline nobody can hit on glass. */

.floorplan { display: grid; grid-template-columns: minmax(0, 1fr) 300px; gap: var(--gap); }
.floorplan__rooms { min-width: 0; }

.floorplan__bar {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
  padding: 10px 12px;
  margin-bottom: var(--gap);
  background: var(--bg-sunken);
  border: 1px solid var(--line);
  border-radius: var(--radius);
}
.floorplan__hint { color: var(--text-dim); font-size: 0.8rem; }
.floorplan__bar .floorplan__hint { margin-left: auto; }

.floorplan__room { margin-bottom: 24px; }
.floorplan__roomhead {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
  margin-bottom: 8px;
}
.floorplan__roomname {
  font-size: 1rem;
  font-weight: 700;
  background: transparent;
  border: 1px solid transparent;
  border-radius: var(--radius);
  color: var(--text);
  padding: 6px 8px;
  min-width: 8ch;
  flex: 1 1 8ch;
}
.floorplan__roomname:focus { border-color: var(--primary); background: var(--bg-sunken); outline: none; }
.floorplan__roomsize { display: flex; align-items: center; gap: 4px; color: var(--text-dim);
                       font-size: 0.78rem; }
/* Room for two digits and the browser's own spinner, which the old 4.5ch
   did not leave once the type came down on a small screen. */
.floorplan__roomsize input { width: calc(2ch + 32px); text-align: center; }

/* The plan itself, in edit mode: the grid squares are drawn so a manager can
   see what they are dropping onto. A drop target you cannot see is a drop
   target you aim at by trial and error. */
.floorplan__grid {
  position: relative;
  display: grid;
  grid-template-columns: repeat(var(--floor-cols, 12), minmax(76px, 1fr));
  grid-template-rows: repeat(var(--floor-rows, 8), var(--floor-row));
  gap: 8px;
  padding: 8px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background-color: var(--bg-sunken);
  background-image:
    linear-gradient(to right, var(--grid-line) 1px, transparent 1px),
    linear-gradient(to bottom, var(--grid-line) 1px, transparent 1px);
  background-size: calc((100% - 16px) / var(--floor-cols, 12)) calc(var(--floor-row) + 8px);
  background-position: 8px 8px;
  touch-action: none;
}

.floorplan__table {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 2px;
  padding: 6px;
  text-align: center;
  border: 2px solid var(--primary);
  border-radius: var(--radius);
  background: var(--primary-soft);
  color: var(--text);
  font: inherit;
  cursor: grab;
  touch-action: none;
  user-select: none;
  -webkit-user-select: none;
}
.floorplan__table.is-circle { border-radius: 50%; }
.floorplan__table.is-bar { border-style: dashed; border-radius: calc(var(--radius) / 2); }
/* Retired tables stay on the plan, greyed: a table that is out of use is still
   in the room, and one that vanished from the editor would be one nobody could
   put back into service. */
.floorplan__table.is-retired { border-color: var(--line); background: transparent; opacity: 0.6; }
.floorplan__table.is-selected { border-color: var(--accent); box-shadow: 0 0 0 2px var(--accent); }
.floorplan__table.is-dragging { opacity: 0.85; cursor: grabbing; z-index: 2; }
/* An open ticket is the one thing a rearrangement must not lose track of, so
   the tile says so rather than looking like any other empty table. */
.floorplan__table.is-busy::after {
  content: "";
  position: absolute;
  top: 4px;
  right: 6px;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--ok);
}
.floorplan__code { font-weight: 700; font-size: 1.05rem; }
.floorplan__seats { font-size: 0.7rem; color: var(--text-dim); }

.floorplan__grip {
  position: absolute;
  right: -6px;
  bottom: -6px;
  width: 28px;
  height: 28px;
  border-radius: 50%;
  border: 2px solid var(--accent);
  background: var(--bg-raised);
  cursor: nwse-resize;
  touch-action: none;
  /* The visible dot is 28px; the target around it is the full 44px the rest of
     the app promises, so a thumb finds it without a second try. */
  box-shadow: 0 0 0 8px rgba(0, 0, 0, 0.001);
}

/* The inspector: one table's details, beside the plan on a till and stacked
   underneath it on a tablet held upright. */
.floorplan__panel {
  align-self: start;
  position: sticky;
  top: 8px;
  background: var(--bg-sunken);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 14px;
}
.floorplan__panel h3 { margin: 0 0 10px; font-size: 0.95rem; }
.floorplan__panel .field { margin-bottom: 10px; }
.floorplan__panel label { display: block; font-size: 0.75rem; color: var(--text-dim);
                          margin-bottom: 4px; }
.floorplan__panel input, .floorplan__panel select {
  width: 100%;
  min-height: var(--touch);
  padding: 8px 10px;
  background: var(--bg);
  color: var(--text);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  font: inherit;
}
.floorplan__row { display: flex; gap: 8px; }
.floorplan__row .field { flex: 1; }
.floorplan__panelactions { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 12px; }
.floorplan__empty { color: var(--text-dim); font-size: 0.85rem; }

@media (max-width: 900px) {
  .floorplan { grid-template-columns: minmax(0, 1fr); }
  .floorplan__panel { position: static; }
}

/* ----------------------------------------------------- desktop density */

/*
 * Everything above is sized for a tablet held in one hand. Plenty of tills are
 * not that: they are older desktop PCs on 1366x768 or 1280x1024 panels, often
 * with Windows display scaling at 125% on top, which leaves Chrome a page of
 * about 1093x614. Sized for a tablet, that page held a single ticket line
 * under four rows of category chips and a topbar that wrapped its own labels
 * -- the "everything looks zoomed in" that only went away at 60% browser zoom.
 *
 * Two steps down, keyed on the room the browser actually has rather than on
 * the device: "compact" for anything up to a 1440 wide or 860 tall page, and
 * "tight" for pages under 680 tall, which is where the scaled 1366x768 panels
 * land. Mostly it is the tokens at the top of the file turning down; the rules
 * underneath are only the padding and type that were never tokens.
 *
 * Buttons only get smaller than 44px under a mouse (pointer: fine). A
 * touchscreen till the same size keeps the promise at the top of this file,
 * and still gets the narrower ticket, shorter tiles and one-line topbar.
 *
 * `screen` on every one of these on purpose: a print preview is a narrow
 * viewport too, and a receipt or a cash-up sheet must not pick up a smaller
 * rem because of it.
 */

@media screen and (max-width: 1440px), screen and (max-height: 860px) {
  :root {
    --font-size: 15px;
    --gap: 10px;
    --btn-lg: 48px;
    --key-h: 52px;
    --page-pad: 12px;
    --ticket-w: clamp(300px, 30vw, 360px);
    --tile-min: 136px;
    --tile-h: 94px;
    --tile-pad: 8px;
    --floor-row: 52px;
  }

  /* One line, whatever the width: a label that wraps doubles the height of
     the one bar that is on screen all day. */
  .topbar { gap: 8px; padding: 6px 12px; }
  .topbar__brand { min-width: 0; }
  .topbar__outlet { max-width: 32ch; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .topbar__nav { gap: 2px; }
  .navlink { padding: 6px 12px; white-space: nowrap; }
  .topbar__right { gap: 8px; }
  .topbar__right .btn, .topbar__user, .pill { white-space: nowrap; }
  .pill { padding: 4px 10px; }

  .btn { padding: 8px 14px; }
  .btn--sm { min-height: 32px; padding: 4px 10px; }
  .iconbtn { font-size: 1rem; }
  .input { min-height: var(--touch); }
  .field { margin-bottom: 10px; }
  .field input, .field--text input, .field--text textarea { padding: 8px 12px; }

  .chiprow { gap: 6px; padding: 2px 0; }
  .chip { padding: 4px 12px; font-size: 0.92rem; }

  /* ---- order */
  .order__menu { padding: 10px; gap: 8px; }
  .order__search input { padding: 6px 12px; }
  .tilegrid { gap: 8px; padding-bottom: 12px; }
  .ticket__head { padding: 6px 10px; gap: 8px; }
  .ticketstaff { padding: 4px 10px; }
  .ticket__lines { padding: 4px 6px; }
  .cartline { padding: 6px; gap: 4px 8px; }
  .ticket__totals { padding: 6px 12px; }
  .totals__row { padding: 2px 0; }
  .totals__row--grand { font-size: 1.2rem; margin-top: 4px; padding-top: 6px; }
  .totals__row--change { font-size: 1.05rem; margin-top: 4px; padding-top: 6px; }
  .ticket__actions { gap: 6px; padding: 8px; }
  /* The secondary buttons (serve all, reprint, adjust, discard) pair up two
     to a row instead of taking a full-width row each; stacked, four of them
     were most of the panel. An odd one out still spans the row. */
  .ticket__actions .btn--wide { grid-column: auto; }
  .ticket__actions .btn--wide:last-child:nth-child(odd) { grid-column: 1 / -1; }

  /* ---- sheets */
  .modal { padding: 10px; }
  .modal__panel { max-height: 94vh; }
  .modal__head { padding: 10px 14px; }
  .modal__body { padding: 12px 14px; }
  .modal__foot { padding: 10px 14px; }
  .modgroup { margin-bottom: 14px; }
  .opt { padding: 6px 10px; }
  .paysum__row--due { font-size: 1.45rem; }
  .payfield { margin: 8px 0; }
  .payfield .input { min-height: var(--btn-lg); font-size: 1.2rem; }

  /* ---- sign-in */
  .login { padding: 12px; }
  .login__card { padding: 20px; }
  .keypad { gap: 8px; margin-top: 12px; }
  .key { font-size: 1.3rem; }

  /* ---- floor, kitchen, back-of-house pages */
  .floor__bar { margin-bottom: 10px; }
  .area { margin-bottom: 18px; }
  .table__code { font-size: 1.05rem; }
  .kds { padding: 10px; }
  .panel { margin: 12px auto; padding: 16px; }
  .panel__head { margin-bottom: 12px; }
  .statrow { gap: 8px; margin-bottom: 16px; }
  .stat { padding: 10px; }
  .stat strong { font-size: 1.3rem; }
  .grid2 { gap: 16px; }
  .coa__card { padding: 12px; }
  table.table.stock__levels th, table.table.stock__levels td { padding: 6px 7px; }
  .floorplan__panel { padding: 12px; }
}

@media screen and (pointer: fine) and (max-width: 1440px),
       screen and (pointer: fine) and (max-height: 860px) {
  :root { --touch: 38px; }
}

@media screen and (max-height: 680px) {
  :root {
    --font-size: 14px;
    --btn-lg: 42px;
    --key-h: 44px;
    --tile-h: 86px;
    --floor-row: 46px;
  }
  .login__card { padding: 16px; }
  .login__brand { margin-bottom: 0.3em; }
  .totals__row--grand { font-size: 1.1rem; }
}

@media screen and (pointer: fine) and (max-height: 680px) {
  :root { --touch: 34px; }
}

/* Under 1180 wide the signed-in name gives up its room first: it is the one
   thing in the topbar nobody acts on, and it is still on the Sign out
   button's tooltip. */
@media screen and (max-width: 1180px) and (min-width: 901px) {
  .topbar__user { display: none; }
  .topbar__outlet { max-width: 14ch; }
}
