/* ===========================================================================
   Theme tokens
   ===========================================================================
   Every color in this file comes from a token below. Nothing hard-codes a
   hex value outside these blocks, so dark mode is a matter of redefining
   the tokens rather than re-styling each component -- and a new component
   inherits both themes for free.

   Three blocks, in this order, and the order matters:

     1. :root                          -- the light theme, always the base
     2. @media (prefers-color-scheme: dark)
        :root:not([data-theme="light"])  -- follow the phone or laptop
     3. :root[data-theme="dark"]       -- an explicit choice, beats the OS

   The :not([data-theme="light"]) guard is what makes "Light" mean light
   even on a phone set to dark. Without it, a device in dark mode would
   drag the app back to dark whatever the staff member picked.

   The theme is applied to <html> by a small inline script in index.html
   that runs before the stylesheet paints, so there is no white flash on a
   dark phone.
   =========================================================================== */

:root {
  --dc-warn-bg: #fff3cd;      /* Close day: open tabs warning */
  --dc-warn-text: #6b4e12;
  /* The calendar's kinds (2026-10-14), same as on Google Calendar. */
  --cal-reservation: #f4511e;
  --cal-payment: #e2b400;
  --cal-task: #3f51b5;
  --cal-notice: #d50000;
  --cal-google: #8a8a8a;
  color-scheme: light;
  --custom-cat-bg: #e3e5ea;
  --light-green: #1f9d55;
  --light-yellow: #d99a06;
  --light-red: #d23b3b;
  /* Japanese names review (2026-10-19): how sure the research is, as a
     3-bar signal -- low red 1 bar, medium yellow 2, high green 3. */
  --jar-low: #d93025;
  --jar-low-bg: #fde8e6;
  --jar-low-ink: #9b1c13;
  --jar-med: #d9a400;
  --jar-med-bg: #fff4cc;
  --jar-med-ink: #6b5100;
  --jar-high: #1e8e3e;
  --jar-high-bg: #e3f4e8;
  --jar-high-ink: #145c29;
  --jar-on-high: #ffffff;
  --jar-bar-off: rgba(0, 0, 0, 0.16);
  --light-blue: #2f6fe4;
  --heat-darkred: #8f1e18;
  --custom-cat-ink: #1b1d22;
  --custom-cat-edge: #b9bdc6;

  /* Dashboard charts: one gold series first (the house colour), then
     colours that stay apart for someone who cannot tell red from green. */
  --chart-1: #b8912a;
  --chart-2: #2f6fd6;
  --chart-3: #2e9d5b;
  --chart-4: #d9822b;
  --chart-5: #8a5cc7;
  --chart-6: #2a9d9d;
  --chart-grid: rgba(0, 0, 0, 0.08);
  --chart-heat-rgb: 184, 145, 42;

  --bg: #f2f1ee;              /* page */
  --surface: #fff;            /* cards, tables, panels */
  --surface-hover: #fafafa;
  --surface-sunken: #f0f0ee;  /* table headers */
  --surface-alt: #f2f2f2;     /* clickable row hover */

  --text: #1c1c1c;
  --text-muted: #666;
  /* #6b6b6b, not the #888 this used to be. This token carries the
     (Optional) markers and every placeholder, and #888 landed at 3.1:1
     on the page background -- under the 4.5:1 minimum for body text.
     #6b6b6b clears it on both the page (4.7:1) and a white card (5.3:1). */
  --text-faint: #6b6b6b;
  --text-on-dark: #fff;       /* text on a filled dark control */

  --border: #ccc;
  --border-soft: #ddd;
  /* The edge of a control sitting ON a card, where the card and the
     control share a background. Picked to clear 3:1 against the card
     in both themes: --border (#ccc) is only 1.6:1 there, which is why
     the cover-charge picker read as text floating on the dialog
     rather than as a row of buttons. */
  --bubble-edge: #8a8a8a;
  --border-faint: #eee;

  --accent: #1c1c1c;          /* the "selected / primary" fill */
  --staff-db: #d9720a;        /* a staff drink's letter, staff screens only (2026-10-12) */
  --accent-hover: #333;
  --accent-text: #fff;

  /* Two danger tokens, not one. --danger is danger as TEXT (an error
     line, a negative total); --danger-fill is danger as a BUTTON, which
     has white text sitting on it. In light mode one value serves both.
     In dark mode it cannot: a red light enough to read on a near-black
     page is too light to put white text on, so they part company. */
  --danger: #b3261e;
  --bell-high: #b26a00;   /* Important, in the bell */
  --poll-star: #c98a00;   /* a lit star in the re-order poll (2026-10-12) */
  --backup-ok: #2e7d32;   /* a backup that reached Drive, Settings (2026-10-12) */
  --poll-star-off: #c9c9c9;
  --cpoll-bar: #b8912a;        /* customer poll results: a choice's share (2026-10-12) */
  --cpoll-bar-track: #ebe8e0;
  --cpoll-warn: #8a6d08;
  --cpoll-draft-bg: #e7d9f7;   --cpoll-draft-text: #4a2a6b;
  --bell-high-ink: #ffffff;
  --bell-unread: #2f6fde;
  --danger-fill: #b3261e;
  --danger-text: #fff;
  --danger-muted: #a33;       /* the (Required) marker */
  --danger-rgb: 179, 38, 30;  /* same red as --danger, for rgba() tints */
  --training-rgb: 214, 40, 40;  /* LIGHT MODE: red. Yellow on a pale page is barely there -- red is the one that carries across the bar. */
  --training-ink: #ffffff;  /* text on the training colour: white on the light-mode red */
  --hazard-ink: #111;  /* the black of hazard tape -- same in both themes */
  --training-bg: #e6e4de;  /* a darker white than the normal page */
  /* Complimentary mode (the next drink is free): a magenta rim and a
     deep-violet notch. The owner can change both in Settings; those
     choices are set inline on <html> and win in every theme. */
  --comp-rgb: 217, 70, 239;
  --comp-notch-bg: linear-gradient(145deg, #7C3AED, #4C1D95);
  --comp-notch-ink: #fff;
  /* --- RJ membership tiers -------------------------------------------
  One background and one ink per tier, so a chip is legible on its own
  colour rather than inheriting whatever the surrounding card is. Black
  is a real tier name, not the theme: in light mode it is near-black
  with white text, and in dark mode it inverts so it does not vanish
  into the page. */
  --tier-bronze-rgb: 176, 108, 58;
  --tier-bronze-ink: #ffffff;
  --tier-silver-rgb: 138, 147, 158;
  --tier-silver-ink: #ffffff;
  --tier-gold-rgb: 193, 150, 24;
  --tier-gold-ink: #1f1804;
  --tier-black-rgb: 43, 45, 52;
  --tier-black-ink: #ffffff;
  --tier-diamond-rgb: 82, 158, 198;
  --tier-diamond-ink: #ffffff;
    /* Tier plates (the owner, 2026-10-20): polished metal you can tell
       apart across a dim room, the same in light and dark. Each metal has
       a plate (-tex), a frame (-rim, a gradient so the edge catches the
       light), lettering (-letter) and a plain ink for small text. */
    --tier-bronze-tex: repeating-linear-gradient(90deg, rgba(255, 230, 200, 0.10) 0 1px, rgba(0, 0, 0, 0.07) 1px 2px, rgba(0, 0, 0, 0) 2px 4px), linear-gradient(180deg, rgba(255, 255, 255, 0.32) 0%, rgba(255, 255, 255, 0) 48%), linear-gradient(135deg, #3e1d0a 0%, #8a4a1e 18%, #d38d52 34%, #ffd2a1 44%, #c47a3c 56%, #7a3f17 74%, #b26a33 88%, #4a230c 100%);
    --tier-bronze-rim: linear-gradient(135deg, #ffe0bd 0%, #7a3f17 32%, #f6bd86 52%, #4a230c 78%, #eaa86f 100%);
    --tier-bronze-letter: linear-gradient(180deg, #ffe6c9 0%, #f3b277 40%, #9c5524 56%, #eba468 78%, #ffd8ad 100%);
    --tier-bronze-tex-ink: #fff6ec;
    --tier-silver-tex: repeating-linear-gradient(90deg, rgba(255, 255, 255, 0.28) 0 1px, rgba(0, 0, 0, 0.05) 1px 2px, rgba(0, 0, 0, 0) 2px 4px), linear-gradient(180deg, rgba(255, 255, 255, 0.5) 0%, rgba(255, 255, 255, 0) 46%), linear-gradient(180deg, #ffffff 0%, #dfe4e9 18%, #9ea7b1 46%, #f4f6f8 54%, #c4cbd2 72%, #8a939d 100%);
    --tier-silver-rim: linear-gradient(135deg, #ffffff 0%, #8a939d 32%, #f7f9fb 52%, #6c757f 78%, #e9edf1 100%);
    --tier-silver-letter: linear-gradient(180deg, #ffffff 0%, #e6eaee 40%, #8d969f 56%, #e1e6eb 78%, #ffffff 100%);
    --tier-silver-tex-ink: #1b1f25;
    --tier-gold-tex: linear-gradient(180deg, rgba(255, 255, 255, 0.38) 0%, rgba(255, 255, 255, 0) 46%), linear-gradient(160deg, #5e4100 0%, #a87b0a 14%, #e3b23c 28%, #fff4c2 40%, #ffe48a 46%, #d9a520 58%, #9c7206 76%, #e8c25a 90%, #6e5100 100%);
    --tier-gold-rim: linear-gradient(135deg, #fff6cc 0%, #a87b0a 32%, #ffe38a 52%, #6e5100 78%, #f3d47a 100%);
    --tier-gold-letter: linear-gradient(180deg, #fffbe0 0%, #ffe07a 38%, #b8860b 55%, #f5d06a 78%, #fff3c0 100%);
    --tier-gold-tex-ink: #2a1d00;
    --tier-black-tex: linear-gradient(115deg, rgba(255, 255, 255, 0) 30%, rgba(255, 255, 255, 0.16) 42%, rgba(255, 255, 255, 0) 54%), linear-gradient(180deg, rgba(255, 255, 255, 0.14) 0%, rgba(255, 255, 255, 0) 45%), radial-gradient(120% 140% at 30% 0%, #34363d 0%, #141519 45%, #030304 100%);
    --tier-black-rim: linear-gradient(135deg, #fff3c4 0%, #8a6a22 30%, #f3dc8c 52%, #5a430f 78%, #e8cf86 100%);
    --tier-black-letter: linear-gradient(180deg, #fff7d6 0%, #f1d27a 40%, #a8801e 56%, #f3dc8c 80%, #fff3c4 100%);
    --tier-black-tex-ink: #ecd9a2;
    --tier-black-script: #f4f1ea;
    --tier-diamond-tex: linear-gradient(115deg, rgba(255, 255, 255, 0.95) 0%, rgba(196, 236, 255, 0.55) 18%, rgba(255, 255, 255, 0.9) 30%, rgba(150, 214, 244, 0.6) 44%, rgba(255, 255, 255, 0.95) 56%, rgba(178, 228, 252, 0.55) 72%, rgba(255, 255, 255, 0.9) 86%), conic-gradient(from 30deg at 40% 50%, #e9f9ff, #a6dcf6, #ffffff, #86cdee, #dff5ff, #b4e3f8, #e9f9ff);
    --tier-diamond-rim: linear-gradient(135deg, #ffffff 0%, #bfe9ff 28%, #ffffff 50%, #86cdee 74%, #ffffff 100%);
    --tier-diamond-tex-ink: #0b3a4f;
    --tier-diamond-glow-rgb: 140, 214, 250;
  /* The RJ Member badge's plate. Dark in BOTH themes on purpose: a member's
  card is an object the bar hands out, not a panel of the page, and neon
  only reads as neon against something dark. */
  --rj-badge-plate: #14151a;
  --rj-badge-plate-black: #0b0b0e;
  --rj-badge-word: #e8e8ea;
  --rj-badge-black-line-rgb: 222, 224, 232;


  --shadow-card: 0 2px 8px rgba(0,0,0,0.08);
  --shadow-row: 0 1px 3px rgba(0,0,0,0.06);
  --backdrop: rgba(0,0,0,0.4);
  --on-fill-tint: rgba(255,255,255,0.2);  /* buttons sitting on a banner */

  /* Status badges: a pale fill with dark text reads well on white but
     turns into a glare patch on black, so dark mode inverts the pairing
     rather than dimming it. */
  --badge-bg: #ddd;
  --badge-text: var(--text);
  --badge-paid-bg: #d4edda;   --badge-paid-text: #155724;
  --badge-open-bg: #fff3cd;   --badge-open-text: #856404;
  --badge-void-bg: #f8d7da;   --badge-void-text: #721c24;
  --badge-db-bg: #e7d9f7;     --badge-db-text: #4a2a6b;
  --perm-yes: #155724;
  --perm-no: #999;

  /* Where a bill stands, at a glance across a dim room. One colour per
     thing that can be true, and only ONE of them means "you are owed
     money and nobody is drinking": red. The two dead ends -- a cancelled
     tab and a fully refunded one -- are grey on purpose, so red never
     has to compete with something nobody needs to act on.

     Each tone carries its own glow colour. The glow is what makes these
     readable from the other side of the bar; the fill alone washes out
     under the lights. */
  --st-open-bg: #1d4ed8;      --st-open-text: #fff;   --st-open-glow: 59, 130, 246;
  /* The lit panel on the customer's copy that says what is about to be
     charged. Dark ink on a light page, so the glow is the page's own
     near-black rather than a colour that would read as a warning. */
  --pay-glow: 28, 28, 28;
  --st-partial-bg: #b45309;   --st-partial-text: #fff; --st-partial-glow: 245, 158, 11;
  --st-paid-bg: #15803d;      --st-paid-text: #fff;   --st-paid-glow: 34, 197, 94;
  --st-overdue-bg: #b3261e;   --st-overdue-text: #fff; --st-overdue-glow: 239, 68, 68;
  /* Still owing from an earlier night, not yet past the overdue line:
     the yellow of the tab lights. Red stays for overdue past the line. */
  --st-late-bg: #d99a06;      --st-late-text: #1b1b1b; --st-late-glow: 245, 180, 30;
  --st-void-bg: #6b7280;      --st-void-text: #fff;   --st-void-glow: 156, 163, 175;
  /* Outstanding: signed for tonight, to be paid later. Teal, so it reads
     as "in hand" -- neither the blue of a live tab nor the red of a debt
     nobody has answered for. */
  --st-outstanding-bg: #0e7490; --st-outstanding-text: #fff; --st-outstanding-glow: 34, 211, 238;
  /* The signing pad is paper in every theme: a signature is dark ink on
     white, and the saved image has to read the same wherever it is
     opened later. */
  --sign-pad-bg: #ffffff;     --sign-pad-edge: #9aa1ad;  --sign-pad-hint: #8a909b;
  --st-refunded-bg: #475569;  --st-refunded-text: #fff; --st-refunded-glow: 100, 116, 139;
  --st-partrefund-bg: #6d28d9; --st-partrefund-text: #fff; --st-partrefund-glow: 167, 139, 250;

  /* Banners keep their alarm colors in both themes -- a stock warning
     that politely fades into a dark background is a warning nobody acts
     on -- but the dark variants are desaturated so they do not glow. */
  --alert-bg: #7a1d14;        --alert-border: #4d120c;
  /* Darkened from #b7950b / #d35400, which carried white text at 2.9:1
     and 4.2:1 -- under the 4.5:1 minimum. These are the stock-expiry
     warnings staff are meant to read at a glance across a dim bar, so
     they are the last place to accept text that is hard to read. The
     urgency ladder (yellow -> orange -> red) is unchanged. */
  --warn-low: #8a6d08;
  --warn-mid: #a34400;
  --warn-high: #b3261e;
  /* Clocked in with tonight's QR / not yet (2026-10-12). */
  --checkin-ok: #15803d;
  --checkin-wait: #a35a00;
  --checkin-bad: #b3261e;

  --tax-note-bg: #fdf8e6;
  --tax-note-border: #c9a227;
  --positive: #2a7;           /* the "On" toggle, commission-earning tag */
  --card-up-text: #15803d;  /* a rise, on the card analysis */
  /* The Log In button, in the logo's gold (the .brand-rj gradient), with
     dark lettering that stays readable on every stop of it. */
  --login-gold-hi: #f6e6a8;
  --login-gold: #d4af37;
  --login-gold-deep: #a8791f;
  --login-gold-ink: #1f1804;
  /* Sign in with Face ID, under the PIN form (2026-10-12). */
  --login-face-bg: #ffffff;
  --login-face-ink: #1c1c1c;
  --login-face-edge: #a8791f;
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --dc-warn-bg: #3d3214;
    --dc-warn-text: #f3dc8a;
    --cal-reservation: #ff7a4d;
    --cal-payment: #f6c94a;
    --cal-task: #7986cb;
    --cal-notice: #ff5252;
    --cal-google: #a0a0a0;
    --custom-cat-bg: #c9cdd5;
    --light-green: #3ccf7a;
    --light-yellow: #f2b632;
    --light-red: #ff5a5a;
    --jar-low: #ff6b5e;
    --jar-low-bg: #3a1714;
    --jar-low-ink: #ffb4ab;
    --jar-med: #f6c945;
    --jar-med-bg: #3a300c;
    --jar-med-ink: #f9dc85;
    --jar-high: #4cc670;
    --jar-high-bg: #12301b;
    --jar-high-ink: #9be2b0;
    --jar-on-high: #0b0b0b;
    --jar-bar-off: rgba(255, 255, 255, 0.22);
  --light-blue: #5b8fff;
  --heat-darkred: #c0392b;
    --light-blue: #5b8fff;
    --heat-darkred: #c0392b;
    --custom-cat-ink: #15171c;
    --custom-cat-edge: #8d929c;
    --chart-1: #d4af37;
    --chart-2: #5b93f0;
    --chart-3: #4cc27e;
    --chart-4: #f0a050;
    --chart-5: #a882e0;
    --chart-6: #4cc4c4;
    --chart-grid: rgba(255, 255, 255, 0.09);
    --chart-heat-rgb: 212, 175, 55;
    color-scheme: dark;

    /* Not pure black: a true #000 bar behind white text is harsh over a
       long shift, and there is no room left underneath it to show depth. */
    --bg: #15161a;
    --surface: #1e2027;
    --surface-hover: #262931;
    --surface-sunken: #24262e;
    --surface-alt: #2b2e37;

    --text: #e8e8ea;
    --text-muted: #a2a6b0;
    --text-faint: #8b8f99;
    --text-on-dark: #fff;

    --border: #3a3e48;
    --border-soft: #333741;
    --bubble-edge: #767d8a;
    --border-faint: #2b2e36;

    /* Inverted: on a dark page the emphatic fill is the light one. */
    --accent: #e8e8ea;
  --staff-db: #ffa040;
    --accent-hover: #fff;
    --accent-text: #15161a;

    --danger: #ef5350;       /* 4.7:1 as text on a card */
    --bell-high: #ffb74d;
    --bell-high-ink: #221500;
    --poll-star: #ffc94d;
    --backup-ok: #6fcf75;
    --poll-star-off: #4a4e58;
    --cpoll-bar: #d4af37;
    --cpoll-bar-track: #2c2d33;
    --cpoll-warn: #f3dc8a;
    --cpoll-draft-bg: #3b2a55;   --cpoll-draft-text: #e3d2fb;
    --bell-unread: #64a0ff;
    --danger-fill: #c93a35;  /* 5.1:1 under white button text */
    --danger-text: #fff;
    --danger-muted: #ef8b88;
    --danger-rgb: 239, 83, 80;  /* same red as --danger, for rgba() tints */
    --training-rgb: 250, 224, 66;  /* DARK MODE: yellow. On a dark page it glows; red would sink into it. */
    --training-ink: #111111;  /* text on the training colour: black on the dark-mode yellow */
    --training-bg: #23262b;  /* a lighter grey than the normal dark page */
    /* Complimentary mode (the next drink is free): a magenta rim and a
       deep-violet notch. The owner can change both in Settings; those
       choices are set inline on <html> and win in every theme. */
    --comp-rgb: 217, 70, 239;
    --comp-notch-bg: linear-gradient(145deg, #7C3AED, #4C1D95);
    --comp-notch-ink: #fff;
    /* --- RJ membership tiers: lifted so each chip carries on a dark page.
    Black inverts to a pale plate with dark ink; left as-is it would be
    a black chip on a black card. */
    --tier-bronze-rgb: 205, 140, 90;
    --tier-bronze-ink: #1a1208;
    --tier-silver-rgb: 186, 194, 204;
    --tier-silver-ink: #15181c;
    --tier-gold-rgb: 228, 186, 62;
    --tier-gold-ink: #221a03;
    --tier-black-rgb: 222, 224, 232;
    --tier-black-ink: #14151a;
    --tier-diamond-rgb: 132, 206, 240;
    --tier-diamond-ink: #06242e;
    /* Tier plates (the owner, 2026-10-20): polished metal you can tell
       apart across a dim room, the same in light and dark. Each metal has
       a plate (-tex), a frame (-rim, a gradient so the edge catches the
       light), lettering (-letter) and a plain ink for small text. */
    --tier-bronze-tex: repeating-linear-gradient(90deg, rgba(255, 230, 200, 0.10) 0 1px, rgba(0, 0, 0, 0.07) 1px 2px, rgba(0, 0, 0, 0) 2px 4px), linear-gradient(180deg, rgba(255, 255, 255, 0.32) 0%, rgba(255, 255, 255, 0) 48%), linear-gradient(135deg, #3e1d0a 0%, #8a4a1e 18%, #d38d52 34%, #ffd2a1 44%, #c47a3c 56%, #7a3f17 74%, #b26a33 88%, #4a230c 100%);
    --tier-bronze-rim: linear-gradient(135deg, #ffe0bd 0%, #7a3f17 32%, #f6bd86 52%, #4a230c 78%, #eaa86f 100%);
    --tier-bronze-letter: linear-gradient(180deg, #ffe6c9 0%, #f3b277 40%, #9c5524 56%, #eba468 78%, #ffd8ad 100%);
    --tier-bronze-tex-ink: #fff6ec;
    --tier-silver-tex: repeating-linear-gradient(90deg, rgba(255, 255, 255, 0.28) 0 1px, rgba(0, 0, 0, 0.05) 1px 2px, rgba(0, 0, 0, 0) 2px 4px), linear-gradient(180deg, rgba(255, 255, 255, 0.5) 0%, rgba(255, 255, 255, 0) 46%), linear-gradient(180deg, #ffffff 0%, #dfe4e9 18%, #9ea7b1 46%, #f4f6f8 54%, #c4cbd2 72%, #8a939d 100%);
    --tier-silver-rim: linear-gradient(135deg, #ffffff 0%, #8a939d 32%, #f7f9fb 52%, #6c757f 78%, #e9edf1 100%);
    --tier-silver-letter: linear-gradient(180deg, #ffffff 0%, #e6eaee 40%, #8d969f 56%, #e1e6eb 78%, #ffffff 100%);
    --tier-silver-tex-ink: #1b1f25;
    --tier-gold-tex: linear-gradient(180deg, rgba(255, 255, 255, 0.38) 0%, rgba(255, 255, 255, 0) 46%), linear-gradient(160deg, #5e4100 0%, #a87b0a 14%, #e3b23c 28%, #fff4c2 40%, #ffe48a 46%, #d9a520 58%, #9c7206 76%, #e8c25a 90%, #6e5100 100%);
    --tier-gold-rim: linear-gradient(135deg, #fff6cc 0%, #a87b0a 32%, #ffe38a 52%, #6e5100 78%, #f3d47a 100%);
    --tier-gold-letter: linear-gradient(180deg, #fffbe0 0%, #ffe07a 38%, #b8860b 55%, #f5d06a 78%, #fff3c0 100%);
    --tier-gold-tex-ink: #2a1d00;
    --tier-black-tex: linear-gradient(115deg, rgba(255, 255, 255, 0) 30%, rgba(255, 255, 255, 0.16) 42%, rgba(255, 255, 255, 0) 54%), linear-gradient(180deg, rgba(255, 255, 255, 0.14) 0%, rgba(255, 255, 255, 0) 45%), radial-gradient(120% 140% at 30% 0%, #34363d 0%, #141519 45%, #030304 100%);
    --tier-black-rim: linear-gradient(135deg, #fff3c4 0%, #8a6a22 30%, #f3dc8c 52%, #5a430f 78%, #e8cf86 100%);
    --tier-black-letter: linear-gradient(180deg, #fff7d6 0%, #f1d27a 40%, #a8801e 56%, #f3dc8c 80%, #fff3c4 100%);
    --tier-black-tex-ink: #ecd9a2;
    --tier-black-script: #f4f1ea;
    --tier-diamond-tex: linear-gradient(115deg, rgba(255, 255, 255, 0.95) 0%, rgba(196, 236, 255, 0.55) 18%, rgba(255, 255, 255, 0.9) 30%, rgba(150, 214, 244, 0.6) 44%, rgba(255, 255, 255, 0.95) 56%, rgba(178, 228, 252, 0.55) 72%, rgba(255, 255, 255, 0.9) 86%), conic-gradient(from 30deg at 40% 50%, #e9f9ff, #a6dcf6, #ffffff, #86cdee, #dff5ff, #b4e3f8, #e9f9ff);
    --tier-diamond-rim: linear-gradient(135deg, #ffffff 0%, #bfe9ff 28%, #ffffff 50%, #86cdee 74%, #ffffff 100%);
    --tier-diamond-tex-ink: #0b3a4f;
    --tier-diamond-glow-rgb: 140, 214, 250;
    /* The RJ Member badge's plate. Dark in BOTH themes on purpose: a member's
    card is an object the bar hands out, not a panel of the page, and neon
    only reads as neon against something dark. */
    --rj-badge-plate: #14151a;
    --rj-badge-plate-black: #0b0b0e;
    --rj-badge-word: #e8e8ea;
    --rj-badge-black-line-rgb: 222, 224, 232;


    --shadow-card: 0 2px 8px rgba(0,0,0,0.45);
    --shadow-row: 0 1px 3px rgba(0,0,0,0.35);
    --backdrop: rgba(0,0,0,0.65);
    --on-fill-tint: rgba(255,255,255,0.18);

    --badge-bg: #333741;
    --badge-text: var(--text);
    --badge-paid-bg: #1d3a26;   --badge-paid-text: #7fd699;
    --badge-open-bg: #3d3520;   --badge-open-text: #e8c76a;
    --badge-void-bg: #3f2222;   --badge-void-text: #f0a0a0;
    --badge-db-bg: #332a44;     --badge-db-text: #c9b3e8;
    --perm-yes: #7fd699;
    --perm-no: #7a7e88;

    /* Lifted a step and the glows pulled back: the same saturation that
       reads as crisp on white becomes a smear of light on a dark bar. */
    --st-open-bg: #2563eb;      --st-open-glow: 96, 165, 250;
    /* Light ink on a dark page: the same panel has to glow light to be
       seen at all, so the triple flips with the theme. */
    --pay-glow: 232, 232, 234;
    --st-partial-bg: #b05c00;   --st-partial-glow: 251, 191, 36;
    --st-paid-bg: #158541;      --st-paid-glow: 74, 222, 128;
    --st-overdue-bg: #dc2626;   --st-overdue-glow: 248, 113, 113;
    --st-late-bg: #f2b632;      --st-late-glow: 250, 204, 21;
    --st-void-bg: #6b7280;      --st-void-glow: 156, 163, 175;
    --st-outstanding-bg: #0e7490; --st-outstanding-glow: 103, 232, 249;
    --sign-pad-bg: #ffffff;     --sign-pad-edge: #6b7280;  --sign-pad-hint: #8a909b;
    --st-refunded-bg: #526175;  --st-refunded-glow: 124, 139, 161;
    --st-partrefund-bg: #7c3aed; --st-partrefund-glow: 196, 181, 253;

    --alert-bg: #6b1a12;        --alert-border: #40100b;
    --warn-low: #876d08;   /* white banner text needs 4.5:1; #8f7409 was 4.497 */
    --warn-mid: #a84400;
    --warn-high: #8f1e18;
    --checkin-ok: #4ade80;
    --checkin-wait: #fbbf24;
    --checkin-bad: #f87171;

    --tax-note-bg: #2c2718;
    --tax-note-border: #a58722;
    --positive: #46c08a;
    --card-up-text: #4ade80;  /* a rise, on the card analysis */
    --login-gold-hi: #f6e6a8;
    --login-gold: #d4af37;
    --login-gold-deep: #a8791f;
    --login-gold-ink: #1f1804;
    /* Sign in with Face ID, under the PIN form (2026-10-12). */
    --login-face-bg: #202024;
    --login-face-ink: #f2f2f2;
    --login-face-edge: #d4af37;
  }
}

/* An explicit "Dark" choice: same values, no media query, so it wins on a
   device set to light. Kept as a copy rather than a shared class because
   a class on <html> cannot be set before paint as reliably as an
   attribute, and the duplication is the price of no flash. */
:root[data-theme="dark"] {
  --dc-warn-bg: #3d3214;
  --dc-warn-text: #f3dc8a;
  --cal-reservation: #ff7a4d;
  --cal-payment: #f6c94a;
  --cal-task: #7986cb;
  --cal-notice: #ff5252;
  --cal-google: #a0a0a0;
  --custom-cat-bg: #c9cdd5;
  --light-green: #3ccf7a;
  --light-yellow: #f2b632;
  --light-red: #ff5a5a;
  --jar-low: #ff6b5e;
  --jar-low-bg: #3a1714;
  --jar-low-ink: #ffb4ab;
  --jar-med: #f6c945;
  --jar-med-bg: #3a300c;
  --jar-med-ink: #f9dc85;
  --jar-high: #4cc670;
  --jar-high-bg: #12301b;
  --jar-high-ink: #9be2b0;
  --jar-on-high: #0b0b0b;
  --jar-bar-off: rgba(255, 255, 255, 0.22);
  --custom-cat-ink: #15171c;
  --custom-cat-edge: #8d929c;
  --chart-1: #d4af37;
  --chart-2: #5b93f0;
  --chart-3: #4cc27e;
  --chart-4: #f0a050;
  --chart-5: #a882e0;
  --chart-6: #4cc4c4;
  --chart-grid: rgba(255, 255, 255, 0.09);
  --chart-heat-rgb: 212, 175, 55;
  color-scheme: dark;

  --bg: #15161a;
  --surface: #1e2027;
  --surface-hover: #262931;
  --surface-sunken: #24262e;
  --surface-alt: #2b2e37;

  --text: #e8e8ea;
  --text-muted: #a2a6b0;
  --text-faint: #8b8f99;
  --text-on-dark: #fff;

  --border: #3a3e48;
  --border-soft: #333741;
  --bubble-edge: #767d8a;
  --border-faint: #2b2e36;

  --accent: #e8e8ea;
  --staff-db: #ffa040;
  --accent-hover: #fff;
  --accent-text: #15161a;

  --danger: #ef5350;       /* 4.7:1 as text on a card */
  --bell-high: #ffb74d;
  --bell-high-ink: #221500;
  --poll-star: #ffc94d;
  --backup-ok: #6fcf75;
  --poll-star-off: #4a4e58;
  --cpoll-bar: #d4af37;
  --cpoll-bar-track: #2c2d33;
  --cpoll-warn: #f3dc8a;
  --cpoll-draft-bg: #3b2a55;   --cpoll-draft-text: #e3d2fb;
  --bell-unread: #64a0ff;
  --danger-fill: #c93a35;  /* 5.1:1 under white button text */
  --danger-text: #fff;
  --danger-muted: #ef8b88;
  --danger-rgb: 239, 83, 80;  /* same red as --danger, for rgba() tints */
  --training-rgb: 250, 224, 66;  /* DARK MODE: yellow. On a dark page it glows; red would sink into it. */
  --training-ink: #111111;  /* text on the training colour: black on the dark-mode yellow */
  --training-bg: #23262b;  /* a lighter grey than the normal dark page */
  /* Complimentary mode (the next drink is free): a magenta rim and a
     deep-violet notch. The owner can change both in Settings; those
     choices are set inline on <html> and win in every theme. */
  --comp-rgb: 217, 70, 239;
  --comp-notch-bg: linear-gradient(145deg, #7C3AED, #4C1D95);
  --comp-notch-ink: #fff;
  /* --- RJ membership tiers: lifted so each chip carries on a dark page.
  Black inverts to a pale plate with dark ink; left as-is it would be
  a black chip on a black card. */
    --tier-bronze-rgb: 205, 140, 90;
    --tier-bronze-ink: #1a1208;
    --tier-silver-rgb: 186, 194, 204;
    --tier-silver-ink: #15181c;
    --tier-gold-rgb: 228, 186, 62;
    --tier-gold-ink: #221a03;
    --tier-black-rgb: 222, 224, 232;
    --tier-black-ink: #14151a;
    --tier-diamond-rgb: 132, 206, 240;
    --tier-diamond-ink: #06242e;
    /* Tier plates (the owner, 2026-10-20): polished metal you can tell
       apart across a dim room, the same in light and dark. Each metal has
       a plate (-tex), a frame (-rim, a gradient so the edge catches the
       light), lettering (-letter) and a plain ink for small text. */
    --tier-bronze-tex: repeating-linear-gradient(90deg, rgba(255, 230, 200, 0.10) 0 1px, rgba(0, 0, 0, 0.07) 1px 2px, rgba(0, 0, 0, 0) 2px 4px), linear-gradient(180deg, rgba(255, 255, 255, 0.32) 0%, rgba(255, 255, 255, 0) 48%), linear-gradient(135deg, #3e1d0a 0%, #8a4a1e 18%, #d38d52 34%, #ffd2a1 44%, #c47a3c 56%, #7a3f17 74%, #b26a33 88%, #4a230c 100%);
    --tier-bronze-rim: linear-gradient(135deg, #ffe0bd 0%, #7a3f17 32%, #f6bd86 52%, #4a230c 78%, #eaa86f 100%);
    --tier-bronze-letter: linear-gradient(180deg, #ffe6c9 0%, #f3b277 40%, #9c5524 56%, #eba468 78%, #ffd8ad 100%);
    --tier-bronze-tex-ink: #fff6ec;
    --tier-silver-tex: repeating-linear-gradient(90deg, rgba(255, 255, 255, 0.28) 0 1px, rgba(0, 0, 0, 0.05) 1px 2px, rgba(0, 0, 0, 0) 2px 4px), linear-gradient(180deg, rgba(255, 255, 255, 0.5) 0%, rgba(255, 255, 255, 0) 46%), linear-gradient(180deg, #ffffff 0%, #dfe4e9 18%, #9ea7b1 46%, #f4f6f8 54%, #c4cbd2 72%, #8a939d 100%);
    --tier-silver-rim: linear-gradient(135deg, #ffffff 0%, #8a939d 32%, #f7f9fb 52%, #6c757f 78%, #e9edf1 100%);
    --tier-silver-letter: linear-gradient(180deg, #ffffff 0%, #e6eaee 40%, #8d969f 56%, #e1e6eb 78%, #ffffff 100%);
    --tier-silver-tex-ink: #1b1f25;
    --tier-gold-tex: linear-gradient(180deg, rgba(255, 255, 255, 0.38) 0%, rgba(255, 255, 255, 0) 46%), linear-gradient(160deg, #5e4100 0%, #a87b0a 14%, #e3b23c 28%, #fff4c2 40%, #ffe48a 46%, #d9a520 58%, #9c7206 76%, #e8c25a 90%, #6e5100 100%);
    --tier-gold-rim: linear-gradient(135deg, #fff6cc 0%, #a87b0a 32%, #ffe38a 52%, #6e5100 78%, #f3d47a 100%);
    --tier-gold-letter: linear-gradient(180deg, #fffbe0 0%, #ffe07a 38%, #b8860b 55%, #f5d06a 78%, #fff3c0 100%);
    --tier-gold-tex-ink: #2a1d00;
    --tier-black-tex: linear-gradient(115deg, rgba(255, 255, 255, 0) 30%, rgba(255, 255, 255, 0.16) 42%, rgba(255, 255, 255, 0) 54%), linear-gradient(180deg, rgba(255, 255, 255, 0.14) 0%, rgba(255, 255, 255, 0) 45%), radial-gradient(120% 140% at 30% 0%, #34363d 0%, #141519 45%, #030304 100%);
    --tier-black-rim: linear-gradient(135deg, #fff3c4 0%, #8a6a22 30%, #f3dc8c 52%, #5a430f 78%, #e8cf86 100%);
    --tier-black-letter: linear-gradient(180deg, #fff7d6 0%, #f1d27a 40%, #a8801e 56%, #f3dc8c 80%, #fff3c4 100%);
    --tier-black-tex-ink: #ecd9a2;
    --tier-black-script: #f4f1ea;
    --tier-diamond-tex: linear-gradient(115deg, rgba(255, 255, 255, 0.95) 0%, rgba(196, 236, 255, 0.55) 18%, rgba(255, 255, 255, 0.9) 30%, rgba(150, 214, 244, 0.6) 44%, rgba(255, 255, 255, 0.95) 56%, rgba(178, 228, 252, 0.55) 72%, rgba(255, 255, 255, 0.9) 86%), conic-gradient(from 30deg at 40% 50%, #e9f9ff, #a6dcf6, #ffffff, #86cdee, #dff5ff, #b4e3f8, #e9f9ff);
    --tier-diamond-rim: linear-gradient(135deg, #ffffff 0%, #bfe9ff 28%, #ffffff 50%, #86cdee 74%, #ffffff 100%);
    --tier-diamond-tex-ink: #0b3a4f;
    --tier-diamond-glow-rgb: 140, 214, 250;
  /* The RJ Member badge's plate. Dark in BOTH themes on purpose: a member's
  card is an object the bar hands out, not a panel of the page, and neon
  only reads as neon against something dark. */
  --rj-badge-plate: #14151a;
  --rj-badge-plate-black: #0b0b0e;
  --rj-badge-word: #e8e8ea;
  --rj-badge-black-line-rgb: 222, 224, 232;


  --shadow-card: 0 2px 8px rgba(0,0,0,0.45);
  --shadow-row: 0 1px 3px rgba(0,0,0,0.35);
  --backdrop: rgba(0,0,0,0.65);
  --on-fill-tint: rgba(255,255,255,0.18);

  --badge-bg: #333741;
  --badge-text: var(--text);
  --badge-paid-bg: #1d3a26;   --badge-paid-text: #7fd699;
  --badge-open-bg: #3d3520;   --badge-open-text: #e8c76a;
  --badge-void-bg: #3f2222;   --badge-void-text: #f0a0a0;
  --badge-db-bg: #332a44;     --badge-db-text: #c9b3e8;
  --perm-yes: #7fd699;
  --perm-no: #7a7e88;

  --st-open-bg: #2563eb;      --st-open-glow: 96, 165, 250;
  --pay-glow: 232, 232, 234;
  --st-partial-bg: #b05c00;   --st-partial-glow: 251, 191, 36;
  --st-paid-bg: #158541;      --st-paid-glow: 74, 222, 128;
  --st-overdue-bg: #dc2626;   --st-overdue-glow: 248, 113, 113;
  --st-late-bg: #f2b632;      --st-late-glow: 250, 204, 21;
  --st-void-bg: #6b7280;      --st-void-glow: 156, 163, 175;
  --st-outstanding-bg: #0e7490; --st-outstanding-glow: 103, 232, 249;
  --sign-pad-bg: #ffffff;     --sign-pad-edge: #6b7280;  --sign-pad-hint: #8a909b;
  --st-refunded-bg: #526175;  --st-refunded-glow: 124, 139, 161;
  --st-partrefund-bg: #7c3aed; --st-partrefund-glow: 196, 181, 253;

  --alert-bg: #6b1a12;        --alert-border: #40100b;
  --warn-low: #876d08;   /* white banner text needs 4.5:1; #8f7409 was 4.497 */
  --warn-mid: #a84400;
  --warn-high: #8f1e18;
  --checkin-ok: #4ade80;
  --checkin-wait: #fbbf24;
  --checkin-bad: #f87171;

  --tax-note-bg: #2c2718;
  --tax-note-border: #a58722;
  --positive: #46c08a;
  --card-up-text: #4ade80;  /* a rise, on the card analysis */
  --login-gold-hi: #f6e6a8;
  --login-gold: #d4af37;
  --login-gold-deep: #a8791f;
  --login-gold-ink: #1f1804;
  /* Sign in with Face ID, under the PIN form (2026-10-12). */
  --login-face-bg: #202024;
  --login-face-ink: #f2f2f2;
  --login-face-edge: #d4af37;
}

/* ===========================================================================
   Components
   =========================================================================== */

* { box-sizing: border-box; }
body {
  margin: 0;
  font-family: -apple-system, "Segoe UI", Roboto, sans-serif;
  background: var(--bg);
  color: var(--text);
  font-size: 17px;
}
h1, h2, h3 { margin: 0.2em 0; }
.hidden { display: none !important; }

/* The top bar is dark in both themes: in light mode it is the anchor that
   separates chrome from content, and in dark mode it still needs to read
   as a bar rather than dissolve into the page. */
#topbar {
  display: flex;
  justify-content: space-between;
  align-items: center;
  background: #1c1c1c;
  color: #fff;
  padding: 10px 16px;
  /* Sticky rather than static so it can slide away and come back without
     the content under it jumping by the bar's height each time. */
  position: sticky;
  top: 0;
  z-index: 40;
  transition: transform 0.22s ease;
}
/* Scrolled down: out of the way. Scrolled up: back. Nothing is lost --
   the bar is one flick away wherever you are in a long tab. */
#topbar.topbar-hidden { transform: translateY(-100%); }
@media (prefers-reduced-motion: reduce) {
  #topbar { transition: none; }
}

/* RJ POS, in the logo's hand. Snell Roundhand is the face the logo was
   set in and is present on the Mac and iPad this runs on; the rest of the
   stack is for anything else that opens it. The RJ keeps the logo's tight
   pairing -- that overlap is the signature -- while POS sits beside it in
   plain caps, the way "Members Lounge" sits under the monogram. */
#brand {
  display: inline-flex;
  align-items: baseline;
  /* Small, because .brand-rj now carries a wide right padding of its own
     to give the J's tail somewhere to be painted. That padding is the
     visual gap; adding much more here would push POS too far out. */
  gap: 2px;
  user-select: none;
  flex: 0 0 auto;
}
.brand-rj {
  /* Snell Roundhand first: it is the face the logo was set in, and it
     ships with macOS and iOS, which is what the bar actually uses. The
     rest are the nearest round hands on other systems, so the POS still
     reads as a script if it is ever opened on Windows, Android or Linux
     rather than falling back to a plain serif. */
  font-family: "Snell Roundhand", "Apple Chancery", "Zapfino",
               "Segoe Script", "Gabriola", "Edwardian Script ITC",
               "URW Chancery L", "Z003", "Brush Script MT", cursive;
  font-size: 2rem;
  font-weight: 700;
  /* The logo's letters touch. Negative tracking gets the R and J back
     into that pairing instead of sitting apart like ordinary text. */
  letter-spacing: -0.06em;
  /* Room for the swashes -- and this is what was cutting the J off.
     background-clip:text paints the gradient ONLY inside this element's
     own padding box. Ink that hangs past that box gets no paint at all,
     so the tail of the J did not overflow, it simply stopped existing.
     The cure is padding, not overflow.
     Measured in a chancery face the J runs 0.10em past where the letter
     officially ends; Snell Roundhand's tail is longer still, so the right
     side is given far more than it needs. It costs nothing -- the gap to
     POS below is reduced to match, so the mark looks the same. */
  padding-left: 0.1em;
  padding-right: 0.5em;
  /* This was 1, and that is what pushed the RJ out of the black bar. A
     line box of exactly 1em cannot hold a script face: measured at 100px,
     the font box wants 118px and the ink itself runs 94px with the tail of
     the J below the baseline. The overflow painted onto the page behind
     the bar, which is the cut-out look. 1.6 leaves headroom above and
     below at every size, and the bar grows to fit it rather than cropping
     it, because the bar's height comes from this line box. */
  line-height: 1.6;
  background: linear-gradient(170deg, #f6e6a8 0%, #d4af37 38%, #a8791f 62%, #f3dd94 100%);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  -webkit-text-fill-color: transparent;
  /* The 3D: a dark edge below and a light one above, so the gold reads as
     raised rather than as a flat gradient fill. */
  filter: drop-shadow(0 1px 0 rgba(0, 0, 0, 0.55))
          drop-shadow(0 -1px 0 rgba(255, 245, 200, 0.28))
          drop-shadow(0 3px 5px rgba(0, 0, 0, 0.45));
}
/* The bar sizes itself from its content, so the brand decides its height.
   The floor only matters if the font falls back to something short --
   #topbar already centres what is in it. */
#topbar { min-height: 56px; }
.brand-pos {
  font-size: 0.78rem;
  font-weight: 600;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: #d4af37;
  opacity: 0.92;
}

/* The logged-in person's name beside the mark (2026-10-14): small, muted
   gold, never wider than it needs; on a phone it shortens with an
   ellipsis so the buttons keep their room. */
.brand-user {
  margin-left: 10px;
  margin-right: auto;
  /* The same size as POS, on POS's line (the owner, 2026-10-20). */
  font-size: 0.78rem;
  line-height: 1;
  font-weight: 600;
  color: #d4af37;
  opacity: 0.85;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 180px;
  align-self: center;
}
.brand-user:empty { display: none; }
@media (min-width: 561px) { .brand-user { position: relative; top: 6px; } }
/* A phone has no room beside the mark (the buttons start right after
   it), so the name sits under the mark, inside the bar's bottom padding. */
@media (max-width: 560px) {
  .brand-user { position: absolute; left: 16px; bottom: 0; margin: 0; max-width: 110px; font-size: 0.7rem; line-height: 1.2; opacity: 0.8; }
}

.topbar-actions { display: flex; align-items: center; gap: 8px; }
/* Quick switch: the names as big bubbles, the 2-digit box wide enough to tap. */
.quick-names { gap: 8px; margin: 6px 0 12px; }
.quick-names .bubble { margin: 0; min-height: 52px; }
input.quick-code { max-width: 140px; font-size: 1.6rem; letter-spacing: 0.4em; text-align: center; }

/* A button with something that drops out of it. Positioned so the panel
   hangs off the button rather than off the page. */
.menu-anchor { position: relative; display: inline-flex; }
.popover {
  position: absolute;
  top: calc(100% + 6px);
  right: 0;
  z-index: 60;
  background: #2a2a2a;
  border: 1px solid #555;
  border-radius: 10px;
  padding: 6px;
  box-shadow: 0 10px 24px rgba(0, 0, 0, 0.45);
  min-width: 170px;
}
.account-menu { display: flex; flex-direction: column; gap: 4px; }
/* Centred, not ragged-left. Three buttons of very different widths --
   a short label, a glyph, a long one -- read as a stack rather than a
   list when their contents line up down the middle. inline-flex does the
   vertical centring too, which text-align alone cannot, and is what
   keeps the globe sitting on the same line as the words above and below
   it rather than riding high in its box. */
.account-menu button {
  width: 100%;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  min-height: 44px;
}
/* The globe has no text to set its height, so it takes the same box as
   its neighbours and centres its mark inside. */
.lang-globe { padding: 0; line-height: 1; }
.lang-globe svg { display: block; }

.icon-btn {
  min-width: 44px;
  min-height: 44px;
  padding: 0;
  font-size: 1.15rem;
  line-height: 1;
  /* Centred both ways. Without this the mark sat left, because the button
     inherits the text-align the other bubbles use for their labels. */
  display: inline-flex;
  align-items: center;
  justify-content: center;
  /* The mark inside is an SVG now, not a character, so there is no font
     to negotiate with and nothing that can come back as a colour emoji.
     It takes this colour through currentColor. */
  color: currentColor;
}
.icon-btn svg { display: block; }

/* The same mark beside each choice in the panel, so which disc means
   which mode is shown rather than remembered. */
.theme-opt-icon {
  display: inline-flex;
  width: 18px;
  height: 18px;
  margin-right: 8px;
  vertical-align: -4px;
}
.theme-opt-icon svg { width: 18px; height: 18px; }
.theme-picker.popover button {
  display: inline-flex;
  align-items: center;
}

/* The name IS the button. Role under it, quieter, so a glance says who is
   on the till without a second line of chrome. */
.account-btn {
  display: inline-flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 1px;
  line-height: 1.15;
  padding: 6px 12px;
  min-height: 44px;
}
#account-name { font-weight: 600; }
#account-role { font-size: 0.72rem; opacity: 0.72; }
/* Stacked in a panel it is no longer the three-segment pill it is on the
   login card, so the pill's own shape has to be undone: a 999px radius on
   a tall box draws an ellipse, and the segment rules would leave the
   buttons square inside a rounded panel. Matches the account menu, which
   hangs off the button beside it. */
.theme-picker.popover {
  display: flex;
  flex-direction: column;
  gap: 4px;
  border-radius: 10px;
  overflow: visible;
  margin-left: 0;
}
.theme-picker.popover button {
  border-left: none;
  border-radius: 999px;
  width: 100%;
  text-align: left;
}
.theme-picker.popover.hidden, .account-menu.hidden { display: none; }

:root[data-theme="dark"] #topbar { background: #101116; }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) #topbar { background: #101116; }
}
#topbar button { background: #444; color: #fff; border-color: #555; }
#topbar button:hover { background: #555; }

/* Light / Dark / System picker. Three segments so the current choice is
   visible at a glance rather than hidden behind a cycling icon -- staff
   should never have to tap twice to find out what mode they are in. */
.theme-picker {
  display: inline-flex;
  gap: 0;
  border-radius: 999px;
  overflow: hidden;
  border: 1px solid #555;
  margin-left: 8px;
  vertical-align: middle;
}
.theme-picker button {
  margin: 0;
  border: none;
  border-radius: 0;
  padding: 8px 12px;
  min-height: 38px;
  font-size: 0.85rem;
  background: #2a2a2a;
  color: #ccc;
}
.theme-picker button:hover { background: #3a3a3a; color: #fff; }
.theme-picker button.active { background: #fff; color: #1c1c1c; font-weight: 600; }
.theme-picker button + button { border-left: 1px solid #555; }

/* Language. A globe in the top bar opens the same three choices that sit
   on the login card -- G asked for the picker to be on the very first
   landing page, and staff who change it mid-shift should not have to log
   out to find it. */
.globe-btn {
  background: #2a2a2a;
  color: #fff;
  border: 1px solid #555;
  border-radius: 999px;
  font-size: 1.15rem;
  line-height: 1;
  padding: 8px 12px;
  min-height: 38px;
  margin-left: 6px;
  vertical-align: middle;
}
.globe-btn:hover { background: #3a3a3a; }

.lang-row { display: flex; flex-direction: column; gap: 6px; margin-top: 4px; }
.lang-btn {
  display: flex; align-items: center; gap: 8px;
  width: 100%; margin: 0; text-align: left;
  background: var(--surface); color: var(--text);
  border: 1px solid var(--border); border-radius: 10px;
  padding: 10px 14px; min-height: 46px; font-size: 1.05rem;
}
.lang-btn:hover { background: var(--surface-hover); }
.lang-btn.active {
  background: var(--accent); color: var(--accent-text); border-color: var(--accent);
  font-weight: 600;
}
.lang-btn .globe { font-size: 1.15rem; }

/* The same picker on the login card, where there is no dark bar behind it
   and it has to sit on the page's own surface instead. */
.card .theme-picker { border-color: var(--border); margin: 0; }
.card .theme-picker button { background: var(--surface); color: var(--text-muted); }
.card .theme-picker button:hover { background: var(--surface-hover); color: var(--text); }
.card .theme-picker button.active { background: var(--accent); color: var(--accent-text); }
.card .theme-picker button + button { border-left: 1px solid var(--border); }

/* Admin-only alert from an outside system (e.g. the GMO automation
   losing its login). Sits above the stock banner because it means a
   back-office job has stopped, not that a product is ageing. */
.alert-banner {
  padding: 10px 16px;
  color: #fff;
  background: var(--alert-bg);
  border-bottom: 3px solid var(--alert-border);
}
.alert-banner button { background: var(--on-fill-tint); color: #fff; border: none; }
.alert-banner ul { margin: 8px 0 0; padding-left: 20px; }
.alert-banner ul.hidden { display: none; }
.alert-banner li { margin: 4px 0; }
.alert-banner .meta { opacity: 0.8; font-size: 0.88em; }

.expiry-banner {
  padding: 10px 16px;
  color: #fff;
  background: var(--warn-low); /* default: mildest urgency present, overridden inline per-load */
}
.expiry-banner.urgency-expired, .expiry-banner.urgency-today { background: var(--warn-high); }
.expiry-banner.urgency-tomorrow, .expiry-banner.urgency-this_week { background: var(--warn-mid); }
.expiry-banner.urgency-two_weeks, .expiry-banner.urgency-one_month { background: var(--warn-low); }
.expiry-banner button { background: var(--on-fill-tint); color: #fff; border: none; }
.expiry-banner ul { margin: 8px 0 0; padding-left: 20px; }
.expiry-banner ul.hidden { display: none; }
.expiry-banner li { margin: 2px 0; }

.screen { padding: 16px; max-width: 1100px; margin: 0 auto; }
/* A sideways swipe is ours (screen to screen), never the browser's back/forward. */
html, body { overscroll-behavior-x: none; }
/* Swiped to: the new screen slides in from the side the finger came from. */
@keyframes swipe-in-left { from { transform: translateX(40px); opacity: 0.4; } to { transform: none; opacity: 1; } }
@keyframes swipe-in-right { from { transform: translateX(-40px); opacity: 0.4; } to { transform: none; opacity: 1; } }
.screen.swipe-in-left { animation: swipe-in-left 0.22s ease-out; }
.screen.swipe-in-right { animation: swipe-in-right 0.22s ease-out; }
@media (prefers-reduced-motion: reduce) { .screen.swipe-in-left, .screen.swipe-in-right { animation: none; } }

.card {
  background: var(--surface);
  border-radius: 10px;
  padding: 20px;
  max-width: 360px;
  margin: 40px auto;
  box-shadow: var(--shadow-card);
}
.card label { display: block; margin-top: 12px; font-size: 0.85em; color: var(--text-muted); }
.card label.checkbox-row { display: flex; align-items: center; gap: 8px; font-size: 0.95em; color: var(--text); }
.card label.checkbox-row input { width: auto; margin: 0; }
.card input, .card select {
  width: 100%; padding: 10px; font-size: 1.1em; margin-top: 4px;
  border: 1px solid var(--border); border-radius: 6px;
  background: var(--surface); color: var(--text);
}

button {
  padding: 12px 20px;
  border-radius: 999px;
  border: 1px solid var(--border);
  background: var(--surface);
  color: var(--text);
  font-size: 1.05rem;
  font-weight: 500;
  cursor: pointer;
  margin: 4px 4px 4px 0;
  min-height: 44px;
}
button:hover { background: var(--surface-hover); }
button.primary { background: var(--accent); color: var(--accent-text); border-color: var(--accent); }
button.primary:hover { background: var(--accent-hover); }
button.danger { background: var(--danger-fill); color: var(--danger-text); border-color: var(--danger-fill); }
button.primary.danger { background: var(--danger-fill); color: var(--danger-text); border-color: var(--danger-fill); }
/* A destructive button waiting for its second tap. It has to look clearly
   different from the first state, or the two taps feel like one button
   that failed to respond. Pulses so it reads as armed and temporary --
   it disarms itself after four seconds. */
button.confirming {
  background: var(--danger-fill);
  color: var(--danger-text);
  border-color: var(--danger-fill);
  font-weight: 600;
  animation: confirm-pulse 1s ease-in-out infinite;
}
@keyframes confirm-pulse {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.62; }
}
@media (prefers-reduced-motion: reduce) {
  button.confirming { animation: none; }
}
button:disabled { opacity: 0.4; cursor: not-allowed; }

/* Any input outside a card (report date pickers, search boxes, the file
   picker) -- these were relying on the browser default, which is a white
   box with black text and unreadable on a dark page. */
input, select, textarea {
  background: var(--surface);
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 8px;
  font-size: 1em;
  font-family: inherit;
}
input[type="checkbox"], input[type="radio"] {
  width: auto; padding: 0; accent-color: var(--accent);
}
input[type="file"] { border: none; padding: 0; background: none; }
::placeholder { color: var(--text-faint); opacity: 1; }

/* The PIN box on the login screen, with its eye at the right end. */
.pin-wrap { position: relative; }
.pin-wrap input { width: 100%; padding-right: 52px; }
.pin-eye {
  position: absolute; right: 4px; top: 50%; transform: translateY(-50%);
  width: 44px; height: 40px; min-height: 0; margin: 0; padding: 0;
  display: inline-flex; align-items: center; justify-content: center;
  background: none; border: none; color: var(--text-muted); border-radius: 8px;
}
.pin-eye:hover { color: var(--text); background: var(--surface-hover); }
.pin-eye .eye-open { display: none; }
.pin-eye.showing .eye-open { display: block; }
.pin-eye.showing .eye-closed { display: none; }

/* Bubble-style choice buttons (payment method, service charge %, discount type) */
.bubble-row { display: flex; flex-wrap: wrap; gap: 10px; margin: 10px 0; }
/* "By card (+5%): ..." under a total: the same bill paid by card. */
.by-card-line { margin: 0 0 10px; color: var(--text-muted); font-weight: 600; }
.by-card-line:empty { display: none; }
.totals .by-card-row { color: var(--text-muted); font-size: 0.92em; }
.client-totals .client-by-card dt, .client-totals .client-by-card dd { color: var(--text-muted); font-weight: 600; }
/* The Breakage bubble sits at the right of the Open Tabs title, on the
   same line even on a 320px phone: the row does not wrap and the title
   may shrink. */
#screen-tabs > .row.space-between { flex-wrap: nowrap; }
#screen-tabs > .row.space-between > h1 { min-width: 0; }
.breakage-list { max-height: 48vh; overflow-y: auto; }
.breakage-list .bubble { display: inline-flex; flex-direction: column; align-items: center; gap: 2px; min-width: 120px; }
.breakage-list .bubble small { font-weight: 500; color: var(--text-muted); font-size: 0.78em; }
.breakage-list .bubble.selected small { color: inherit; opacity: 0.8; }
#breakage-q { width: 100%; margin: 4px 0 8px; }
.bubble {
  padding: 14px 22px;
  border-radius: 999px;
  border: 2px solid var(--border-soft);
  background: var(--surface);
  color: var(--text);
  font-size: 1.1rem;
  font-weight: 600;
  cursor: pointer;
  min-height: 52px;
}
.bubble:hover { border-color: var(--border); background: var(--surface-hover); }
.bubble.selected { background: var(--accent); color: var(--accent-text); border-color: var(--accent); }

.big-btn {
  display: block;
  width: 100%;
  padding: 20px;
  font-size: 1.35rem;
  font-weight: 700;
  border-radius: 999px;
  text-align: center;
}

.bottom-bar {
  position: sticky;
  bottom: 0;
  background: var(--bg);
  padding: 14px 0 4px;
  margin-top: 10px;
  z-index: 5;
}
@media (max-width: 800px) {
  /* NOT sticky on a phone. Stuck to the bottom it floated over the drink
     bubbles -- and the owner's rule is that tapping a drink beats reading
     the tab. Closing a bill happens once a night and can live at the
     bottom of the page where it belongs. */
  .bottom-bar { position: static; }
}

/* Credit-to, complimentary and the cover charge. Below the drinks on a
   phone, and visually quieter, because they modify an order rather than
   being one. */
.menu-modifiers {
  margin-top: 18px;
  padding-top: 14px;
  border-top: 1px solid var(--border-faint);
}
.menu-modifiers label { color: var(--text-muted); font-size: 0.95em; }

/* Wraps rather than overflowing. English fits on one line at phone width;
   Portuguese runs 20-30% longer and pushed the customer/note row off the
   side of a 390px screen, which scrolled the whole page sideways. Wrapping
   is the fix for the whole class of problem, in any language. */
.row { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.row.space-between { justify-content: space-between; }

.list { display: flex; flex-direction: column; gap: 8px; margin: 10px 0; }

.tab-card, .bill-item-row, .payment-row {
  background: var(--surface);
  border-radius: 12px;
  padding: 16px;
  display: flex;
  justify-content: space-between;
  align-items: center;
  box-shadow: var(--shadow-row);
  font-size: 1.05rem;
}
.tab-card { cursor: pointer; gap: 12px; }
.tab-card:hover { background: var(--surface-hover); }
/* The customer's name and the table number are what staff scan for, so
   they lead the card at the largest size; time, bill number and totals
   follow small and quiet. */
.tab-card-main { min-width: 0; display: flex; flex-direction: column; gap: 3px; }
.tab-card-head { display: flex; align-items: center; flex-wrap: wrap; gap: 6px 10px; margin-bottom: 2px; }
.tab-card-name {
  font-size: 1.55rem;
  font-weight: 800;
  line-height: 1.15;
  color: var(--text);
  overflow-wrap: anywhere;
}
.tab-card-name.no-name { color: var(--text-muted); font-weight: 700; }
.tab-card .tab-card-table {
  font-size: 1.35rem;
  line-height: 1.15;
  padding: 4px 12px;
  min-width: 44px;
  margin-right: 0;
  vertical-align: 0;
}
.tab-card-head .client-code { font-size: 0.9rem; }
.tab-card-meta { color: var(--text-muted); font-size: 0.85rem; line-height: 1.35; }
@media (max-width: 560px) {
  .tab-card-name { font-size: 1.35rem; }
  .tab-card .tab-card-table { font-size: 1.2rem; }
}

/* Status above, Email receipt below, so the button never squeezes the
   badge off the end of a narrow phone card. */
.tab-card-side {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 8px;
  flex-shrink: 0;
}
/* The status chip in the tabs list. Same colours and wording as the one
   on the bill it opens -- only smaller, because here it sits beside a
   date and a running total rather than under the figure it describes.
   "Unpaid -- earlier night" is a long label, so it wraps in the card
   instead of stretching the row. */
.tab-card-side .status-chip {
  font-size: 0.78rem;
  padding: 6px 12px;
  gap: 6px;
  max-width: 11rem;
  text-align: center;
  line-height: 1.25;
}
.tab-card-side .status-chip::before { width: 7px; height: 7px; }

.tab-card-side .btn-email-bill {
  font-size: 0.82rem;
  padding: 8px 12px;
  /* The card underneath opens the bill; this has to read as its own
     control or staff will tap it by accident and lose their place. */
  background: var(--surface-sunken);
  border: 1px solid var(--border-faint);
  white-space: nowrap;
}
.tab-card-side .btn-email-bill:hover { color: var(--accent); }
.bill-item-row.voided { opacity: 0.5; text-decoration: line-through; }

.bill-layout { display: grid; grid-template-columns: 1fr 1fr; gap: 20px; }
@media (max-width: 800px) {
  .bill-layout { grid-template-columns: 1fr; }
  /* On a phone the ORDER PANEL comes first and the running tab sits under
     it. Ordering is what staff do fifty times a night; reading the total
     is what they do once. Putting the bubbles at the top means no
     scrolling before the first tap. */
  .bill-layout .menu-panel { order: 1; }
  .bill-layout .bill-items-panel { order: 2; }
}

/* The undo button on a just-added line. Deliberately NOT the danger red
   that Void wears: undoing a thirty-second-old mistake is routine, and
   dressing it as destructive makes staff hesitate over exactly the action
   they should take without thinking. */
.btn-undo {
  background: var(--surface-sunken);
  border-color: var(--border);
  color: var(--text);
  font-variant-numeric: tabular-nums;   /* the countdown must not jitter */
  min-width: 118px;
  padding: 8px 12px;
  font-size: 0.95rem;
}
.btn-undo:hover { background: var(--surface-hover); }

/* The top bar on a phone. At 17px with five full-size buttons it used up
   270px before any content -- which is most of why the app "didn't fit"
   and had to be zoomed out. The controls stay full-size enough to tap;
   the name shrinks, because it is the one thing nobody taps. */
@media (max-width: 560px) {
  #topbar { padding: 6px 10px; gap: 6px; flex-wrap: nowrap; }
  /* This slot used to hold the staff name, which was clamped to two lines
     inside 92px. It holds the brand now, and those rules were cropping it:
     a script "RJ POS" is wider than 92px, and overflow:hidden took the
     difference off the end. The mark is never truncated -- it shrinks a
     little instead, and is allowed to keep its own width. */
  #brand { flex: 0 0 auto; overflow: visible; }
  /* The mark keeps its size on a phone too -- measured, it still clears
     the bar and the buttons at 430px. Only POS tightens up. */
  .brand-pos { font-size: 0.7rem; letter-spacing: 0.2em; }
  #topbar > div { display: flex; flex-wrap: wrap; gap: 4px; justify-content: flex-end; }
  #topbar button { padding: 8px 10px; min-height: 38px; font-size: 0.9rem; margin: 0; }
  .theme-picker { margin-left: 0; }
  .theme-picker button { padding: 6px 8px; min-height: 34px; font-size: 0.78rem; }
  .globe-btn { margin-left: 4px; padding: 6px 9px; min-height: 34px; }
  .screen { padding: 12px 10px; }
}

/* The color picker, inline inside the category dialog. */
.color-picker { margin-top: 6px; }
.color-preview {
  height: 76px;
  border-radius: 16px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-weight: 700;
  font-size: 1.1rem;
  margin-bottom: 10px;
  box-shadow: inset 0 1px 0 rgba(255,255,255,0.28), 0 2px 6px rgba(0,0,0,0.18);
  background: var(--surface-sunken);
  color: var(--text-muted);
}
.color-warning {
  background: var(--tax-note-bg);
  border-left: 4px solid var(--tax-note-border);
  color: var(--text);
  padding: 8px 10px;
  margin: 0 0 10px;
  font-size: 0.9em;
  border-radius: 0 6px 6px 0;
}
.color-picker input[type="color"] {
  width: 64px; height: 44px; padding: 2px;
  border: 1px solid var(--border); border-radius: 10px;
  background: var(--surface); cursor: pointer;
}
.color-picker .bubble { min-height: 44px; padding: 8px 16px; font-size: 0.95rem; }
.color-picker .bubble-grid .bubble { min-height: 54px; font-size: 0.85rem; }
.color-picker input[type="range"] { accent-color: var(--accent); }
.color-picker .single-stop { gap: 0; }

/* Confirmation that a drink is on the tab. Fixed to the top so it is
   nowhere near the thumb that just tapped the cart, and green because it
   is the one banner in the app that means everything went right. */
.added-banner {
  position: fixed;
  top: 0; left: 0; right: 0;
  z-index: 120;
  background: #1d7a4c;
  color: #fff;
  font-size: 1.1rem;
  font-weight: 600;
  text-align: center;
  padding: 14px 16px;
  box-shadow: 0 2px 10px rgba(0,0,0,0.3);
  transform: translateY(-100%);
  transition: transform 0.22s ease;
}
.added-banner.show { transform: translateY(0); }

/* The quantity stepper that opens under a drink. One row: minus, count,
   plus, running total, cart. Sized so all five are comfortable thumb
   targets side by side even on a narrow phone. */
.qty-row {
  display: flex;
  align-items: center;
  /* Wraps so the "who is this for" row on a DB tab drops underneath the
     stepper rather than squeezing it. */
  flex-wrap: wrap;
  gap: 8px;
  margin: -2px 0 10px;
  padding: 8px 10px;
  background: var(--surface-sunken);
  border-radius: 14px;
}
.qty-step {
  margin: 0;
  min-width: 52px; min-height: 52px;
  font-size: 1.5rem; font-weight: 700; line-height: 1;
  border-radius: 12px;
}
.qty-count {
  min-width: 42px; text-align: center;
  font-size: 1.35rem; font-weight: 700;
}
.qty-total { flex: 1; text-align: right; font-size: 1.05rem; color: var(--text-muted); }
.qty-cart {
  margin: 0;
  min-width: 60px; min-height: 52px;
  font-size: 1.35rem; line-height: 1;
  border-radius: 12px;
}
.menu-item-btn.qty-open {
  outline: 2px solid var(--accent);
  outline-offset: 1px;
}

/* ---------------------------------------------------------------------
   Drink bubbles
   ---------------------------------------------------------------------
   Color-coded by category and sized for a thumb. On a phone they fill
   the width in an even grid rather than wrapping raggedly, so the target
   is as big as the screen allows and the row never leaves an awkward
   half-width gap. */
.bubble-row.bubble-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
  gap: 10px;
}
@media (max-width: 560px) {
  .bubble-row.bubble-grid { grid-template-columns: repeat(2, 1fr); gap: 8px; }
}

.bubble-grid .bubble {
  width: 100%;
  min-height: 84px;          /* a comfortable thumb target */
  margin: 0;
  padding: 12px 10px;
  border-radius: 18px;
  font-size: 1.05rem;
  line-height: 1.25;
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  word-break: break-word;
  border-width: 2px;
  /* The color is the point -- a colored bubble should not also be
     fighting a border in a different color. */
  border-color: transparent;
  box-shadow: var(--shadow-row);
}
.bubble-grid .bubble.is-colored { font-weight: 700; }
.bubble-grid .bubble.is-colored:hover { filter: brightness(1.08); }
.bubble-grid .bubble.selected { outline: 3px solid var(--text); outline-offset: 2px; }

/* The one color that is not a color. Kept as its own class rather than
   an inline style so the gradient lives with the rest of the theme. */
.bubble.color-rainbow {
  background: linear-gradient(145deg,
    #e53935 0%, #fb8c00 18%, #fdd835 36%, #43a047 54%, #1e88e5 72%, #8e24aa 100%);
  color: #fff;
  text-shadow: 0 1px 3px rgba(0,0,0,0.55);
}

/* Colored bubbles carry their own gradient (see gradientFrom) plus a soft
   inner highlight along the top edge, which is what stops a large flat
   shape looking like a printed swatch. */
.bubble-grid .bubble.is-colored {
  box-shadow: inset 0 1px 0 rgba(255,255,255,0.28), 0 2px 6px rgba(0,0,0,0.18);
  border: none;
  transition: transform 0.08s ease, filter 0.12s ease;
}
.bubble-grid .bubble.is-colored:active { transform: scale(0.97); }

/* A white or very pale bubble needs an edge or it vanishes into the card
   behind it, especially in light mode. */
.bubble-grid .bubble.is-pale { border-color: var(--border); }

/* The item buttons under a bottle: same generous target, full width. */
.menu-item-btn {
  display: block; width: 100%; text-align: left; margin-bottom: 8px;
  min-height: 60px; font-size: 1.05rem; border-radius: 14px;
}


.totals { background: var(--surface); border-radius: 12px; padding: 16px; margin-top: 10px; font-size: 1.15rem; }
.totals > div { display: flex; justify-content: space-between; padding: 6px 0; }
.totals .total-line { font-weight: bold; border-top: 1px solid var(--border-soft); margin-top: 6px; padding-top: 10px; font-size: 1.3rem; }
.totals .discount-line { color: var(--danger); }

.menu-panel .category { margin-bottom: 14px; }
.menu-panel .category h4 { margin: 6px 0; color: var(--text-muted); }
.menu-panel .category-item-header { margin-bottom: 10px; }
/* The way back out of a category, inverted so it stands out from the
   drinks around it: the page's ink as its fill, the page as its text,
   in both themes. */
.menu-panel .category-item-header > button {
  background: var(--text);
  color: var(--surface);
  border-color: var(--text);
  font-weight: 700;
}
.menu-panel .category-item-header > button:hover { background: var(--text); opacity: 0.88; }
.menu-panel .category-item-header h4 { margin: 0; }


/* Inline magnitude bars + week heatmap in the Traffic & Stay Time report */
.bar {
  display: inline-block;
  height: 10px;
  border-radius: 5px;
  background: var(--accent);
  opacity: 0.35;
  vertical-align: middle;
  margin-left: 6px;
}
.admin-table.heatmap { font-size: 0.85rem; }
.admin-table.heatmap th, .admin-table.heatmap td { padding: 6px 4px; }
.admin-table.heatmap td:first-child { white-space: nowrap; padding-right: 10px; }

.badge {
  padding: 4px 10px; border-radius: 999px;
  background: var(--badge-bg); color: var(--badge-text); font-size: 0.85em;
}
.badge.Paid, .badge.PAID { background: var(--badge-paid-bg); color: var(--badge-paid-text); }
.badge.Open, .badge.OPEN { background: var(--badge-open-bg); color: var(--badge-open-text); }
.badge.Voided, .badge.Refunded { background: var(--badge-void-bg); color: var(--badge-void-text); }
/* A website or social-media sign-up nobody at RJ has met yet. Same
   amber as an open tab: something still to be settled. */
.badge.Unverified { background: var(--badge-open-bg); color: var(--badge-open-text); margin-left: 6px; }

/* --- Where the bill stands --------------------------------------------
   Colour-coded and lit, because the single most expensive mistake on a
   busy night is closing a tab that still has money on it, or chasing one
   that is already settled. Blue is live, yellow is part paid, green is
   done, red is money owed from a night that has already closed. The two
   dead ends -- cancelled and refunded -- are grey, so red never has to
   compete with something nobody needs to act on.

   The glow is the point. A flat pill washes out under bar lights at a
   glance; a lit one does not. It is a box-shadow rather than a filter so
   the text inside stays sharp. */
.status-chip {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 8px 18px;
  border-radius: 999px;
  font-weight: 800;
  font-size: 0.95rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  background: var(--st-void-bg);
  color: var(--st-void-text);
  box-shadow: 0 0 0 2px rgba(var(--st-void-glow), 0.45),
              0 0 16px 2px rgba(var(--st-void-glow), 0.55);
}
/* A dot ahead of the word, so the colour is legible to someone who
   cannot tell red from green -- roughly one man in twelve. */
.status-chip::before {
  content: "";
  width: 9px; height: 9px;
  border-radius: 50%;
  background: currentColor;
  flex: none;
}
.status-chip.st-open {
  background: var(--st-open-bg); color: var(--st-open-text);
  box-shadow: 0 0 0 2px rgba(var(--st-open-glow), 0.45),
              0 0 16px 2px rgba(var(--st-open-glow), 0.55);
}
.status-chip.st-partial {
  background: var(--st-partial-bg); color: var(--st-partial-text);
  box-shadow: 0 0 0 2px rgba(var(--st-partial-glow), 0.45),
              0 0 18px 3px rgba(var(--st-partial-glow), 0.6);
}
.status-chip.st-paid {
  background: var(--st-paid-bg); color: var(--st-paid-text);
  box-shadow: 0 0 0 2px rgba(var(--st-paid-glow), 0.45),
              0 0 16px 2px rgba(var(--st-paid-glow), 0.55);
}
.status-chip.st-overdue {
  background: var(--st-overdue-bg); color: var(--st-overdue-text);
  box-shadow: 0 0 0 2px rgba(var(--st-overdue-glow), 0.55),
              0 0 22px 4px rgba(var(--st-overdue-glow), 0.7);
}
.status-chip.st-late {
  background: var(--st-late-bg); color: var(--st-late-text);
  box-shadow: 0 0 0 2px rgba(var(--st-late-glow), 0.5),
              0 0 18px 3px rgba(var(--st-late-glow), 0.6);
}
.status-chip.st-outstanding {
  background: var(--st-outstanding-bg); color: var(--st-outstanding-text);
  box-shadow: 0 0 0 2px rgba(var(--st-outstanding-glow), 0.45),
              0 0 16px 2px rgba(var(--st-outstanding-glow), 0.55);
}
.status-chip.st-void {
  background: var(--st-void-bg); color: var(--st-void-text);
  /* No glow. A cancelled tab is a dead end -- nothing is owed, nothing
     is to be done -- and lighting it up would put it in competition with
     the one state that does need somebody to act. */
  box-shadow: none;
}
.status-chip.st-refunded {
  background: var(--st-refunded-bg); color: var(--st-refunded-text);
  box-shadow: 0 0 0 2px rgba(var(--st-refunded-glow), 0.4);
}
.status-chip.st-partrefund {
  background: var(--st-partrefund-bg); color: var(--st-partrefund-text);
  box-shadow: 0 0 0 2px rgba(var(--st-partrefund-glow), 0.45),
              0 0 16px 2px rgba(var(--st-partrefund-glow), 0.55);
}

/* Money you are owed from a closed night is the one state worth
   interrupting somebody for, so it breathes. Anyone who has asked their
   device to stop moving things gets a still chip instead. */
@keyframes status-chip-pulse {
  0%, 100% { box-shadow: 0 0 0 2px rgba(var(--st-overdue-glow), 0.55),
                         0 0 18px 3px rgba(var(--st-overdue-glow), 0.6); }
  50%      { box-shadow: 0 0 0 3px rgba(var(--st-overdue-glow), 0.75),
                         0 0 30px 7px rgba(var(--st-overdue-glow), 0.85); }
}
@media (prefers-reduced-motion: no-preference) {
  .status-chip.st-overdue { animation: status-chip-pulse 2.2s ease-in-out infinite; }
}

/* Under the total on the bill screen: the big one, the whole point of
   the exercise. The top-bar copy stays small. */
.status-chip-row {
  display: flex;
  justify-content: center;
  margin: 12px 0 0;
}
.status-chip-row .status-chip { font-size: 1.15rem; padding: 12px 30px; }
.status-chip-row .status-chip::before { width: 12px; height: 12px; }

/* A glow is light bleeding onto paper it cannot reach. Printed, the chip
   is a plain outline. */
@media print {
  .status-chip { box-shadow: none !important; animation: none !important; }
}

.error { color: var(--danger); font-size: 0.9em; }

.admin-layout { display: grid; grid-template-columns: 160px 1fr; gap: 20px; }
/* A grid track is min-width:auto by default, which means it refuses to
   shrink below the widest thing inside it. The Inventory table is wider
   than an iPad held upright, so the column grew, the DOCUMENT grew with
   it, and the black bar -- which is only as wide as the viewport -- ended
   part way across. Measured at 768px wide: the page ran to 924px.
   min-width:0 lets the column shrink, and the pane below scrolls its own
   wide tables sideways instead of dragging the page along. */
.admin-content { min-width: 0; }
.admin-pane { overflow-x: auto; }
@media (max-width: 700px) { .admin-layout { grid-template-columns: 1fr; } }
.admin-nav { display: flex; flex-direction: column; gap: 4px; }

/* On a phone the fourteen sections become two columns of seven, so the
   whole list is on screen at once. One column meant scrolling past
   Bottle Keep to reach Settings, on the device this is actually used on.
   Two equal tracks rather than auto-fit: the point is exactly two, and
   the longest label ("GMO Reconciliation") must not be allowed to widen
   its column and squeeze the other. */
@media (max-width: 700px) {
  .admin-nav {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 6px;
    align-items: stretch;
  }
  .admin-nav .admin-tab {
    /* Centered, because a half-width bubble reads as a bubble; left
       aligned it reads as a list item that has lost its list. */
    text-align: center;
    padding: 10px 8px;
    font-size: 0.86rem;
    line-height: 1.2;
    /* Still a thumb target even at two up, and equal height across a row
       whether the label wraps to two lines or not. */
    min-height: 44px;
    display: flex;
    align-items: center;
    justify-content: center;
    /* "Zentoshin (Historical)" has nowhere to go in half a phone width
       but onto a second line. */
    overflow-wrap: anywhere;
    hyphens: auto;
  }
}

/* Folded away, the section list gives its column back to the content --
   the whole point on a phone, where the list otherwise sits on top of it. */
.admin-layout.nav-collapsed { grid-template-columns: 1fr; }
.admin-layout.nav-collapsed #admin-nav { display: none; }

.admin-navbar { display: flex; align-items: center; gap: 10px; margin: 0 0 14px; }
.admin-nav-toggle {
  background: var(--surface);
  border: 1px solid var(--border-faint);
  font-size: 1.1rem;
  line-height: 1;
  /* A thumb target, not a mouse target: this is used on a phone behind a
     bar, often one-handed. */
  min-width: 44px;
  min-height: 44px;
  padding: 0;
}
.admin-nav-toggle:hover { color: var(--accent); }
.admin-nav-current { font-weight: 600; color: var(--text-muted); }
.admin-nav .admin-tab { text-align: left; background: var(--surface); border: none; }
.admin-nav .admin-tab.active { background: var(--accent); color: var(--accent-text); }
.admin-pane.hidden { display: none; }
.report-tab.active { background: var(--accent); color: var(--accent-text); border-color: var(--accent); }

/* The report bubbles, all one size. A flex row sized each one to its own
   label, so "Taxes" was a stub next to "Staff Performance" and the row
   read as a ragged mess rather than a set of equal choices. A grid of
   equal tracks fixes the width without touching the type: the longest
   label decides the track, the rest match it, and a label that still does
   not fit wraps to a second line inside its own bubble rather than
   stretching it. */
#report-nav {
  display: grid !important;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: 8px;
  align-items: stretch;
}
#report-nav .report-tab {
  width: 100%;
  margin: 0;
  min-height: 46px;
  padding: 8px 10px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  line-height: 1.2;
  /* Long names are allowed to wrap rather than force every other bubble
     wider; the type size is left alone. */
  white-space: normal;
  overflow-wrap: anywhere;
}
/* The line between Analysis / Dashboard / Revenue and the rest (A to Z). */
#report-nav .report-divider { grid-column: 1 / -1; height: 0; margin: 4px 0; border-top: 1px solid var(--border); }
@media (max-width: 560px) {
  #report-nav { grid-template-columns: repeat(auto-fit, minmax(112px, 1fr)); }
  #report-nav .report-tab { min-height: 44px; font-size: 0.86rem; }
}
.report-pane.hidden { display: none; }

/* Printable receipt: hidden on screen, shown (and everything else hidden)
   when printing. Forced to black-on-white whatever the screen theme is --
   a receipt printer has no dark mode, and printing a dark page wastes
   toner and comes out gray. */
#receipt-print-area { display: none; }
#receipt-print-area .receipt {
  width: 320px; margin: 0 auto; font-family: monospace; font-size: 14px; line-height: 1.5;
}
#receipt-print-area .receipt .center { text-align: center; }
#receipt-print-area .receipt hr { border: none; border-top: 1px dashed #000; margin: 8px 0; }
#receipt-print-area .receipt .line { display: flex; justify-content: space-between; }
#receipt-print-area .receipt .big { font-size: 1.2em; font-weight: bold; }
@media print {
  /* !important rather than a more specific selector on purpose. The dark
     rules above are :root[data-theme="dark"] and :root:not([data-theme="light"]),
     both of which out-specify a bare :root -- so without this, printing
     from a dark phone on "Auto" would come out with a black background.
     Printing always wins, and saying so bluntly is clearer than an
     arms race of selectors that the next person has to decode. */
  :root {
    color-scheme: light !important;
    --bg: #fff !important; --surface: #fff !important; --surface-hover: #fff !important;
    --surface-sunken: #f0f0f0 !important; --surface-alt: #f5f5f5 !important;
    --text: #000 !important; --text-muted: #444 !important; --text-faint: #666 !important;
    --border: #999 !important; --border-soft: #bbb !important; --border-faint: #ddd !important;
    --accent: #000 !important; --accent-text: #fff !important;
    --danger: #a00 !important; --danger-fill: #a00 !important; --danger-muted: #a00 !important;
    --shadow-card: none !important; --shadow-row: none !important;
    --badge-bg: #eee !important; --badge-text: #000 !important;
    --badge-paid-bg: #e6f4ea !important; --badge-paid-text: #14532d !important;
    --badge-open-bg: #fff6d8 !important; --badge-open-text: #6b4e00 !important;
    --badge-void-bg: #fbe4e4 !important; --badge-void-text: #6b1111 !important;
    --badge-db-bg: #efe6fb !important; --badge-db-text: #3f2360 !important;
    --perm-yes: #14532d !important; --perm-no: #666 !important;
    --tax-note-bg: #fff !important; --tax-note-border: #999 !important;
    --positive: #14532d !important;
  }
  body { background: #fff !important; color: #000 !important; }
  body > *:not(#receipt-print-area) { display: none !important; }
  #receipt-print-area { display: block !important; }
}

/* Printing a report (the payout slips and the tax sheet) rather than a
   receipt: there is no #receipt-print-area content, so the rule above
   would blank the page. This brings the screen back for those. */
@media print {
  body.printing-report > *:not(#receipt-print-area) { display: revert !important; }
  body.printing-report #receipt-print-area { display: none !important; }
  body.printing-report #topbar,
  body.printing-report .admin-nav,
  body.printing-report button { display: none !important; }
}

.admin-table {
  width: 100%;
  /* Separate borders, no spacing, not collapse: a collapsed border belongs
     to the table and stays behind when a cell sticks (the sticky header
     row and first column, further down), leaving a see-through line along
     the stuck cells. Each cell carries its own borders; the lines are the
     same. */
  border-collapse: separate; border-spacing: 0;
  background: var(--surface); color: var(--text);
  border-radius: 8px; overflow: hidden;
}
.admin-table th, .admin-table td { text-align: left; padding: 8px 10px; border-bottom: 1px solid var(--border-faint); font-size: 0.92em; }
/* One header style for every table in the back office (the owner,
   2026-10-12): boxed cells that stand off the table, in every tab. */
.admin-table thead th {
  background: var(--surface-sunken); color: var(--text); font-weight: 700;
  border: 1px solid var(--border-soft); border-bottom-width: 2px;
}
.admin-table thead th + th { border-left: 0; }
.admin-table thead tr:first-child th:first-child { border-top-left-radius: 8px; }
.admin-table thead tr:first-child th:last-child { border-top-right-radius: 8px; }
.admin-table th { background: var(--surface-sunken); }
.admin-table button { padding: 4px 10px; font-size: 0.85em; margin: 0 2px; }
.supplier-row, .unit-row, .category-row {
  background: var(--surface); border-radius: 8px; padding: 8px 12px;
  display: flex; justify-content: space-between; align-items: center;
  gap: 10px;
}
/* Name on the left, buttons pinned to the right edge. The buttons' box
   used to be as wide as whatever the name left over, so on every row they
   started somewhere different and wrapped differently. Now the name takes
   the slack and the buttons keep their own width, in one line, flush
   right -- the same on every row of every list that uses this. */
.supplier-row > :first-child, .unit-row > :first-child, .category-row > :first-child {
  flex: 1 1 auto;
  min-width: 0;
}
.supplier-row > :last-child, .unit-row > :last-child, .category-row > :last-child {
  flex: 0 0 auto;
  margin-left: auto;
  display: flex;
  flex-wrap: nowrap;
  justify-content: flex-end;
  align-items: center;
  gap: 6px;
}
.supplier-row > :last-child > button, .unit-row > :last-child > button,
.category-row > :last-child > button { margin: 0; }
@media (max-width: 560px) {
  /* On a phone the name gets the whole line and the buttons sit under it,
     still flush right, a little smaller so all of them fit on one line. */
  .supplier-row, .unit-row, .category-row { flex-wrap: wrap; row-gap: 6px; }
  .supplier-row > :first-child, .unit-row > :first-child, .category-row > :first-child {
    flex-basis: 100%;
  }
  .supplier-row > :last-child > button, .unit-row > :last-child > button,
  .category-row > :last-child > button {
    min-height: 38px;
    padding: 6px 14px;
    font-size: 0.92rem;
  }
}
.perm-yes { color: var(--perm-yes); font-weight: bold; }
.perm-no { color: var(--perm-no); }

#toast {
  position: fixed; bottom: 20px; left: 50%; transform: translateX(-50%) translateY(20px);
  background: var(--accent); color: var(--accent-text); padding: 12px 20px; border-radius: 8px;
  opacity: 0; pointer-events: none; transition: opacity 0.2s, transform 0.2s;
  z-index: 100; max-width: 90vw;
}
#toast.show { opacity: 1; transform: translateX(-50%) translateY(0); }
#toast.error { background: var(--danger-fill); color: var(--danger-text); }

#modal-backdrop {
  position: fixed; inset: 0; background: var(--backdrop);
  display: flex; align-items: center; justify-content: center; z-index: 50;
}
#modal { min-width: 320px; max-width: 560px; max-height: 88vh; overflow-y: auto; }
#modal .field { margin-bottom: 10px; }
#modal .field label { display: block; font-size: 0.85em; color: var(--text-muted); margin-bottom: 4px; }
#modal .field input, #modal .field select {
  width: 100%; padding: 8px; border: 1px solid var(--border); border-radius: 6px;
  background: var(--surface); color: var(--text);
}
/* ...but not a tick-box. The rule above stretched every checkbox in a
   modal to the full width of the field, pushing its own label onto the
   next line -- so "Active" and "Earns drink-back" read as a floating
   square above their text. Needs this specificity to beat it. */
#modal .field input[type="checkbox"], #modal .field input[type="radio"] {
  width: auto; margin: 0 6px 0 0; vertical-align: middle;
}
#modal .field label > input[type="checkbox"] { margin-right: 8px; }

/* A table row that opens a detail view when clicked (drink-back payout). */
.admin-table tr.clickable { cursor: pointer; }
.admin-table tr.clickable:hover { background: var(--surface-alt); }

/* DB tab badge and the small "remove" control on a DB client bubble. */
.badge.DbTab { background: var(--badge-db-bg); color: var(--badge-db-text); }
.link-btn {
  background: none; border: none; color: inherit; cursor: pointer;
  font-size: 1.1em; line-height: 1; padding: 0 0 0 6px;
}
.link-btn:hover { opacity: 0.7; }
.link-btn:disabled { opacity: 0.35; cursor: default; }

/* Required / Optional marker beside every field label. Muted and italic so
   it reads as an annotation on the label rather than part of the name. */
.req, .opt { font-style: italic; font-weight: normal; font-size: 0.85em; }
.req { color: var(--danger-muted); }
.opt { color: var(--text-faint); }

/* Unit and supplier rows: name on the left, actions on the right. */
.unit-row, .supplier-row { display: flex; justify-content: space-between; align-items: center; gap: 12px; }

/* The bottle question at checkout. Two big answers, because it is asked
   with a customer standing at the till and a phone in one hand. */
.bottle-q-name {
  font-size: 1.25rem;
  font-weight: 700;
  margin: 4px 0 8px;
}
.bottle-q-owner { color: var(--text-muted); margin: 0 0 12px; }
#modal .big-btn { width: 100%; margin-bottom: 8px; min-height: 52px; font-size: 1.05rem; }

/* Bottle sets: the toggle in the add-to-cart row, and the bottle picker. */
.qty-set {
  min-height: 44px;
  padding: 6px 12px;
  font-size: 0.85rem;
  white-space: nowrap;
}
.qty-set.selected {
  background: var(--accent);
  color: #fff;
  border-color: var(--accent);
}
.bottle-source {
  display: block;
  width: 100%;
  text-align: left;
  padding: 12px 14px;
  margin-bottom: 8px;
  min-height: 56px;
}
.bottle-source-name { display: block; font-weight: 600; }
.bottle-source-sub,
.bottle-source-note { display: block; font-size: 0.85em; color: var(--text-muted); }
.bottle-source-note { font-weight: 600; }
/* First in the list on purpose: at 1am "I can't find it" must be easier to
   press than guessing at the wrong bottle. */
.bottle-source.cant-find {
  border-left: 4px solid var(--warn-mid);
  margin-bottom: 14px;
}

/* Past its date, in any of the bottle tables. */
.admin-table td.overdue { color: var(--danger); font-weight: 600; }

/* Category filter above the menu items table. Smaller than an order-screen
   bubble -- this is a back-office control, not something tapped mid-service
   -- and it wraps, because a bar with twelve categories should not push the
   table off the bottom of the screen. */
.menu-filter {
  margin: 1px 0 9px;
  /* The selected chip's outline (2px plus a 1px offset) reaches 3px past
     the chip. Without this inset the "All" chip at the very left was
     clipped by the pane's edge (the owner, 2026-10-14, iPhone). */
  padding: 3px;
  gap: 6px;
  flex-wrap: wrap;
  /* The admin table is wider than a phone, which stretches this pane with
     it. Left alone, the chips would wrap at the TABLE's width and the last
     few would sit off-screen. Wrapping at the viewport instead keeps them
     all reachable, and sticking to the left keeps them in view when the
     table beneath is scrolled sideways. */
  max-width: calc(100vw - 32px);
  position: sticky;
  left: 0;
}
.menu-filter-chip {
  min-height: 38px;
  padding: 6px 14px;
  font-size: 0.9rem;
  opacity: 0.55;
}
.menu-filter-chip.selected {
  opacity: 1;
  outline: 2px solid var(--accent);
  outline-offset: 1px;
}

/* --- Folding lists (Inventory: purchases, stock, inactive stock) --------
   Closed by default; the heading carries the count, so a closed list still
   says how much is in it. Sized like the h3 headings they replace. */
.fold-section { margin-top: 10px; }
.fold-section > summary {
  cursor: pointer;
  font-weight: 700;
  font-size: 1.17em;
  padding: 8px 0;
  list-style-position: inside;
  /* Same reason as .menu-filter: the pane is as wide as its widest table,
     and the heading should stay in view when the table is scrolled. */
  max-width: calc(100vw - 32px);
  position: sticky;
  left: 0;
}
.fold-section > summary:hover { color: var(--accent); }
.fold-tools { gap: 8px; flex-wrap: nowrap; max-width: calc(100vw - 32px); position: sticky; left: 0; }
.fold-search {
  width: 100%;
  max-width: min(420px, calc(100vw - 32px));
  padding: 8px 12px;
  margin: 6px 0 10px;
  border: 1px solid var(--border);
  border-radius: 999px;
  background: var(--surface);
  color: var(--text);
  position: sticky;
  left: 0;
}
.fold-tools .fold-search { margin: 6px 0; flex: 1 1 auto; min-width: 0; }
.fold-tools button { flex: 0 0 auto; margin: 0; }

/* Inventory: the Bar / Kitchen switch, and the equipment lists under it. */
.area-switch {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 8px;
  max-width: min(420px, calc(100vw - 32px));
  margin: 4px 0 14px;
  position: sticky;
  left: 0;
}
.area-switch .bubble { margin: 0; min-height: 46px; font-size: 1.05rem; }
.equip-short-mark { color: var(--danger); font-weight: 700; }
.equip-ok-mark { color: var(--positive); }
.equip-note { color: var(--text-muted); font-size: 0.82em; font-weight: 400; }
.equip-is-off { opacity: 0.6; }
.equip-change-down { color: var(--danger); }
.equip-change-up { color: var(--positive); }

/* "Show all (N)" under a table cut to its newest rows. */
.show-more { margin: 8px 0 4px; min-height: 38px; padding: 6px 16px; font-size: 0.92rem; }

/* The stock list's category bubbles: the menu's own colours (painted by
   the same bubbleColorStyle), but a fraction of the drink bubbles' height
   -- they are a filter here, not the thing being tapped all night. */
#inventory-filter.inventory-cat-grid {
  grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
  gap: 6px;
  margin: 4px 0 10px;
  max-width: calc(100vw - 32px);
  position: sticky;
  left: 0;
  /* Room for the selected bubble's outline (3px + 2px offset): flush to the
     edge, the scrolling table's box cut it off on the left (2026-10-19). */
  padding: 5px;
  box-sizing: border-box;
}
#inventory-filter.inventory-cat-grid .bubble {
  min-height: 40px;
  padding: 4px 6px;
  font-size: 0.85rem;
  line-height: 1.15;
  border-radius: 12px;
}
@media (max-width: 560px) {
  #inventory-filter.inventory-cat-grid { grid-template-columns: repeat(3, 1fr); }
  /* The search gets the whole width on a phone, the add button its own
     line under it: side by side, the box was too narrow to read. */
  .fold-tools { flex-wrap: wrap; }
  .fold-tools .fold-search { flex-basis: 100%; }
}

/* --- Tables that become cards on a phone (.stack-on-phone) --------------
   Labels come from the headers (labelStackedTable in app.js). The first
   cell -- the person, the date -- heads the card; the buttons sit along its
   foot, flush right. Wider screens keep the ordinary table. */
@media (max-width: 560px) {
  table.stack-on-phone, table.stack-on-phone tbody { display: block; width: 100%; }
  table.stack-on-phone thead { display: none; }
  /* A table that can be sorted keeps its sortable headers, as a row of
     small pills above the cards -- otherwise a phone could not sort it. */
  table.stack-on-phone thead:has(th.sortable) { display: block; }
  table.stack-on-phone thead:has(th.sortable) tr {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    padding: 8px 12px;
    border-bottom: 1px solid var(--border-faint);
  }
  table.stack-on-phone thead th { display: none; }
  table.stack-on-phone thead th.sortable {
    display: inline-block;
    padding: 4px 10px;
    border: 1px solid var(--border);
    border-radius: 999px;
    font-size: 0.78rem;
    background: var(--surface);
  }
  table.stack-on-phone thead th.sortable.sorted { border-color: var(--accent); }
  table.stack-on-phone tr {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: 6px 12px;
    padding: 10px 12px;
    border-bottom: 1px solid var(--border-faint);
  }
  /* Four short numbers per row (equipment): one line of them, not two. */
  table.stack-on-phone.stack-4 tr { grid-template-columns: repeat(4, minmax(0, 1fr)); }
  table.stack-on-phone td {
    padding: 0;
    border: 0;
    min-width: 0;
    overflow-wrap: anywhere;
  }
  table.stack-on-phone td[data-label]::before {
    content: attr(data-label);
    display: block;
    font-size: 0.72rem;
    color: var(--text-muted);
  }
  table.stack-on-phone td:first-child { grid-column: 1 / -1; font-weight: 600; }
  table.stack-on-phone td:first-child::before { display: none; }
  table.stack-on-phone td.stack-full { grid-column: 1 / -1; font-weight: 400; }
  table.stack-on-phone td.stack-actions {
    grid-column: 1 / -1;
    display: flex;
    flex-wrap: wrap;
    justify-content: flex-end;
    gap: 6px;
  }
  table.stack-on-phone td.stack-actions button { margin: 0; }
}

/* Sortable column headers on the menu items table. The arrow lives in a
   pseudo-element so it never re-flows the header text when it appears. */
.admin-table th.sortable {
  cursor: pointer;
  user-select: none;
  white-space: nowrap;
}
.admin-table th.sortable:hover { color: var(--accent); }
.admin-table th.sortable::after {
  content: attr(data-arrow);
  display: inline-block;
  width: 1em;
  font-size: 0.8em;
  color: var(--accent);
}
.admin-table th.sortable.sorted { color: var(--accent); }

/* The entrance dialog: one row per item the cover charge includes, with
   the top-up called out in red so nobody adds a 5,000 yen bottle to a
   1,500 yen allowance without noticing what it does to the tab. */
.entrance-row { align-items: center; gap: 8px; margin-bottom: 6px; }
.entrance-row select { flex: 1; min-width: 0; }
.entrance-over { color: var(--danger); font-size: 0.88em; white-space: nowrap; }
.entrance-covered { color: var(--text-muted); font-size: 0.88em; white-space: nowrap; }
.entrance-row .entrance-remove { padding: 4px 10px; }

/* Margins worth looking at twice. Color rather than a column of numbers,
   because the point of the report is to find the thin ones fast. */
.margin-thin { color: var(--danger); font-weight: 600; }
.margin-watch { color: #b8860b; font-weight: 600; }

/* A soft drink that cannot be a chaser because it is ticked as alcohol.
   Shown rather than hidden: an item that silently vanishes from a list
   reads as the POS refusing, not as a box to untick. */
.chaser-blocked td { opacity: 0.55; }

/* The Favorites bubble. First in the grid, because it is the one most
   taps end at. Its colour comes from the owner's own choice now, painted
   inline by bubbleColorStyle exactly as a category's is -- what is left
   here is the weight, and a gold fallback for the one case where no
   colour arrives at all (the favorites call failed), so the bubble is
   never a plain grey lump. Inline styles win over these, so a chosen
   colour always shows. */
.favorites-bubble {
  background: var(--accent);
  color: var(--accent-text);
  font-weight: 600;
}
.favorites-bubble:hover { filter: brightness(1.08); }

/* The same mark on the login screen, bigger -- it is the first thing
   anyone sees, and at title size the script has room to read. */
#login-brand {
  display: flex;
  align-items: baseline;
  /* Centred here and only here. The login card below it is already
     centred, so a left-aligned mark was the one thing on the screen not
     lining up. In the top bar the brand stays left, where it anchors the
     row of buttons on its right. */
  justify-content: center;
  gap: 12px;
  margin-bottom: 18px;
}
#login-brand .brand-rj { font-size: 3.4rem; line-height: 1.5; }
#login-brand { gap: 2px; }
#login-brand .brand-pos { font-size: 1rem; letter-spacing: 0.28em; }


/* --- Which table a tab is sitting at ------------------------------------
   The seat is the fastest way to tell two nameless tabs apart, so it is
   styled to be scanned rather than read: a gold chip on the card, and a
   bubble on the tab that becomes the seat once one is picked. */
/* How many people, reading back the count once it is known. Same
   treatment as the seat bubble beside it, so the row of little answers
   on the bill header reads as one set rather than three styles. */
.party-bubble.has-party {
  background: var(--accent);
  color: var(--accent-text);
  border-color: var(--accent);
  font-weight: 700;
  letter-spacing: 0.02em;
}
.table-bubble.has-table {
  background: var(--accent);
  color: var(--accent-text);
  border-color: var(--accent);
  font-weight: 700;
  letter-spacing: 0.02em;
}
.table-chip {
  display: inline-block;
  min-width: 34px;
  padding: 2px 8px;
  margin-right: 6px;
  border-radius: 999px;
  background: #c8a44d;
  color: #1c1c1c;
  font-weight: 700;
  font-size: 0.86rem;
  text-align: center;
  vertical-align: 2px;
}
/* The picker: one tap per seat, so they have to be big enough to hit
   without looking, and the current one has to be obvious. */
.table-pick {
  min-width: 56px;
  min-height: 44px;
  font-weight: 600;
}
.table-pick.active {
  background: var(--accent);
  color: var(--accent-text);
  border-color: var(--accent);
}


/* The little colour chip beside Favorites in the Categories list, so the
   current choice is visible without opening the picker. */
.bubble-swatch {
  display: inline-block;
  width: 26px;
  height: 26px;
  line-height: 26px;
  border-radius: 999px;
  text-align: center;
  font-size: 0.8rem;
  vertical-align: -7px;
  margin-right: 8px;
  border: 1px solid var(--border);
  background: var(--accent);
  color: var(--accent-text);
}


/* A group heading inside the Permissions table. It is a row, not a
   separate table, so the three columns stay in line down the whole
   screen -- a heading that broke the alignment would defeat the point.

   The heading carries a filled band and the group below it is inset, so
   "On the floor" reads as a section you are inside rather than as one
   more row of the same list. The gap above each group is a transparent
   spacer row drawn with border-top, which is the only way to put real
   air between <tbody> blocks without breaking the column alignment. */
.perm-group-row th {
  text-align: left;
  padding: 10px 12px;
  font-size: 0.8rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text);
  background: var(--surface-sunken);
  border-top: 18px solid transparent;
  border-bottom: 2px solid var(--accent);
  background-clip: padding-box;
}
/* No leading gap on the first group -- it would just push the whole
   table away from its own heading. */
.admin-table tbody:first-of-type .perm-group-row th { border-top-width: 0; }

/* The rows inside a group, stepped in from the heading above them. */
.perm-group-row ~ tr td:first-child,
.perm-group-row ~ tr th:first-child {
  padding-left: 26px;
}


/* A heading that folds. It is a real button inside the <h3> rather than a
   click handler on the heading itself, so it is reachable by keyboard and
   announces its open/shut state -- but it is styled back down to look like
   the heading it replaced, not like another button on a page full of
   them. */
.section-toggle {
  background: none;
  border: none;
  padding: 0;
  margin: 0;
  font: inherit;
  color: inherit;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  min-height: 0;
}
.section-toggle:hover { background: none; opacity: 0.8; }
.section-caret {
  display: inline-block;
  width: 1em;
  font-size: 0.8em;
  color: var(--text-muted);
}
.section-count {
  font-size: 0.72em;
  font-weight: 500;
  color: var(--text-muted);
}


/* A badge that is also a button. It has to keep looking like the badge it
   is -- the tab type is read far more often than it is changed -- so this
   only adds the affordance: a pointer, and a hint of a border that says
   it can be pressed. */
.badge.badge-button {
  cursor: pointer;
  border: 1px dashed currentColor;
}
.badge.badge-button:hover { filter: brightness(1.1); }
.tab-type-pick { min-width: 110px; min-height: 44px; font-weight: 600; }
.tab-type-pick.active {
  background: var(--accent);
  color: var(--accent-text);
  border-color: var(--accent);
}


/* Which cover is being rung, when the bar has more than one kind. Two
   lines per bubble -- the name and what it costs -- because "Event" on
   its own does not tell you whether you are about to charge 3,000 or
   5,000. */
.entrance-charge-pick {
  min-width: 120px;
  min-height: 52px;
  line-height: 1.25;
  font-weight: 600;
}
.entrance-charge-pick small { font-weight: 500; opacity: 0.8; }
.entrance-charge-pick.active {
  background: var(--accent);
  color: var(--accent-text);
  border-color: var(--accent);
}


/* A customer's code. Monospaced and tracked out, because it is read
   character by character and said out loud -- GN01 has to be
   unmistakeable from GNO1 at a glance on a busy floor. */
.client-code {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  letter-spacing: 0.06em;
  font-weight: 600;
  color: var(--text-muted);
}


/* Complimentary and Cover Charge: two buttons of the same weight, so they
   read as the two exceptions to ringing an ordinary drink rather than as
   one being the bigger decision. Equal tracks, and they wrap to one
   column only when a phone genuinely cannot fit two. */
/* The three modifiers above the drinks: Credit to, Complimentary, Cover
   Charge. Always three across, even on a phone -- the point of the row is
   that it costs one line instead of three, and letting them wrap would
   give the height straight back. They shrink instead, which is why the
   caption is small and the value sits under it. */
.tab-action-trio {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 8px;
  margin: 0 0 12px;
}
.tab-action-trio > .bubble {
  width: 100%;
  margin: 0;
  min-height: 56px;
  padding: 6px 8px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 2px;
  text-align: center;
  line-height: 1.15;
  white-space: normal;
}
.tab-action-trio .bubble-cap {
  font-size: 0.78em;
  opacity: 0.75;
  letter-spacing: 0.01em;
}
.tab-action-trio .bubble-value {
  font-weight: 600;
  font-size: 0.95em;
  /* A long staff name must not push the bubble wider than its third. */
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* Credit to is only "set" when a person is actually chosen -- Default is
   the resting state and should not look like a decision was made. */
#credit-bubble.has-value .bubble-value { text-decoration: underline; }

/* The staff picker behind the Credit to bubble. Same grid as the drinks,
   so picking a person feels like picking a drink. */
.staff-pick-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(130px, 1fr));
  gap: 8px;
  margin: 10px 0 16px;
}
.staff-pick-grid > .bubble {
  width: 100%;
  margin: 0;
  min-height: 52px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  line-height: 1.2;
  white-space: normal;
}

.tab-action-pair {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  gap: 8px;
  margin: 10px 0;
}
.tab-action-pair > .bubble {
  width: 100%;
  margin: 0;
  min-height: 52px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  line-height: 1.2;
  white-space: normal;
  overflow-wrap: anywhere;
}


/* Picking what the cover includes. The categories are the same coloured
   bubbles as the order screen, so the gesture is the one staff already
   know rather than a dropdown they have to read. */
.entrance-cat { min-width: 120px; min-height: 52px; font-weight: 600; }
.entrance-item {
  min-width: 130px;
  min-height: 56px;
  line-height: 1.25;
  font-weight: 600;
  /* Name over price, stacked properly -- a <br> left the two running
     together once the name wrapped. */
  display: inline-flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 2px;
  padding: 8px 12px;
  white-space: normal;
}
.entrance-item small { font-weight: 500; opacity: 0.8; }
/* What has been chosen, above the picker: the drink and what it costs. */
.entrance-chosen { font-weight: 600; }
.entrance-chosen small { font-weight: 500; color: var(--text-muted); margin-left: 6px; }

/* DB page: the paid / part paid / unpaid chip on every DB sale. It reuses
   the existing badge tokens rather than inventing colors -- paid is the
   same green as a paid bill, unpaid the same red as a voided one -- so it
   inherits both themes for free and matches what staff already read
   elsewhere. The word is always there as well as the color. */
.db-status {
  display: inline-block;
  padding: 2px 10px;
  border-radius: 999px;
  font-size: 0.85em;
  font-weight: 700;
  white-space: nowrap;
  background: var(--badge-bg);
  color: var(--badge-text);
}
.db-status-paid { background: var(--badge-paid-bg); color: var(--badge-paid-text); }
.db-status-part { background: var(--badge-open-bg); color: var(--badge-open-text); }
.db-status-unpaid { background: var(--badge-void-bg); color: var(--badge-void-text); }

/* Charges: the cover, table and event charges on their own screen. The
   row itself reuses .category-row; these are only the bits a charge has
   that a category does not. */
.charge-price { color: var(--text-muted); margin-left: 8px; }
.charge-inactive { opacity: 0.55; }

/* The all-you-can-drink clock on a tab.
   Three states, because they are three different conversations: plenty
   of time left (quiet), nearly up (amber, go and ask for a last round),
   over (red, it is costing the bar money every minute nobody notices).
   The colours come from the same warning tokens the expiry banner uses,
   so the whole app agrees on what "nearly" and "now" look like. */
.ff-banner {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  margin: 8px 0;
  padding: 10px 14px;
  border-radius: 12px;
  background: var(--card);
  border: 2px solid var(--border-soft);
}
.ff-banner.ff-warning { border-color: var(--warn-mid); }
.ff-banner.ff-expired { border-color: var(--warn-high); }
.ff-banner-main { display: flex; align-items: baseline; gap: 10px; min-width: 0; }
.ff-clock {
  font-weight: 800;
  font-size: 1.35rem;
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.01em;
}
.ff-banner.ff-warning .ff-clock { color: var(--warn-mid); }
.ff-banner.ff-expired .ff-clock { color: var(--warn-high); }
.ff-text { color: var(--text-muted); font-size: 0.95em; }
.ff-banner.ff-expired .ff-text { color: var(--warn-high); font-weight: 600; }
.ff-banner.ff-ended { border-style: dashed; }
.ff-banner.ff-ended .ff-clock { color: var(--text-muted); }
.ff-banner-actions { display: flex; gap: 8px; flex-wrap: wrap; }
.ff-banner-actions button { min-height: 40px; }

/* The same clock on the tabs list, so the floor sees a table running out
   without opening it. */
.ff-chip {
  display: inline-block;
  padding: 2px 9px;
  border-radius: 999px;
  font-weight: 700;
  font-size: 0.82rem;
  font-variant-numeric: tabular-nums;
  background: var(--badge-open-bg);
  color: var(--badge-open-text);
}
.ff-chip.ff-warning { background: var(--warn-mid); color: #fff; }
.ff-chip.ff-expired { background: var(--warn-high); color: #fff; }

/* A line poured on a running free flow: it cost nothing, and the bill
   should say why rather than showing a bare zero. */
.ff-line-tag { color: var(--text-muted); font-size: 0.85em; }

/* A pinned charge. Gold, because it is a favourite rather than a warning,
   and it sits on both the Charges screen and the till's charge bubbles so
   the reason a charge is first is visible in both places. */
.charge-pin-mark {
  color: var(--badge-open-text);
  font-size: 0.95em;
}
/* On a selected bubble the fill goes dark (light theme) or light (dark
   theme) and a fixed gold stops being readable, so the star takes the
   button's own text colour there. */
.bubble.active .charge-pin-mark,
.bubble.selected .charge-pin-mark { color: inherit; }

/* The two count boxes side by side in the on-the-spot charge form:
   drinks and food, which are separate numbers and read as a pair. */
.charge-count-row {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 12px;
  margin-bottom: 10px;
}
.charge-count-row label {
  display: block;
  font-size: 0.9em;
}

/* The Custom bubble in the till's charge row. Outlined rather than
   filled: it is not one of the bar's charges, it is the thing that makes
   one, and it should not read as just another cover. */
.bubble.entrance-charge-custom {
  border-style: dashed;
}
.entrance-custom-note {
  color: var(--text-muted);
  font-size: 0.9em;
  margin: 0 0 10px;
}

/* The redeemable list in the charge editor. Scrolls on its own so a long
   menu never pushes the Save button off the bottom of the dialog. */
.charge-item-list {
  max-height: 260px;
  overflow-y: auto;
  border: 1px solid var(--border-soft);
  border-radius: 8px;
  padding: 6px 10px;
}
.charge-item-row {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 4px 0;
}
.charge-item-row small { color: var(--text-muted); margin-left: auto; }

/* --- The redeemable picker's bubbles ------------------------------------
   A bottle carries two things: a name that can run long ("Ballantines 17y
   on the rocks") and a price that must never be split. Side by side in a
   row they fight over the same narrow bubble and the price loses, coming
   out broken across three lines. Stacked, the name gets the full width
   to wrap into and the price sits whole underneath it. Category bubbles use the
   same shape so the two steps of the picker look like one thing. */
.bubble-grid .bubble.charge-cat,
.bubble-grid .bubble.charge-item-pick {
  flex-direction: column;
  justify-content: center;
  gap: 4px;
  min-height: 92px;
  padding: 12px 10px;
  line-height: 1.2;
}
.charge-cat > span,
.charge-item-pick > span {
  max-width: 100%;
  font-size: 1rem;
  font-weight: 700;
  /* Long words break rather than spilling past the rounded edge. */
  overflow-wrap: anywhere;
}
.charge-cat > small,
.charge-item-pick > small {
  font-size: 0.85rem;
  font-weight: 600;
  /* The whole point: a price is one token, never three lines. */
  white-space: nowrap;
  opacity: 0.85;
}

/* A complimentary reason that needs somebody's PIN. The padlock and the
   name together answer "whose" without having to open the editor. */
.comp-locked {
  color: var(--text-muted);
  font-size: 0.9em;
  margin-left: 6px;
}

/* "One more of these" on a bill line. Deliberately quiet next to the red
   Void beside it: adding a round is the common, harmless action and it
   should not shout louder than the one that takes money off. */
.btn-again {
  min-width: 34px;
  font-weight: 700;
  font-size: 1.05em;
  line-height: 1;
  padding: 4px 10px;
}

/* "They have a bottle in the back." Deliberately loud and deliberately
   sticky: it stays until somebody dismisses it, because a bottle nobody
   remembers is a bottle the customer buys a second time. */
/* --- The payment log behind an unpaid bill ----------------------------
   Four columns: when, how, who took it, how much. Refunds are in the list
   and struck through rather than netted out of the total, because a
   figure that does not add up in front of a customer is worse than a
   longer list. */
.unpaid-log-row > td { background: var(--surface-sunken); padding: 0; }
.unpaid-log { padding: 12px 16px 14px; }
.unpaid-log-head {
  font-weight: 700;
  margin-bottom: 8px;
  color: var(--text-muted);
  font-size: 0.9rem;
}
.unpaid-log-line {
  display: grid;
  grid-template-columns: 1fr 1fr 1fr auto;
  gap: 10px;
  padding: 6px 0;
  border-bottom: 1px solid var(--border-faint);
  font-size: 0.95rem;
}
.unpaid-log-line.is-refund { color: var(--danger); }
.unpaid-log-sum {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: 4px 16px;
  margin-top: 10px;
  padding-top: 8px;
  border-top: 2px solid var(--border);
  font-size: 1rem;
}
.unpaid-log-sum > :nth-child(even) { text-align: right; }
@media (max-width: 560px) {
  .unpaid-log-line { grid-template-columns: 1fr auto; }
}

/* --- This customer already owes you money ----------------------------
   The loudest thing on the tab screen, and the only banner that is red.
   It goes up when a name is put on a tab and that person has an unpaid
   bill from a night that has already closed -- which is the moment
   somebody can still be asked to settle it. */
.debt-banner {
  display: flex;
  align-items: stretch;
  gap: 0;
  margin: 12px 0;
  border-radius: 10px;
  overflow: hidden;
  background: var(--st-overdue-bg);
  color: var(--st-overdue-text);
  box-shadow: 0 0 0 2px rgba(var(--st-overdue-glow), 0.5),
              0 0 20px 3px rgba(var(--st-overdue-glow), 0.55);
}
.debt-banner-body {
  flex: 1;
  text-align: left;
  background: none;
  border: none;
  color: inherit;
  font-size: 1.05rem;
  font-weight: 700;
  padding: 14px 16px;
  cursor: pointer;
  line-height: 1.35;
}
.debt-banner-body:hover { background: var(--on-fill-tint); }
.debt-banner-dismiss {
  background: none;
  border: none;
  color: inherit;
  font-size: 1.5rem;
  line-height: 1;
  padding: 0 16px;
  cursor: pointer;
  opacity: 0.85;
}
.debt-banner-dismiss:hover { background: var(--on-fill-tint); opacity: 1; }

.keep-banner {
  display: flex;
  align-items: stretch;
  gap: 0;
  margin: 12px 0;
  border-radius: 10px;
  overflow: hidden;
  background: var(--badge-db-bg);
  color: var(--badge-db-text);
  border: 2px solid var(--badge-db-text);
}
.keep-banner-body {
  flex: 1;
  text-align: left;
  background: none;
  border: none;
  color: inherit;
  font-size: 1rem;
  font-weight: 600;
  padding: 12px 14px;
  cursor: pointer;
  min-height: 48px;
}
.keep-banner-body:hover { background: var(--on-fill-tint); }
.keep-banner-dismiss {
  background: none;
  border: none;
  border-left: 1px solid var(--badge-db-text);
  color: inherit;
  font-size: 1.4rem;
  line-height: 1;
  padding: 0 16px;
  cursor: pointer;
}
.keep-banner-dismiss:hover { background: var(--on-fill-tint); }

/* A chaser bubble in the pour-from-keep picker. */
.keep-chaser small { display: block; color: var(--text-muted); font-weight: 500; }

/* Splitting a bill at closing. The note under the bubbles is the number
   staff read out loud, so it is given real size rather than being a hint. */
.split-note {
  margin: 6px 0 10px;
  font-weight: 600;
  min-height: 1.2em;
}
.split-paid {
  margin: 4px 0;
  color: var(--text-muted);
}
.split-custom { display: inline-flex; align-items: center; gap: 6px; }
.split-custom label { margin: 0; }
.split-custom input { width: 70px; }
.split-custom input#modal-split-amount { width: 96px; }

/* By-item sits apart from the others: it is the one that makes a second
   bill, so it does not look like another way to divide the same one. */
.split-by-item-row {
  margin: 4px 0 12px;
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
}
.split-by-item-row small { color: var(--text-muted); }

/* Money owed to the bar. Loud, because a tab nobody chases is a tab
   nobody gets paid for -- and dismissible, because at 7pm on a Friday it
   is not the thing to be arguing with. */
.unpaid-banner {
  display: flex;
  align-items: stretch;
  margin: 0 0 12px;
  border-radius: 10px;
  overflow: hidden;
  background: var(--badge-open-bg);
  color: var(--badge-open-text);
  border: 2px solid var(--badge-open-text);
}
.unpaid-banner-body {
  flex: 1;
  text-align: left;
  background: none;
  border: none;
  color: inherit;
  font-size: 1rem;
  font-weight: 600;
  padding: 12px 14px;
  cursor: pointer;
  min-height: 48px;
}
.unpaid-banner-body:hover { background: var(--on-fill-tint); }
.unpaid-banner-dismiss {
  background: none;
  border: none;
  border-left: 1px solid var(--badge-open-text);
  color: inherit;
  font-size: 1.4rem;
  line-height: 1;
  padding: 0 16px;
  cursor: pointer;
}
.unpaid-banner-dismiss:hover { background: var(--on-fill-tint); }

/* Did they come? Same colors as the unpaid banner, one row per reservation. */
.res-banner { flex-direction: column; gap: 6px; padding: 8px 12px; }
.res-ask { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 10px; }
.res-ask-text { flex: 1 1 200px; font-weight: 600; }
.res-ask-btns { display: flex; flex-wrap: wrap; gap: 6px; }
.res-ask-btns button, .sc-row .res-ask-btns button { min-height: 0; padding: 5px 12px; font-size: 0.85rem; margin: 0; }
.res-ask-more { background: none; border: none; color: inherit; font-weight: 600; text-decoration: underline;
  padding: 2px 0; cursor: pointer; min-height: 0; }
.res-answer { font-weight: 600; }
.bell-ask { margin: 10px 0 4px; }

/* A row past the escalation line. Tinted rather than reddened: the whole
   screen is money owed, so the distinction is "this one has gone on too
   long", not "this one is an error". */
.unpaid-escalated td { background: var(--badge-void-bg); }
.unpaid-estimate-note {
  background: var(--badge-open-bg);
  color: var(--badge-open-text);
  border-radius: 8px;
  padding: 8px 12px;
  margin: 8px 0;
}

/* --- Supplier prices and scheduled price changes ----------------------- */

.price-muted { color: var(--text-muted); font-size: 0.9em; }

.price-sub { color: var(--text-muted); margin: 0 0 12px; }

.price-help { color: var(--text-muted); font-size: 0.9em; margin: 4px 0 0; }

/* One scheduled change, listed under the supplier it belongs to. */
.price-sched { display: block; margin-bottom: 4px; }
.price-sched:last-child { margin-bottom: 0; }

/* Prices something proposed and nobody has approved yet. Borrowed from the
   unpaid banner's colours rather than new ones: both mean the same thing to
   whoever is looking at the back office -- this needs you. */
.price-pending-card {
  margin: 0 0 16px;
  padding: 14px 16px;
  border-radius: 10px;
  background: var(--badge-open-bg);
  color: var(--badge-open-text);
  border: 2px solid var(--badge-open-text);
}
.price-pending-card h3 { margin: 0 0 4px; }
.price-pending-card p { margin: 0 0 12px; }

/* The cost-basis column holds one short button; letting it wrap onto three
   lines made the row twice as tall as the ones beside it. */
.price-prefer { white-space: nowrap; }

/* --- The customer's copy ------------------------------------------------
   Handed across the table, so it is set larger and plainer than anything
   on the staff side: one column, generous line height, and no controls
   inside the sheet itself. */

/* While it is up the device belongs to the customer. The top bar and every
   staff banner come off, so a stray tap cannot reach the back office. */
body.client-view #topbar,
body.client-view #alert-banner,
body.client-view #bottleset-banner,
body.client-view #bottlekeep-banner,
body.client-view #expiry-banner,
body.client-view #unpaid-banner,
body.client-view #keep-banner,
/* Every staff banner, by kind, so a new one can never show on the
   customer's screen (the tasks banner did, 2026-10-14). */
body.client-view .notice-banner,
body.client-view .unpaid-banner,
body.client-view .pay-banner,
body.client-view .added-banner { display: none !important; }

.client-sheet {
  max-width: 520px;
  margin: 24px auto 0;
  padding: 28px 30px 24px;
  background: var(--surface);
  border-radius: 12px;
  box-shadow: var(--shadow-card);
  /* Read at arm's length, across a table, by somebody who has been in a
     dark bar for three hours. The whole sheet steps up rather than just
     the total, so the lines the total is made of stay readable next to
     it. */
  font-size: 1.2rem;
}
.client-head { text-align: center; margin-bottom: 4px; }
.client-meta {
  text-align: center;
  color: var(--text-muted);
  font-size: 0.9rem;
  margin: 0 0 20px;
}
.client-title {
  text-align: center;
  margin: 0 0 18px;
  font-size: 1.15rem;
  font-weight: 600;
  letter-spacing: 0.04em;
}

.client-lines { width: 100%; border-collapse: collapse; }
.client-lines th {
  text-align: left;
  font-size: 0.8rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--text-muted);
  border-bottom: 1px solid var(--border);
  padding-bottom: 6px;
}
.client-lines td { padding: 9px 0; border-bottom: 1px solid var(--border-faint); }
.client-lines .client-item { padding-right: 12px; }
.client-lines .client-qty { text-align: center; width: 3.5em; }
.client-lines .client-money { text-align: right; width: 7em; white-space: nowrap; }

.client-totals { margin: 18px 0 0; }
.client-totals > div {
  display: flex;
  justify-content: space-between;
  gap: 16px;
  padding: 5px 0;
}
.client-totals dt { color: var(--text-muted); margin: 0; }
.client-totals dd { margin: 0; text-align: right; white-space: nowrap; }
.client-totals .client-grand {
  border-top: 2px solid var(--border);
  margin-top: 10px;
  padding-top: 12px;
  /* The one number everybody at the table is looking for. Tabular
     figures so the digits do not shuffle sideways between repaints. */
  font-size: 2.3rem;
  font-weight: 800;
  line-height: 1.15;
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.01em;
  align-items: baseline;
}
.client-totals .client-grand dt {
  /* The label does not need to be as loud as the figure beside it. */
  font-size: 1.05rem;
  font-weight: 600;
}
.client-totals .client-grand dt { color: var(--text); }

.client-thanks {
  text-align: center;
  margin: 24px 0 0;
  color: var(--text-muted);
  letter-spacing: 0.08em;
}

.client-actions {
  max-width: 520px;
  margin: 16px auto 40px;
  display: flex;
  gap: 10px;
  justify-content: center;
}
.client-actions button { min-height: 48px; padding: 0 22px; }

/* The Export screen: a tick box per dataset, laid out so the whole list
   is visible at once rather than as a column you have to scroll. */
.pin-note { margin: 0 0 10px; padding: 8px 10px; border-radius: 10px; background: var(--surface-hover); }
.alert-seen { margin: 6px 0 0; padding: 4px 12px; min-height: 0; }

/* The drinks stock export: two big choices, one picked. */
.inv-export-row { margin: 0 0 16px; }
.inv-export-choices { display: grid; gap: 10px; margin: 6px 0 4px; }
.inv-export-choice {
  display: flex; flex-direction: column; align-items: flex-start; gap: 2px;
  text-align: left; border-radius: 14px; padding: 12px 14px; margin: 0;
}
.inv-export-choice small { font-weight: 400; color: var(--text-muted); line-height: 1.3; }
.inv-export-choice.selected small { color: inherit; opacity: 0.85; }

.export-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: 8px 18px;
  margin: 10px 0 4px;
  max-width: 900px;
}
.export-row {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  padding: 8px 10px;
  border: 1px solid var(--border-soft);
  border-radius: 10px;
}
.export-row input { margin-top: 3px; flex: 0 0 auto; width: auto; }
.export-row-name { font-weight: 600; display: block; }
.export-row-desc { color: var(--text-muted); font-size: 0.9em; }
.export-dates {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  gap: 16px;
  max-width: 480px;
  margin: 10px 0;
}
.export-dates .field { margin: 0; }

/* The two money settings on the Charges tab, side by side on a wide
   screen and stacked on a phone. */
.charge-money-row {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: 16px;
  margin: 12px 0;
  max-width: 760px;
}
.charge-money-row .field { margin: 0; }
.field-help { color: var(--text-muted); font-size: 0.9em; margin: 4px 0 0; }

/* How many items are ticked on a charge's own list. */
.charge-chosen { margin: 0 0 8px; color: var(--text-muted); font-size: 0.95em; }

/* --- Splitting by item ------------------------------------------------
   Each line of the bill is a target you can hit at speed. A native
   checkbox is about 16px of tappable area and its two states look almost
   the same across a dim room; the whole row is the control here, and the
   selected one is filled rather than merely ticked. */
.split-pick-list {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin: 12px 0;
  /* A long tab must not push the buttons off the bottom of the dialog. */
  max-height: 46vh;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
}
.split-pick {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  width: 100%;
  min-height: 62px;
  margin: 0;
  padding: 10px 14px;
  text-align: left;
  border: 2px solid var(--border-soft);
  border-radius: 14px;
  background: var(--surface);
  color: var(--text-muted);
  cursor: pointer;
  transition: border-color 120ms ease, background 120ms ease, color 120ms ease;
}
.split-pick-main { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.split-pick-name {
  font-weight: 600;
  font-size: 1rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.split-pick-sub { font-size: 0.85rem; opacity: 0.8; }
.split-pick-right {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 3px;
  flex-shrink: 0;
}
.split-pick-amount { font-weight: 700; font-size: 1.05rem; }
.split-pick-state {
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  padding: 2px 8px;
  border-radius: 999px;
  background: var(--surface-sunken);
  color: var(--text-faint);
  white-space: nowrap;
}
/* Picked: filled, full-contrast, and it says so. */
.split-pick.selected {
  border-color: var(--accent);
  background: var(--accent);
  color: var(--accent-text);
}
.split-pick.selected .split-pick-sub { opacity: 0.85; }
.split-pick.selected .split-pick-state {
  background: var(--on-fill-tint);
  color: var(--accent-text);
}

.split-pick-summary {
  margin: 4px 0 14px;
  text-align: center;
  font-size: 1rem;
  color: var(--text-muted);
}
.split-pick-summary strong { color: var(--text); font-size: 1.1rem; }

/* --- Slide to take payment -------------------------------------------
   The last action before money changes hands, on a device being held out
   across a table. A button here gets pressed by a thumb steadying the
   iPad; a slide does not. The track fills green as it travels so it is
   obvious something is happening, and it springs back if let go early. */
.client-pay-slot {
  max-width: 520px;
  margin: 0 auto 48px;
}
/* The figure the slide is about to take. It sits at the very bottom of
   a long sheet, and it is the last thing anybody should be able to
   misread -- so it gets a lit panel of its own rather than a line of
   grey text. rgb triples and rgba(), not color-mix(): Safari only
   learned color-mix() in 16.2 and the bar's iPads are older. */
.client-pay-line {
  margin: 0 0 12px;
  padding: 12px 16px;
  text-align: center;
  font-size: 1.05rem;
  color: var(--text-muted);
  background: var(--surface);
  border: 2px solid var(--accent);
  border-radius: 14px;
  box-shadow: 0 0 0 4px rgba(var(--pay-glow), 0.16),
              0 8px 22px rgba(var(--pay-glow), 0.22);
}
.client-pay-line strong {
  display: block;
  margin-top: 4px;
  color: var(--text);
  font-size: 2.1rem;
  font-weight: 800;
  line-height: 1.15;
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.01em;
}

/* One size for every slide control (basket, take payment, start
   training), and never edge to edge: at least 20px clear on each side
   inside its box, so a thumb swiping the page from the screen edge does
   not land on the knob (the owner's rule). */
.slide-confirm {
  padding: 0;
  width: min(calc(100% - 40px), 420px);
  margin-left: auto;
  margin-right: auto;
}
/* Inside the training dialog the box is already well in from the screen
   edges, so the slide fills it rather than shrinking twice. */
#training-slide-step .slide-confirm { width: 100%; }
.slide-track {
  position: relative;
  height: 56px;
  border-radius: 28px;
  background: var(--surface-sunken);
  border: 1px solid var(--border);
  overflow: hidden;
  display: flex;
  align-items: center;
  justify-content: center;
  /* The fill is painted as a background layer that grows with the knob,
     so there is no second element to keep in step. */
  background-image: linear-gradient(var(--positive), var(--positive));
  background-repeat: no-repeat;
  background-size: 0% 100%;
  transition: background-size 180ms ease;
}
.slide-track.dragging { transition: none; }
.slide-label {
  position: relative;
  z-index: 1;
  font-weight: 600;
  font-size: 0.95rem;
  color: var(--text-muted);
  pointer-events: none;
  padding: 0 52px 0 64px;
  text-align: center;
  line-height: 1.2;
}
.slide-track.armed .slide-label { color: var(--accent-text); }
.slide-knob {
  position: absolute;
  left: 4px;
  top: 4px;
  width: 48px;
  height: 48px;
  border-radius: 50%;
  background: var(--accent);
  color: var(--accent-text);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 1.1rem;
  z-index: 2;
  cursor: grab;
  /* Without this the browser takes the drag as a page scroll and the
     knob never moves on a touchscreen. */
  touch-action: none;
  user-select: none;
  transition: transform 180ms ease;
}
.slide-track.dragging .slide-knob { transition: none; cursor: grabbing; }
.slide-confirm.done .slide-track { background-size: 100% 100%; }
.slide-confirm.done .slide-knob { opacity: 0.6; }
.slide-confirm.busy { pointer-events: none; opacity: 0.7; }

/* The method, under the slide, so a customer who changes their mind at
   the last moment is one tap away rather than a page away. Big targets:
   the device is usually being held by the customer. */
.client-methods { margin-top: 18px; text-align: center; }
.client-methods-cap {
  margin: 0 0 8px;
  font-size: 0.95rem;
  color: var(--text-muted);
}
.client-method-bubbles { justify-content: center; gap: 8px; }
.client-method-bubbles .bubble {
  min-height: 48px;
  padding: 10px 18px;
  font-size: 1rem;
}
#client-other-wrap { margin-top: 12px; text-align: left; }

/* --- What is still owed ----------------------------------------------
   A part-paid bill used to read as an ordinary total with a payment
   tucked underneath, and staff were closing tabs that still had money on
   them. The outstanding figure is now the biggest thing on the block. */
.outstanding-box {
  margin: 12px 0 0;
  padding: 14px 16px;
  border-radius: 12px;
  background: var(--badge-open-bg);
  border: 2px solid var(--warn-mid);
  text-align: center;
}
.outstanding-box .outstanding-cap {
  display: block;
  font-size: 0.85rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--badge-open-text);
}
.outstanding-box .outstanding-amount {
  display: block;
  font-size: 2rem;
  font-weight: 800;
  line-height: 1.15;
  color: var(--badge-open-text);
}
.outstanding-box .outstanding-sub {
  display: block;
  margin-top: 2px;
  font-size: 0.9rem;
  color: var(--badge-open-text);
}

/* The same figure inside the close-bill dialog, where it has less room. */
.modal .outstanding-box { margin: 10px 0 14px; }
.modal .outstanding-box .outstanding-amount { font-size: 1.7rem; }

/* How the bill was split, on the customer's copy. */
/* What each person owes, directly under the total. Big -- but a step
   down from it, because the total is the bill and this is a division of
   it, and two figures at the same size make the reader work out which is
   which. */
.client-split-note {
  margin: 10px 0 0;
  text-align: right;
  font-size: 1.05rem;
  color: var(--text-muted);
}
.client-split-note strong {
  display: block;
  margin-top: 2px;
  color: var(--text);
  font-size: 1.7rem;
  font-weight: 800;
  line-height: 1.2;
  font-variant-numeric: tabular-nums;
}

/* --- The thank-you page ------------------------------------------------
   What the customer is left holding once the money is taken. The
   transaction is over, so the page is almost empty on purpose: a kicker,
   a line of thanks with the logo set in it, and the three things still
   worth doing along the bottom. */
.done-sheet {
  max-width: 560px;
  margin: 0 auto;
  padding: 56px 24px 40px;
  text-align: center;
  /* Centred in whatever height is left over, so it sits in the middle of
     the iPad rather than clinging to the top of it. */
  min-height: 62vh;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 22px;
}
/* Small, wide and quiet: it is a status, not the message. */
.done-kicker {
  margin: 0;
  font-size: 0.78rem;
  font-weight: 600;
  letter-spacing: 0.34em;
  text-transform: uppercase;
  color: var(--text-muted);
}
.done-thanks {
  margin: 0;
  font-size: 2rem;
  line-height: 1.5;
  font-weight: 300;
  /* The three parts sit on one baseline however the script font's own
     metrics fall, and wrap as a unit on a narrow screen. */
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: center;
  /* Small, because .brand-rj carries a wide right padding of its own to
     give the J's tail somewhere to be painted. That padding is most of
     the gap after it; a larger one here would leave "Family" adrift. */
  gap: 0.12em;
}
/* The logo, at the size the sentence needs rather than the size the top
   bar needs. */
.done-thanks .brand-rj {
  font-size: 3.1rem;
  line-height: 1;
  /* line-height 1 keeps the sentence tight, but it also shrinks the box
     the gold is painted in to one em -- and Snell Roundhand's J hangs far
     below that, so its tail was cut off flat. background-clip:text paints
     nothing outside the padding box, so the room has to be padding. The
     matching negative margins hand the space straight back, so the line
     sits exactly where it did. */
  padding-top: 0.2em;
  padding-bottom: 0.6em;
  margin-top: -0.2em;
  margin-bottom: -0.6em;
}
/* The tail's padding only belongs on the right, so the word before it
   is not pushed away as well. */
.done-thanks .done-the { margin-right: 0.1em; }
.done-amount {
  margin: 0;
  font-size: 1.15rem;
  color: var(--text-muted);
  font-variant-numeric: tabular-nums;
}
.done-actions {
  max-width: 560px;
  margin: 8px auto 40px;
  padding: 0 16px;
  display: flex;
  gap: 10px;
  justify-content: center;
  flex-wrap: wrap;
}
.done-actions button { min-height: 52px; padding: 0 22px; }

@media (max-width: 560px) {
  .done-sheet { padding: 40px 16px 28px; gap: 20px; }
  .done-thanks { font-size: 1.6rem; }
  .done-thanks .brand-rj { font-size: 2.5rem; }
  .done-actions button { flex: 1 1 auto; }
}

/* Printed, the sheet is the page: no card, no shadow, no rounded corner.
   Everything else is shared with the screen, so what comes out of the
   printer is what the customer was just handed. */
.client-print-sheet {
  max-width: none; margin: 0; padding: 0; box-shadow: none; background: none;
}

@media (max-width: 560px) {
  .client-sheet { margin: 12px 0 0; padding: 20px 16px 18px; border-radius: 0; }
  .client-actions { margin: 12px 16px 32px; }
}

/* --- Chaser bubbles -----------------------------------------------------
   The drink is the choice; the kind is how it fills a set. Stacked, so
   "Oolong Tea" and "Pitcher" are two lines rather than one run-together
   word, and the bubble is tall enough to hold both without squeezing. */
.bubble-grid .bubble.keep-chaser {
  flex-direction: column;
  justify-content: center;
  gap: 4px;
  min-height: 92px;
  padding: 14px 12px;
  line-height: 1.2;
}
.keep-chaser .chaser-name { font-weight: 700; }
.keep-chaser .chaser-kind {
  font-size: 0.82rem;
  font-weight: 500;
  color: var(--text-muted);
  letter-spacing: 0.04em;
}

/* Which bottle these chasers are being poured from. The staff member may
   have chosen it on a different screen a minute ago, so the chaser page
   says it rather than assuming they remember. */
.chosen-bottle {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 4px 10px;
  margin: 0 0 12px;
  padding: 10px 12px;
  border-radius: 10px;
  background: var(--surface-sunken);
}
.chosen-bottle-name { font-weight: 700; }
.chosen-bottle-sub { color: var(--text-muted); font-size: 0.9rem; }
.chosen-bottle .link-btn { margin-left: auto; }

/* "Sales by:" on the tab. Same shape as the seat bubble beside it -- both
   are a question that becomes its own answer once someone taps it. */
.sales-by-bubble {
  border-radius: 999px;
  padding: 6px 14px;
  min-height: 38px;
}
.sales-by-bubble.has-source {
  background: var(--accent);
  color: var(--accent-text);
  border-color: var(--accent);
  font-weight: 700;
}

/* Which promoter a drink is for, on a DB tab carrying more than one name.
   Its own row under the quantity stepper: it is a second question, and
   putting it beside the +/- made both look like one control. */
.qty-whofor {
  flex-basis: 100%;
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 6px;
  margin-top: 8px;
}
.qty-whofor-label { color: var(--text-muted); font-size: 0.9rem; }
.qty-whofor .bubble { margin: 0; padding: 5px 12px; min-height: 34px; font-size: 0.9rem; }

/* The unit the "Pour size" box is asking for, named rather than implied. */
.pour-unit-note { color: var(--text-muted); font-weight: 400; }

/* The tab's status sits on the same row as the back button and was less
   than half its height, which made the row read as lopsided rather than
   as two ends of one bar. Sized to match it. Scoped to this one badge:
   the badges in the tab list and the reports are meant to be small, and
   growing those would push every table row apart. The status COLOURS
   still come from .badge.Open / .badge.Paid and are untouched. */
#bill-status {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  /* The same metrics the base button rule gives the back button, so the
     two ends of the row match rather than nearly match. */
  min-height: 44px;
  padding: 12px 20px;
  font-size: 1.05rem;
  font-weight: 600;
  border: 1px solid transparent;
}

/* A category whose bottles can carry a chaser set. */
.cat-takes-set { color: var(--positive); }
/* A category whose missing DB prices are asked for at the till. */
.cat-asks-db { color: var(--accent); }
.cat-is-food { color: var(--warn-mid); }

/* A bill number on the DB page that opens the bill behind it. Styled as
   text rather than a button so the table still reads as a table, but it
   is a real <button> underneath -- keyboard and screen readers get a
   control, not a decorated span. */
.bill-link {
  background: none;
  border: none;
  padding: 0;
  font: inherit;
  font-size: 0.85em;
  color: var(--accent);
  text-decoration: underline;
  cursor: pointer;
}
.bill-link:hover { color: var(--text); }

/* A line taken off the bill, shown struck through in the read-only
   popup rather than hidden: the question being asked there is what
   happened on that tab, and a voided line is part of the answer. */
.bill-line-void td { color: var(--text-faint); text-decoration: line-through; }
.bill-line-tag {
  font-size: 0.8em;
  color: var(--text-muted);
  text-decoration: none;
}

/* Save and Cancel pinned to the bottom of a long dialog.

   The category dialog grew past the height of an iPad in portrait, which
   put its Save button off the screen: the dialog scrolled, but nothing
   said so, so a setting near the bottom could neither be found nor saved.
   Sticky rather than fixed, so it sits at the end of the content on a
   short dialog and only pins itself once there is more than fits. */
.modal-actions {
  position: sticky;
  /* The card's own 20px padding, pulled out to its edges and given back
     as the bar's padding. Without this the bar stops short of the bottom
     and the content keeps scrolling through the strip underneath it. */
  bottom: -20px;
  margin: 16px -20px -20px;
  padding: 12px 20px 20px;
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
  background: var(--surface);
  border-top: 1px solid var(--border);
}

/* The bulk menu editor. Inputs inside table cells rather than a dialog,
   so a whole category's bottles can be set without opening anything. */
#menu-bulk-rows input[type="text"],
#menu-bulk-rows select {
  width: 100%;
  min-width: 120px;
  padding: 5px 6px;
  border: 1px solid var(--border);
  border-radius: 5px;
  background: var(--surface);
  color: var(--text);
  font: inherit;
}
#menu-bulk-rows input[type="checkbox"] { width: 18px; height: 18px; }
/* Phone-first (the owner, 2026-10-13): the name column stays put while
   the rest scrolls sideways, inputs are as narrow as their contents allow,
   and the yes/no columns stand their headers upright. */
/* overscroll-behavior: reaching the end of the table with a trackpad must
   not carry the swipe on to the pane or the page and shove the whole
   screen sideways. */
.bulk-scroll { overflow-x: auto; -webkit-overflow-scrolling: touch; overscroll-behavior-x: contain; }
.admin-pane, .inv-scroll, .inv-bulk-scroll, .sched-scroll { overscroll-behavior-x: contain; }
/* overflow:hidden on .admin-table would make the table its own scroll box
   and the sticky column would stick to nothing; the wrapper scrolls instead. */
#menu-bulk-table { font-size: 0.85rem; overflow: visible; }
#menu-bulk-table th, #menu-bulk-table td { padding: 4px 4px; white-space: nowrap; }
#menu-bulk-table .bulk-sticky { position: sticky; left: 0; z-index: 2; background: var(--surface); min-width: 130px; max-width: 170px;
  box-shadow: 2px 0 0 var(--border-soft); }
#menu-bulk-table thead .bulk-sticky { z-index: 4; background: var(--surface-alt, var(--surface)); }
.bulk-row.bulk-touched > td.bulk-sticky { background: var(--surface-alt); }
#menu-bulk-rows input[type="text"] { min-width: 84px; width: 9em; }
#menu-bulk-rows td.bulk-sticky input[type="text"] { width: 100%; min-width: 0; font-weight: 600; }
#menu-bulk-rows select { min-width: 0; width: auto; max-width: 9em; }
#menu-bulk-table .bulk-num { width: 5.2em; }
/* The tick columns' headers stand upright. The vertical writing mode sits
   on a span INSIDE the cell: Safari lays a vertical table cell out wrong
   (every header lands on one spot, upside down), a vertical inline box
   it gets right. */
/* 2026-10-15: at 45 degrees, read without turning the head. The label is
   ordinary horizontal text turned from its bottom-left corner, so it
   leans over the next headers instead of widening the column. */
#menu-bulk-table th.bulk-chk { position: relative; height: 104px; width: 30px; max-width: 30px; padding: 0; font-size: 0.72rem; font-weight: 600; vertical-align: bottom; overflow: visible; }
/* The locked name column's header stays over the leaning tick headers. */
#menu-bulk-table thead th:first-child { z-index: 40; }
#menu-bulk-table th.bulk-chk .bulk-chk-label { position: absolute; left: 12px; bottom: 8px; transform-origin: 0 100%; transform: rotate(-45deg); white-space: nowrap; line-height: 1.1; pointer-events: none; }
#menu-bulk-table td.bulk-chk { text-align: center; width: 30px; }
.bulk-row.bulk-inactive > td:not(.bulk-sticky) { opacity: 0.6; }

/* A row with unsaved changes on it. Marked rather than counted alone, so
   "3 unsaved" can always be traced to which three. */
.bulk-row.bulk-touched > td { background: var(--surface-alt); }
.bulk-row.bulk-touched > td:first-child { border-left: 3px solid var(--accent); }

/* The "no recipe yet" worklist chip. Warned rather than colored like a
   category, because it is a job to finish rather than a place to look --
   and it disappears entirely once the list is empty. */
.no-recipe-chip {
  border-style: dashed;
  color: var(--danger);
}
.no-recipe-chip.selected { color: var(--surface); background: var(--danger); }

/* A pasted price line that cannot be filed yet -- unreadable, or with no
   bottle chosen for it. Marked rather than hidden: a line that quietly
   vanished is a price change nobody notices is missing. */
.pi-attention > td { background: var(--surface-alt); }
.pi-problem { color: var(--danger); font-style: italic; }
#pi-text { width: 100%; font-family: ui-monospace, monospace; }
/* Which way a supplier's price moved. A rise is what RJ pays MORE of. */
.pi-up { color: var(--danger); }
.pi-down { color: var(--positive); }


/* Clearing the practice data. Styled apart from everything else on the
   Settings screen on purpose: this is the one control there that cannot
   be undone, and it should not look like the buttons beside it. */
.danger-heading {
  color: rgb(var(--danger-rgb));
}
.wipe-panel {
  margin-top: 14px;
  padding: 16px;
  border: 1px solid rgba(var(--danger-rgb), 0.45);
  border-radius: 12px;
  background: rgba(var(--danger-rgb), 0.06);
}
.wipe-panel h4 {
  margin: 0 0 8px;
  font-size: 0.95rem;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  color: var(--text-muted);
}
.wipe-panel table {
  width: 100%;
  border-collapse: collapse;
  margin-bottom: 14px;
}
.wipe-panel td {
  padding: 3px 0;
  font-size: 0.95rem;
}
.wipe-panel td.n {
  text-align: right;
  font-variant-numeric: tabular-nums;
  padding-left: 18px;
  white-space: nowrap;
}
.wipe-keep {
  color: var(--text-muted);
  font-size: 0.9rem;
  line-height: 1.6;
}
.cutoff-export {
  margin-top: 16px;
  padding-top: 14px;
  border-top: 1px solid rgba(var(--danger-rgb), 0.3);
}
.cutoff-export-opt {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 0.9rem;
  color: var(--text-muted);
}
.cutoff-export-opt select {
  width: auto;
  min-width: 64px;
}
.cutoff-export-opt input[type="checkbox"] {
  width: 18px;
  height: 18px;
  margin: 0;
}
.wipe-confirm {
  margin-top: 16px;
  padding-top: 14px;
  border-top: 1px solid rgba(var(--danger-rgb), 0.3);
}
.wipe-confirm input {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}
.wipe-phrase {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-weight: 700;
  color: rgb(var(--danger-rgb));
}

/* ---------------------------------------------------------------------
   Training Mode
   ---------------------------------------------------------------------
   Nobody should ever have to wonder which mode a till is in. Three
   signals at once, none of which can be scrolled away from: the page
   itself changes shade, the rim of the screen glows, and hazard tape
   sits across the top and bottom.

   All of it lives outside #app and is position:fixed, so it stays put
   over every screen and every modal. The glow takes no taps. */

body.training-mode {
  background: var(--training-bg);
}

/* The rim. Four inset shadows would clip at the corners, so this is one
   element with a radial-ish pair of gradients per edge -- strongest at
   the very edge, fading inward over about 60px into the page colour. */
#training-glow {
  position: fixed;
  inset: 0;
  z-index: 2147483000;   /* over modals, under nothing */
  pointer-events: none;  /* taps pass straight through */
  /* Pulled in and quietened. The first version reached 110px into the
     page and washed over the RJ POS mark in the corner -- unmistakable,
     but it sat on top of the one thing the screen should always show
     clearly. Half the reach and a third of the strength still reads as
     a lit rim from across the bar. */
  box-shadow:
    inset 0 0 9px 0 rgba(var(--training-rgb), 0.72),
    inset 0 0 24px 3px rgba(var(--training-rgb), 0.26),
    inset 0 0 46px 9px rgba(var(--training-rgb), 0.10);
}

/* Hazard tape, as a notch rather than a band across the whole screen.
   A full-width bar ate a strip off the top and bottom of a small screen
   and pushed everything down to make room. A notch says exactly the same
   thing in the one place nothing else uses -- the middle of the very top
   and bottom edges -- and leaves the rest of the width alone.

   pointer-events:none instead of page padding: nothing underneath can be
   covered if the notch cannot be touched in the first place. */
.training-bar {
  position: fixed;
  left: 50%;
  transform: translateX(-50%);
  z-index: 2147483001;
  display: flex;
  align-items: center;
  justify-content: center;
  pointer-events: none;
  /* Room either side of the chip so the stripes actually read as
     caution tape rather than a thin yellow outline. */
  padding: 5px 20px;
  background: repeating-linear-gradient(
    45deg,
    rgb(var(--training-rgb)) 0 12px,
    var(--hazard-ink) 12px 24px
  );
  box-shadow: 0 0 14px rgba(var(--training-rgb), 0.5);
}
#training-bar-top {
  top: 0;
  border-radius: 0 0 13px 13px;
}
#training-bar-bottom {
  bottom: 0;
  border-radius: 13px 13px 0 0;
}
/* The top notch sits over the middle of the top bar, which on a phone is
   exactly where Tabs and Admin are. While a notch is showing, the bar
   gets room above its buttons so the notch never covers them. */
body.training-mode #topbar,
body.comp-mode #topbar { padding-top: 34px; }

/* The words sit on a solid chip so they stay readable over the stripes. */
.training-bar span {
  background: rgb(var(--training-rgb));
  color: var(--training-ink);
  font-weight: 800;
  font-size: 0.72rem;
  letter-spacing: 0.2em;
  padding: 2px 12px;
  border-radius: 4px;
  white-space: nowrap;
}

/* The toggle, a row in the account menu: the shield, then the words. */
.training-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
}
.training-btn svg {
  display: block;
  flex: none;
}
/* Scoped to #topbar deliberately: `#topbar button` sets a grey
   background at ID specificity, so a plain class here loses to it --
   which left the toggle looking off while the till was in training. */
/* The two-second hold: the ring fills up while the finger stays down. */
#topbar .training-btn { position: relative; overflow: hidden; }
#topbar .training-btn::after {
  content: ""; position: absolute; inset: 0; background: rgba(var(--training-rgb), 0.55);
  transform: scaleX(0); transform-origin: left; transition: transform 0s; pointer-events: none;
}
#topbar .training-btn.holding::after { transform: scaleX(1); transition: transform 2s linear; }
#topbar .training-btn[aria-pressed="true"] {
  background: rgb(var(--training-rgb));
  color: var(--training-ink);
  border-color: rgb(var(--training-rgb));
  box-shadow: 0 0 12px rgba(var(--training-rgb), 0.7);
}

/* PIN, then slide. Its own dialog because openModal() replaces the
   shared one's innerHTML and this opens over whatever was already up. */
.training-dialog-backdrop {
  position: fixed;
  inset: 0;
  z-index: 2147483002;
  background: rgba(0, 0, 0, 0.55);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 16px;
}
.training-dialog {
  background: var(--surface);
  color: var(--text);
  border: 1px solid rgba(var(--training-rgb), 0.55);
  border-radius: 16px;
  padding: 22px;
  width: min(420px, 100%);
  box-shadow: 0 18px 50px rgba(0, 0, 0, 0.45);
}
.training-dialog h3 {
  margin: 0 0 8px;
}
.training-dialog p {
  margin: 0 0 14px;
  color: var(--text-muted);
  font-size: 0.95rem;
  line-height: 1.5;
}
.training-dialog input {
  width: 100%;
  font-size: 1.3rem;
  letter-spacing: 0.3em;
  text-align: center;
}
.training-error {
  color: var(--danger);
  font-size: 0.9rem;
  margin: 8px 0 0;
}

/* The open-tabs warning inside the training dialog. Not an error -- the
   tabs are safe either way -- so it reads as a caution, in the mode's
   own colour rather than the red used for things that went wrong. */
.training-open-tabs {
  border: 1px solid rgba(var(--training-rgb), 0.6);
  background: rgba(var(--training-rgb), 0.12);
  border-radius: 10px;
  padding: 10px 12px;
  font-size: 0.92rem;
  line-height: 1.45;
}

/* A whole table row that opens something. Used by the GMO settlements
   list: hunting for a small button at the end of a row on a tablet is
   needless work when the reference itself is the thing you mean to tap. */
.admin-table tr.row-clickable {
  cursor: pointer;
}
.admin-table tr.row-clickable:hover,
.admin-table tr.row-clickable:focus-visible {
  background: var(--row-hover, rgba(127, 127, 127, 0.12));
  outline: none;
}
.admin-table tr.row-clickable:focus-visible {
  box-shadow: inset 0 0 0 2px var(--accent, currentColor);
}

/* --- RJ membership tiers -------------------------------------------------
   Phase 1 is status only, so these are chips and a bar, not a trophy case.
   The full neon "RJ Member" badge is a separate piece of work. */
.tier-chip {
  display: inline-block;
  padding: 3px 12px;
  border-radius: 999px;
  font-size: 0.88rem;
  font-weight: 700;
  letter-spacing: 0.03em;
  white-space: nowrap;
  border: 1px solid rgba(255, 255, 255, 0.16);
}
.tier-chip-big { font-size: 1.15rem; padding: 6px 18px; }
.tier-none {
  background: transparent;
  color: var(--text-faint);
  border-color: var(--border-soft);
}
/* The metal chips (the owner, 2026-10-20: "stand out and shine more",
   more metallic parts). Built in three layers: the chip's own background
   is the frame (a metal gradient, so the edge catches the light), ::before
   is the plate set just inside it, ::after a band of light that crosses
   now and then. The name sits on top. Each rung adds a ring:
     Bronze   the frame
     Silver   the frame and a bright outer ring
     Gold     the frame, a gold ring and a warm glow
     Black    a three-band gold frame, gold lettering and a gold glow
     Diamond  a crystal plate, a white frame and an icy glow */
.tier-bronze, .tier-silver, .tier-gold, .tier-black, .tier-diamond {
  position: relative; isolation: isolate; overflow: hidden;
  border: 0; padding: 4px 13px;
}
.tier-chip-big.tier-bronze, .tier-chip-big.tier-silver, .tier-chip-big.tier-gold,
.tier-chip-big.tier-black, .tier-chip-big.tier-diamond { padding: 7px 19px; }
.tier-bronze::before, .tier-silver::before, .tier-gold::before, .tier-black::before, .tier-diamond::before {
  content: ""; position: absolute; inset: 1.5px; border-radius: inherit; z-index: 0; pointer-events: none;
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.55), inset 0 -1px 2px rgba(0, 0, 0, 0.45);
}
.tier-bronze::after, .tier-silver::after, .tier-gold::after, .tier-black::after, .tier-diamond::after {
  content: ""; position: absolute; top: 0; bottom: 0; left: 0; width: 45%; z-index: 1; pointer-events: none;
  background: linear-gradient(100deg, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 0.6) 50%, rgba(255, 255, 255, 0) 100%);
  transform: translateX(-160%) skewX(-20deg);
  animation: tier-shine 6s ease-in-out infinite;
}
.tier-chip .tier-chip-label { position: relative; z-index: 2; }
@keyframes tier-shine { 0%, 62% { transform: translateX(-160%) skewX(-20deg); } 88%, 100% { transform: translateX(330%) skewX(-20deg); } }

.tier-bronze { background: var(--tier-bronze-rim); color: var(--tier-bronze-tex-ink);
  text-shadow: 0 -1px 0 rgba(40, 15, 0, 0.6), 0 1px 0 rgba(255, 220, 180, 0.35);
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.35); }
.tier-bronze::before { background: var(--tier-bronze-tex); }

.tier-silver { background: var(--tier-silver-rim); color: var(--tier-silver-tex-ink);
  text-shadow: 0 1px 0 rgba(255, 255, 255, 0.85), 0 -1px 0 rgba(0, 0, 0, 0.15);
  box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.85), 0 0 0 2px rgba(110, 120, 130, 0.55), 0 1px 4px rgba(0, 0, 0, 0.3); }
.tier-silver::before { background: var(--tier-silver-tex); }
.tier-silver::after { animation-delay: 1.2s; }

.tier-gold { background: var(--tier-gold-rim); color: var(--tier-gold-tex-ink);
  text-shadow: 0 1px 0 rgba(255, 248, 210, 0.75), 0 -1px 0 rgba(90, 60, 0, 0.3);
  box-shadow: 0 0 0 1px #fff2b8, 0 0 0 2.5px #9c7206, 0 0 10px 1px rgba(var(--tier-gold-rgb), 0.55), 0 1px 4px rgba(0, 0, 0, 0.3); }
.tier-gold::before { background: var(--tier-gold-tex); }
.tier-gold::after { animation-delay: 2.4s; background: linear-gradient(100deg, rgba(255, 255, 240, 0) 0%, rgba(255, 255, 240, 0.75) 50%, rgba(255, 255, 240, 0) 100%); }

.tier-black { background: var(--tier-black-rim); letter-spacing: 0.08em;
  box-shadow: 0 0 0 1px #1a1408, 0 0 0 2.5px #e8cf86, 0 0 0 3.5px #5a430f, 0 0 12px 2px rgba(232, 200, 110, 0.45), 0 1px 4px rgba(0, 0, 0, 0.45); }
.tier-black::before { inset: 2px; background: var(--tier-black-tex);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.18), inset 0 -1px 2px rgba(0, 0, 0, 0.7); }
.tier-black::after { animation-delay: 3.6s; background: linear-gradient(100deg, rgba(255, 240, 200, 0) 0%, rgba(255, 240, 200, 0.32) 50%, rgba(255, 240, 200, 0) 100%); }
/* Polished gold lettering on the black plate. */
.tier-black .tier-chip-label {
  color: var(--tier-black-tex-ink);
  background: var(--tier-black-letter); -webkit-background-clip: text; background-clip: text;
  -webkit-text-fill-color: transparent; filter: drop-shadow(0 1px 0 rgba(0, 0, 0, 0.85));
}

.tier-diamond { background: var(--tier-diamond-rim); color: var(--tier-diamond-tex-ink);
  text-shadow: 0 1px 0 rgba(255, 255, 255, 0.9);
  box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.95), 0 0 8px rgba(var(--tier-diamond-glow-rgb), 0.9), 0 0 20px rgba(var(--tier-diamond-glow-rgb), 0.55); }
.tier-diamond::before { background: var(--tier-diamond-tex); box-shadow: inset 0 0 6px rgba(255, 255, 255, 0.8); }
.tier-diamond::after { animation-delay: 0.6s; background: linear-gradient(100deg, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 0.95) 50%, rgba(255, 255, 255, 0) 100%); }

/* What the member sees, beside what they have actually earned. Both are
   on screen at once because this is a staff view -- a customer-facing
   screen must never show the calculated one. */
.tier-headline {
  display: flex;
  flex-wrap: wrap;
  gap: 22px;
  align-items: flex-end;
  margin: 10px 0 6px;
}
.tier-headline > div { display: flex; flex-direction: column; gap: 4px; }
.tier-headline small { color: var(--text-muted); font-size: 0.8rem; }

.tier-gift-note { margin: 2px 0 10px; font-size: 0.9rem; }
.tier-gift-note.muted { color: var(--text-muted); }

.tier-progress { margin: 4px 0 12px; max-width: 400px; }
.tier-progress-bar {
  height: 8px;
  border-radius: 999px;
  background: var(--surface-hover);
  overflow: hidden;
  margin-bottom: 4px;
}
.tier-progress-bar > span {
  display: block;
  height: 100%;
  background: var(--accent);
  border-radius: 999px;
}
.tier-progress small { color: var(--text-muted); }

.tier-history-row {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: 6px 0;
  border-bottom: 1px solid var(--border-soft);
}
.tier-history-row:last-child { border-bottom: none; }
.tier-history-row small { color: var(--text-muted); }

/* The threshold editor on the Settings screen. */
.tier-ladder-row {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 8px 0;
  border-bottom: 1px solid var(--border-soft);
}
.tier-ladder-row:last-child { border-bottom: none; }
.tier-ladder-row label { flex: 0 0 120px; margin: 0; }
.tier-ladder-row input { max-width: 180px; }
.tier-ladder-row .tier-ladder-note { color: var(--text-muted); font-size: 0.88rem; }

/* A dialog that has to sit OUTSIDE #modal, because openModal replaces the
   modal's innerHTML and a second panel nested inside it would be wiped
   (and could never be shown through .hidden on an ancestor anyway). */
#gift-tier-overlay {
  position: fixed;
  inset: 0;
  z-index: 2147482000;
  background: rgba(0, 0, 0, 0.55);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 16px;
  overflow-y: auto;
}
#gift-tier-overlay .overlay-card {
  background: var(--surface);
  color: var(--text);
  border-radius: 14px;
  padding: 20px;
  width: 100%;
  max-width: 520px;
  box-shadow: 0 18px 50px rgba(0, 0, 0, 0.4);
}

/* The line on a delete confirmation that says what cannot be undone. Red
   without being a banner: the dialog around it is already the warning. */
.danger-note {
  color: var(--danger);
  font-weight: 600;
  margin: 12px 0;
}

/* The Delete button sits beside Finish on a bottle row; it needs air so
   a thumb aiming for one never lands on the other. */
.btn-bk-delete { margin-left: 8px; }

/* --- The RJ Member badge -------------------------------------------------
   The neon card a member's rank is shown on. Built like the Paid/Unpaid
   status chips -- a coloured ring and an outer bloom in the same colour --
   but on its own dark plate in BOTH themes, because a member's card should
   look like a thing the bar hands out rather than a piece of the page. It
   reads as a bar sign at night, not a badge in a game.

   Two faces on purpose: "RJ" in the logo's script, "Member" in plain,
   wide, modern caps. The contrast between them is the design. */
.rj-badge {
  display: inline-flex;
  align-items: baseline;
  gap: 8px;
  padding: 8px 20px 8px 18px;
  border-radius: 999px;
  background: var(--rj-badge-plate);
  /* The ring, then the bloom -- the same two-layer shadow the status
     chips use, so the two read as one family. */
  box-shadow: 0 0 0 2px rgba(var(--tier-silver-rgb), 0.55),
              0 0 18px 2px rgba(var(--tier-silver-rgb), 0.45);
  white-space: nowrap;
  user-select: none;
  vertical-align: middle;
}
.rj-badge-rj {
  /* The same stack as the top-bar mark: Snell Roundhand is what the logo
     is set in and is on every Mac and iPad the bar uses; the rest keep it
     a script anywhere else. */
  font-family: "Snell Roundhand", "Apple Chancery", "Zapfino",
               "Segoe Script", "Gabriola", "Edwardian Script ITC",
               "URW Chancery L", "Z003", "Brush Script MT", cursive;
  font-size: 1.45em;
  font-weight: 700;
  letter-spacing: -0.06em;
  /* The J's tail runs well past where the letter officially ends. Without
     room on the right it is clipped -- the same thing that once cut the
     top-bar logo in half. Padding, not overflow, is the cure. */
  padding-left: 0.06em;
  padding-right: 0.34em;
  line-height: 1;
  color: rgb(var(--tier-silver-rgb));
  /* The neon itself: the letter glows rather than the box around it. */
  text-shadow: 0 0 6px rgba(var(--tier-silver-rgb), 0.85),
               0 0 14px rgba(var(--tier-silver-rgb), 0.45);
}
.rj-badge-word {
  font-size: 0.72em;
  font-weight: 600;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--rj-badge-word);
  line-height: 1;
}

/* One rule per tier, built like the chips: the badge's own background is
   a metal frame, ::before the plate just inside it, ::after a band of
   light crossing now and then, and "RJ" set in polished metal lettering
   with a glow of its own colour. Each rung adds a layer of "armor". */
.rj-badge.rj-bronze, .rj-badge.rj-silver, .rj-badge.rj-gold, .rj-badge.rj-black, .rj-badge.rj-diamond {
  position: relative; isolation: isolate; overflow: hidden;
}
.rj-badge.rj-bronze::before, .rj-badge.rj-silver::before, .rj-badge.rj-gold::before,
.rj-badge.rj-black::before, .rj-badge.rj-diamond::before {
  content: ""; position: absolute; inset: 2px; border-radius: inherit; z-index: 0; pointer-events: none;
  box-shadow: inset 0 1.5px 0 rgba(255, 255, 255, 0.18), inset 0 -2px 4px rgba(0, 0, 0, 0.6);
}
.rj-badge.rj-bronze::after, .rj-badge.rj-silver::after, .rj-badge.rj-gold::after,
.rj-badge.rj-black::after, .rj-badge.rj-diamond::after {
  content: ""; position: absolute; top: -20%; bottom: -20%; left: 0; width: 40%; z-index: 1; pointer-events: none;
  background: linear-gradient(100deg, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 0.35) 50%, rgba(255, 255, 255, 0) 100%);
  transform: translateX(-160%) skewX(-18deg); animation: tier-shine 5s ease-in-out infinite;
}
.rj-badge.rj-bronze > span, .rj-badge.rj-silver > span, .rj-badge.rj-gold > span,
.rj-badge.rj-black > span, .rj-badge.rj-diamond > span { position: relative; z-index: 2; }
/* Metal lettering: the gradient is painted inside the letters; the glow
   is a drop-shadow, because a text-shadow would show through them. */
.rj-badge.rj-bronze .rj-badge-rj, .rj-badge.rj-silver .rj-badge-rj,
.rj-badge.rj-gold .rj-badge-rj, .rj-badge.rj-black .rj-badge-rj {
  -webkit-background-clip: text; background-clip: text; -webkit-text-fill-color: transparent; text-shadow: none;
}

.rj-badge.rj-bronze {
  background: var(--tier-bronze-rim);
  box-shadow: 0 0 16px 2px rgba(var(--tier-bronze-rgb), 0.45);
}
.rj-badge.rj-bronze::before { background: radial-gradient(80% 120% at 50% 130%, rgba(205, 127, 50, 0.38), rgba(0, 0, 0, 0) 70%), var(--rj-badge-plate); }
.rj-badge.rj-bronze .rj-badge-rj { color: rgb(var(--tier-bronze-rgb)); background-image: var(--tier-bronze-letter);
  filter: drop-shadow(0 0 5px rgba(var(--tier-bronze-rgb), 0.8)); }

.rj-badge.rj-silver {
  background: var(--tier-silver-rim);
  box-shadow: 0 0 0 1.5px rgba(255, 255, 255, 0.85), 0 0 18px 3px rgba(var(--tier-silver-rgb), 0.55);
}
.rj-badge.rj-silver::before { background: radial-gradient(80% 120% at 50% 130%, rgba(200, 215, 230, 0.32), rgba(0, 0, 0, 0) 70%), var(--rj-badge-plate); }
.rj-badge.rj-silver::after { animation-delay: 1s; }
.rj-badge.rj-silver .rj-badge-rj { color: rgb(var(--tier-silver-rgb)); background-image: var(--tier-silver-letter);
  filter: drop-shadow(0 0 5px rgba(220, 230, 240, 0.75)); }

.rj-badge.rj-gold {
  background: var(--tier-gold-rim);
  box-shadow: 0 0 0 1.5px #fff2b8, 0 0 0 3px #9c7206, 0 0 20px 4px rgba(var(--tier-gold-rgb), 0.6);
}
.rj-badge.rj-gold::before { inset: 2.5px; background: radial-gradient(80% 120% at 50% 130%, rgba(255, 200, 60, 0.4), rgba(0, 0, 0, 0) 70%), var(--rj-badge-plate); }
.rj-badge.rj-gold::after { animation-delay: 2s; background: linear-gradient(100deg, rgba(255, 250, 220, 0) 0%, rgba(255, 250, 220, 0.45) 50%, rgba(255, 250, 220, 0) 100%); }
.rj-badge.rj-gold .rj-badge-rj { color: rgb(var(--tier-gold-rgb)); background-image: var(--tier-gold-letter);
  filter: drop-shadow(0 0 6px rgba(var(--tier-gold-rgb), 0.85)); }
.rj-badge.rj-gold .rj-badge-word { color: #f5e3a6; }

/* Black: the black card. A glossy black plate in a three-band gold frame,
   champagne-gold lettering and a soft gold glow. */
.rj-badge.rj-black {
  background: var(--tier-black-rim);
  box-shadow: 0 0 0 1.5px #1a1408, 0 0 0 3px #e8cf86, 0 0 0 4.5px #5a430f, 0 0 0 5.5px #f3dc8c,
              0 0 18px 4px rgba(232, 200, 110, 0.45);
}
.rj-badge.rj-black::before { inset: 2.5px; background: var(--tier-black-tex);
  box-shadow: inset 0 1.5px 0 rgba(255, 255, 255, 0.2), inset 0 -2px 4px rgba(0, 0, 0, 0.8); }
.rj-badge.rj-black::after { animation-delay: 3s; background: linear-gradient(100deg, rgba(255, 240, 200, 0) 0%, rgba(255, 240, 200, 0.28) 50%, rgba(255, 240, 200, 0) 100%); }
.rj-badge.rj-black .rj-badge-rj { color: var(--tier-black-script); background-image: var(--tier-black-letter);
  filter: drop-shadow(0 0 5px rgba(232, 200, 110, 0.7)); }
.rj-badge.rj-black .rj-badge-word { color: var(--tier-black-tex-ink); }

/* Diamond: the crystal plate, a white frame and an icy glow. */
.rj-badge.rj-diamond {
  background: var(--tier-diamond-rim);
  box-shadow: 0 0 0 1.5px rgba(255, 255, 255, 0.95), 0 0 0 3px rgba(var(--tier-diamond-glow-rgb), 0.7),
              0 0 14px 3px rgba(var(--tier-diamond-glow-rgb), 0.85), 0 0 30px 8px rgba(var(--tier-diamond-glow-rgb), 0.45);
}
.rj-badge.rj-diamond::before { background: var(--tier-diamond-tex); box-shadow: inset 0 0 6px rgba(255, 255, 255, 0.85); }
.rj-badge.rj-diamond::after { animation-delay: 0.5s; background: linear-gradient(100deg, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 0.9) 50%, rgba(255, 255, 255, 0) 100%); }
.rj-badge.rj-diamond .rj-badge-word { color: var(--tier-diamond-tex-ink); }
.rj-badge.rj-diamond .rj-badge-rj {
  color: var(--tier-diamond-tex-ink);
  text-shadow: 0 0 6px rgba(var(--tier-diamond-glow-rgb), 0.95), 0 0 14px rgba(255, 255, 255, 0.8);
}

/* The shine is decoration: off for anyone who asks their device for less motion. */
@media (prefers-reduced-motion: reduce) {
  .tier-bronze::after, .tier-silver::after, .tier-gold::after, .tier-black::after, .tier-diamond::after,
  .rj-badge.rj-bronze::after, .rj-badge.rj-silver::after, .rj-badge.rj-gold::after,
  .rj-badge.rj-black::after, .rj-badge.rj-diamond::after { animation: none; display: none; }
}

/* Small, for a table row. Everything is in em, so one size change is the
   whole badge. */
.rj-badge.rj-badge-sm { font-size: 0.8rem; padding: 5px 14px 5px 12px; gap: 6px; }
.rj-badge.rj-badge-lg { font-size: 1.25rem; padding: 11px 26px 11px 22px; }

/* Somebody with no tier yet gets no badge at all rather than an empty
   plate -- an unlit sign says "broken", not "no member". */
.rj-badge-none { color: var(--text-faint); font-size: 0.9rem; }

/* --- Loyalty points ------------------------------------------------------
   A balance and a ledger, shown under the membership block but never
   inside it: points and tiers measure different things. */
.pts-headline {
  display: flex;
  align-items: baseline;
  gap: 10px;
  margin: 8px 0 2px;
}
.pts-balance {
  font-size: 2rem;
  font-weight: 800;
  line-height: 1;
}
.pts-headline small {
  color: var(--text-muted);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  font-size: 0.78rem;
}

.pts-row {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: 6px 0;
  border-bottom: 1px solid var(--border-soft);
}
.pts-row:last-child { border-bottom: none; }
.pts-row-main { display: flex; align-items: baseline; gap: 10px; }
.pts-row small { color: var(--text-muted); }
/* An earn and a correction should not look the same at a glance. */
.pts-plus { font-weight: 800; min-width: 3ch; }
.pts-minus { font-weight: 800; min-width: 3ch; color: var(--danger); }

/* The points editor on the Settings screen, beside the tier ladder. */
.pts-value-row {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 8px 0;
  border-bottom: 1px solid var(--border-soft);
  flex-wrap: wrap;
}
.pts-value-row:last-child { border-bottom: none; }
.pts-value-row label { flex: 0 0 210px; margin: 0; }
.pts-value-row input { max-width: 120px; }
.pts-value-row .pts-how {
  color: var(--text-muted);
  font-size: 0.85rem;
  letter-spacing: 0.04em;
}

/* The give-points dialog reuses the gifting overlay's shape. */
#give-points-overlay {
  position: fixed;
  inset: 0;
  z-index: 2147482000;
  background: rgba(0, 0, 0, 0.55);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 16px;
  overflow-y: auto;
}
#give-points-overlay .overlay-card {
  background: var(--surface);
  color: var(--text);
  border-radius: 14px;
  padding: 20px;
  width: 100%;
  max-width: 560px;
  box-shadow: 0 18px 50px rgba(0, 0, 0, 0.4);
}
/* Same stacked shape as the charge picker: a long reason name wraps and
   the points stay whole underneath. */
.bubble-grid .bubble.pts-reason {
  flex-direction: column;
  justify-content: center;
  gap: 4px;
  min-height: 84px;
  padding: 12px 10px;
  line-height: 1.2;
}
.pts-reason > span {
  max-width: 100%;
  font-size: 0.95rem;
  font-weight: 700;
  overflow-wrap: anywhere;
}
.pts-reason > small { font-size: 0.85rem; font-weight: 600; white-space: nowrap; }

/* --- Reports > Analysis -------------------------------------------------
   Built entirely from the existing tokens, so all three theme blocks
   (:root, the prefers-color-scheme media query and :root[data-theme])
   stay correct without a fourth place to keep in step.

   `.muted` has no global rule in this file -- several screens use the
   class and get nothing -- so it is scoped to this pane here rather than
   defined globally, which would silently restyle every other use of it. */
#report-analysis .muted { color: var(--text-muted); }
#report-analysis .error { color: var(--danger); }

.analysis-periods {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin: 0 0 16px;
}
.analysis-period {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 2px;
  padding: 8px 14px;
  border: 1px solid var(--border);
  border-radius: 18px;
  background: var(--surface);
  color: var(--text);
  cursor: pointer;
  text-align: left;
  line-height: 1.25;
}
.analysis-period:hover { border-color: var(--accent); }
.analysis-period.active {
  background: var(--accent);
  color: var(--accent-text);
  border-color: var(--accent);
}
.analysis-period-name { font-weight: 700; font-size: 0.95rem; }
.analysis-period-sub { font-size: 0.78rem; opacity: 0.7; }
/* Greyed, never hidden: a period that has not collected enough nights
   still has to be visible, or it looks like the feature is missing. */
.analysis-period.not-ready { opacity: 0.55; }
.analysis-period.not-ready.active { opacity: 1; }

.analysis-bar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 12px 14px;
  border: 1px solid var(--border-soft);
  border-radius: 10px;
  background: var(--surface);
}
.analysis-window { line-height: 1.5; }
.analysis-actions { display: flex; flex-wrap: wrap; gap: 8px; }
.analysis-ghost {
  background: transparent;
  border: 1px solid var(--border);
  color: var(--text);
}
.analysis-ghost:hover { border-color: var(--accent); }
.analysis-waiting {
  margin: 10px 0 0;
  color: var(--text-muted);
  font-size: 0.9rem;
}

.analysis-card {
  margin: 16px 0;
  padding: 16px 18px;
  border: 1px solid var(--border-soft);
  border-radius: 10px;
  background: var(--surface);
  box-shadow: var(--shadow-card);
}
.analysis-body { line-height: 1.6; overflow-wrap: anywhere; }
.analysis-body p { margin: 0 0 10px; }
.analysis-body h5 {
  margin: 16px 0 6px;
  font-size: 0.92rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--text-muted);
}
.analysis-body ul { margin: 0 0 10px; padding-left: 20px; }
.analysis-body li { margin: 0 0 6px; }
.analysis-meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 4px 14px;
  margin-top: 14px;
  padding-top: 10px;
  border-top: 1px solid var(--border-faint);
  font-size: 0.82rem;
  color: var(--text-muted);
}

.analysis-notice {
  margin: 0 0 14px;
  padding: 12px 14px;
  border: 1px solid var(--border-soft);
  border-radius: 10px;
  background: var(--bg);
}
.analysis-notice p { margin: 6px 0 0; color: var(--text-muted); font-size: 0.9rem; }

/* The payload, shown verbatim. Scrolls rather than stretching the modal,
   and wraps so a long line cannot push the dialog off a phone screen. */
.analysis-payload {
  max-height: 46vh;
  overflow: auto;
  padding: 12px;
  border: 1px solid var(--border-faint);
  border-radius: 8px;
  background: var(--bg);
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 0.74rem;
  line-height: 1.45;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}
.analysis-history { max-height: 60vh; overflow: auto; }

@media (max-width: 600px) {
  .analysis-bar { flex-direction: column; align-items: stretch; }
  .analysis-actions button { flex: 1; }
}

/* --- The membership ladder, under the client list ----------------------
   A legend, not a control: it says which badge colour means what, and
   what earns it. Built from the server's configured thresholds, so
   editing the ladder cannot leave this contradicting it. */
.tier-legend {
  margin: 22px 0 8px;
  padding: 16px 18px;
  border: 1px solid var(--border-soft);
  border-radius: 10px;
  background: var(--surface);
}
.tier-legend h4 { margin: 0 0 12px; font-size: 1rem; }
.tier-legend-grid {
  display: grid;
  /* Wide enough for a whole band on one line -- "1,000,000 a month and
     above" is the longest, and at 210px it broke across five lines.
     Five rungs side by side where there is room, wrapping down to one
     on a phone rather than squeezing five columns into 390px. */
  grid-template-columns: repeat(auto-fit, minmax(230px, 1fr));
  gap: 16px 18px;
}
.tier-legend-row {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 5px;
  min-width: 0;
}
.tier-legend-head { display: flex; align-items: center; gap: 8px; }
.tier-legend-name { font-weight: 700; font-size: 0.9rem; }
.tier-legend-band {
  color: var(--text-muted);
  font-size: 0.85rem;
  overflow-wrap: anywhere;
}
.tier-legend-note {
  margin: 14px 0 0;
  padding-top: 12px;
  border-top: 1px solid var(--border-faint);
  color: var(--text-muted);
  font-size: 0.85rem;
  line-height: 1.5;
}

/* The customer dialog got long enough to need signposting: three short
   headings break contact details, marketing and service notes apart so
   nobody scrolls past the consent box without seeing it. */
.client-section {
  margin: 20px 0 10px;
  padding-top: 14px;
  border-top: 1px solid var(--border-faint);
  font-size: 0.9rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--text-muted);
}
.client-section:first-of-type { border-top: none; padding-top: 0; }
/* Sits under the control it explains, so the explanation cannot be read
   as a label for the next field down. */
.field-help {
  margin: -2px 0 12px;
  color: var(--text-muted);
  font-size: 0.82rem;
  line-height: 1.45;
}


/* --- A bubble inside a dialog ------------------------------------------
   #modal is a .card, so it is painted with --surface -- and so is every
   .bubble on it. Measured, the cover-charge drink picker was 1.00:1
   against the dialog behind it in BOTH themes: the same colour exactly,
   with only a 1.3:1 border between them. The owner's words were that it
   was hard to see there was a button there at all, and he was right --
   what he was seeing was the label, floating.

   So on a card a bubble gets a fill one step off the card and an edge
   that clears 3:1 against it.

   The ID in the selector outranks .bubble.selected on its own, so the
   selected and disabled states are restated here rather than being
   silently overridden -- the same trap as #topbar button. */
#modal .bubble {
  background: var(--surface-hover);
  border-color: var(--bubble-edge);
}
#modal .bubble:hover:not(:disabled) {
  background: var(--surface);
  border-color: var(--accent);
}
/* The hover state is named too: :hover:not(:disabled) above outranks a
   bare .selected, and a phone keeps the bubble just tapped in :hover --
   so the choice read as white text on white until the next tap.
   .active is the same idea in the pickers that open on a value already
   set (table, tab type, cover charge, head-count): the ID rule above
   outranked their own .active rules, so the current answer did not
   stand out -- and the head-count picker had no .active rule at all. */
#modal .bubble.selected,
#modal .bubble.selected:hover:not(:disabled),
#modal .bubble.active,
#modal .bubble.active:hover:not(:disabled) {
  background: var(--accent);
  color: var(--accent-text);
  border-color: var(--accent);
}
/* Greyed, but still legibly a button that is simply not available --
   the entrance picker disables an item whose kind is already taken. */
#modal .bubble:disabled {
  opacity: 0.45;
  cursor: default;
}

/* --- One phone screen: the New Tab popup and the top of a tab ----------
   The owner's rule: on a phone, opening a tab and getting to the first
   drink must not need a scroll. Everything here is inside a phone-width
   query, so the iPad keeps the roomier layout it already has. */
@media (max-width: 560px) {
  /* The New Tab popup. Tab types three across, head-counts five across,
     and Start / Skip side by side -- the same choices, about half the
     height, so all of it sits in the popup at once. */
  #modal .new-tab-sheet h3 { margin: 0 0 8px; }
  #modal .new-tab-sheet .field { margin-bottom: 8px; }
  #modal .new-tab-sheet .field input { padding: 8px; min-height: 40px; }
  #modal .new-tab-sheet small { display: block; font-size: 0.78rem; line-height: 1.3; margin-top: 4px; }
  #modal .new-tab-types {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 6px;
    margin: 4px 0;
  }
  #modal .new-tab-types .bubble,
  #modal .party-grid .bubble {
    display: flex;
    align-items: center;
    justify-content: center;
    text-align: center;
    min-height: 40px;
    margin: 0;
    padding: 4px 6px;
    font-size: 0.92rem;
    border-radius: 12px;
    line-height: 1.15;
  }
  #modal .bubble-row.bubble-grid.party-grid {
    grid-template-columns: repeat(5, 1fr);
    gap: 6px;
    margin: 4px 0;
  }
  #modal .new-tab-actions { display: flex; gap: 8px; margin-top: 10px; }
  #modal .new-tab-actions button { flex: 1 1 0; margin: 0; min-height: 48px; padding: 8px 10px; }
  #modal .new-tab-actions .big-btn { width: auto; font-size: 1rem; }


  #screen-bill .tab-action-trio { gap: 6px; margin-bottom: 8px; }
  #screen-bill .tab-action-trio > .bubble { min-height: 48px; padding: 4px 6px; }

  /* A DB tab's client box. The warning in red and the name box are what
     matter at the till; the sentence explaining the 50% is for someone
     learning the system, and it cost three lines above the drinks. */
  #db-clients-row { margin: 2px 0 6px !important; font-size: 0.88rem; }
  #db-clients-row .db-clients-help { display: none; }
  #db-clients-row .bubble-row { margin: 2px 0 4px; }
  #db-clients-row .row { gap: 6px; flex-wrap: nowrap; }
  #db-clients-row input { flex: 1 1 auto; min-width: 0; min-height: 36px; padding: 6px 10px; }
  #db-clients-row button { margin: 0; min-height: 36px; padding: 6px 14px; font-size: 0.9rem; }

  /* The top bar on ONE line. Tabs, Admin, the two switches and the name
     all fit beside the logo if the buttons lose a little padding and the
     name gives way first: a long name is cut with an ellipsis rather than
     pushing itself onto a second row. The logo keeps its size. */
  #topbar > div { flex-wrap: nowrap; gap: 2px; min-width: 0; }
  #topbar button { padding: 6px 7px; min-height: 36px; font-size: 0.85rem; }
  #topbar .icon-btn { min-width: 33px; min-height: 36px; padding: 0; }
  #topbar .account-btn { padding: 3px 6px; min-width: 0; max-width: 118px; }
  #topbar .menu-anchor { min-width: 0; }
  #topbar .bell-btn { padding: 6px 5px; }
  #account-name, #account-role {
    display: block;
    max-width: 100%;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
  #account-role { font-size: 0.66rem; }
  #account-name { font-size: 0.8rem; }
  #topbar { gap: 4px; padding-left: 8px; padding-right: 8px; }

  /* The drink categories, three across instead of two. */
  #menu-categories .bubble-row.bubble-grid.category-grid {
    grid-template-columns: repeat(3, 1fr);
    gap: 6px;
    margin: 0;
  }
  #menu-categories .category-grid .bubble {
    min-height: 52px;
    padding: 4px 4px;
    font-size: 0.9rem;
    line-height: 1.15;
    border-radius: 14px;
  }
}


/* --- The tab's header ---------------------------------------------------
   The bill number on its own, centered; then two lines of bubbles that
   are all the same size.
   Line 1: Tabs, T# | where the tab stands | people, Show. A grid whose
   two outer columns are forced equal (minmax(0, 1fr)), so the status is
   truly centered whatever the two ends hold.
   Line 2: who the tab is for, the note, sales by, the tab type -- one
   line, the two bits of text giving way (with "...") before any button
   does. */
.bill-head { margin-bottom: 8px; }
/* The top row: the customer's name centered and biggest, over the status
   bubble; the bill number small at the far right. The empty first column
   matches the last so the name sits in the true middle. */
.bill-top {
  display: grid;
  /* Both sides keep room for the bill number, so a long name wraps onto
     a second line instead of pushing the number off the edge. */
  grid-template-columns: minmax(4.6rem, 1fr) minmax(0, auto) minmax(4.6rem, 1fr);
  align-items: center;
  gap: 8px;
  margin: 0 0 8px;
}
#bill-headline {
  grid-column: 2;
  margin: 0;
  text-align: center;
  font-size: 1.75rem;
  font-weight: 800;
  line-height: 1.15;
  color: var(--text);
  overflow-wrap: anywhere;
  max-width: 100%;
}
#bill-headline-name.no-name { color: var(--text-muted); font-weight: 700; }
/* The code under the name, then in / out / how long (the owner, 2026-10-20). */
#bill-headline-code:not(:empty) { display: block; margin-top: 2px; font-size: 0.85rem; white-space: nowrap; }
#bill-headline .bill-times:not(:empty) { display: block; margin-top: 4px; font-size: 0.8rem; font-weight: 600; color: var(--text-muted); }
/* A name that opens the client's sheet: dotted underline says tap me. */
#bill-headline-name.bill-name-link { cursor: pointer; text-decoration: underline dotted; text-underline-offset: 5px; text-decoration-thickness: 2px; }
#bill-headline-name.bill-name-link:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; border-radius: 4px; }
/* The client sheet. */
.cstats h3 .client-code { font-size: 0.85rem; margin-left: 6px; vertical-align: 2px; }
.cstats-badges { display: flex; align-items: center; gap: 10px; margin: 0 0 12px; flex-wrap: wrap; }
.cstats-points { font-weight: 700; }
.cstats-grid { max-width: none; margin-bottom: 12px; }
.cstats h4 { margin: 14px 0 6px; }
.cstats-lists { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 0 18px; }
.cstats-top { margin: 0; padding-left: 20px; }
.cstats-top li { margin: 2px 0; }
.cstats-top small, .cstats-grid small { color: var(--text-muted); }
/* The line-2 copy of the name: the headline shows it now. */
#bill-customer-display { display: none !important; }
#bill-title {
  grid-column: 3;
  justify-self: end;
  margin: 0;
  text-align: right;
  font-size: 0.8rem;
  font-weight: 500;
  letter-spacing: 0.04em;
  color: var(--text-muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.bill-line1 {
  display: grid;
  /* The status may take at most a third of the row: a long one ("Partially
     Refunded", "Parcialmente Estornada") wraps onto two lines inside its
     bubble rather than pushing the ends off the screen. */
  grid-template-columns: minmax(0, 1fr) fit-content(34%) minmax(0, 1fr);
  align-items: center;
  gap: 6px;
}
.bill-side {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 6px;
  min-width: 0;
}
.bill-side > * { flex: none; margin: 0; }
.bill-line1 > #bill-status {
  justify-self: center;
  text-align: center;
  line-height: 1.15;
}
.bill-line2 {
  display: flex;
  flex-wrap: nowrap;
  align-items: center;
  justify-content: space-between;
  gap: 6px;
  margin-top: 8px;
}
.bill-line-text {
  flex: 0 3 auto;
  min-width: 0;
  color: var(--text-muted);
  font-size: 0.9rem;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
/* Every bubble in the header, one size. */
#btn-back-to-tabs,
.bill-chip,
#btn-show-client-bill,
#bill-category-badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  box-sizing: border-box;
  min-height: 38px;
  margin: 0;
  padding: 6px 14px;
  border-radius: 999px;
  font-size: 0.92rem;
  line-height: 1.1;
  white-space: nowrap;
}
#bill-category-badge:not(.badge-button) { border: 1px solid transparent; }
.bill-line2 > .bill-chip { flex: none; }
/* A long answer ("Sales by: Staff Aoi") gives way before the line breaks. */
.bill-line2 > #btn-sales-by,
.bill-line2 > #bill-category-badge {
  flex: 0 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  display: block;
  line-height: 24px;
}

/* Line 2 is spread across the full width at every size, the owner's
   call: equal space between each piece, and the tab-type badge at the
   far right, level with the Show button above it. */
@media (min-width: 561px) {
  .bill-line2 { gap: 8px; }
}

/* On a phone: the same places, smaller bubbles and tighter spacing so
   each line stays one line. */
@media (max-width: 560px) {
  #bill-title { font-size: 0.72rem; }
  #bill-headline { font-size: 1.4rem; }
  .bill-top { margin-bottom: 6px; }
  .bill-line1, .bill-side { gap: 4px; }
  .bill-line2 { gap: 4px; margin-top: 6px; }
  #btn-back-to-tabs,
  .bill-chip,
  #btn-show-client-bill,
  #bill-category-badge {
    min-height: 32px;
    padding: 4px 9px;
    font-size: 0.78rem;
  }
  .bill-line2 > #btn-sales-by,
  .bill-line2 > #bill-category-badge { line-height: 22px; }
  .bill-line-text { font-size: 0.76rem; }
  /* A real name or note would be squeezed to nothing beside four
     buttons, so it drops to a line of its own under them. A grid keeps
     the buttons on one row, spread edge to edge with equal gaps (the 1fr
     tracks), and lets only "Sales by" shorten if room runs out. The
     placeholders ("Walk-in", "No note") give way instead. */
  .bill-line2.has-values {
    display: grid;
    grid-template-columns: auto 1fr auto 1fr minmax(0, auto) 1fr auto;
    column-gap: 4px;
    row-gap: 4px;
    align-items: center;
  }
  .bill-line2.has-values > #btn-edit-customer { grid-row: 1; grid-column: 1; }
  .bill-line2.has-values > #btn-edit-note { grid-row: 1; grid-column: 3; }
  .bill-line2.has-values > #btn-sales-by { grid-row: 1; grid-column: 5; max-width: 100%; }
  .bill-line2.has-values > #bill-category-badge { grid-row: 1; grid-column: 7; }
  /* Both share the second line, half each: the name from the left, the
     note from the right, each cut short with "..." only past its half. */
  .bill-line2.has-values > .bill-line-text.has-value {
    grid-row: 2; grid-column: 1 / -1; font-size: 0.8rem; min-width: 0; max-width: calc(50% - 4px);
  }
  .bill-line2.has-values > #bill-customer-display.has-value { justify-self: start; }
  .bill-line2.has-values > #bill-note-display.has-value { justify-self: end; text-align: right; }
  /* Only one of them: it may use the whole line. */
  .bill-line2.has-values.one-value > .bill-line-text.has-value { max-width: 100%; }
  /* The "Walk-in" / "No note" placeholders never fit on a phone (they were
     squeezed to a stray letter), and the Client and Note buttons say the
     same thing, so on a phone they are not shown at all. */
  .bill-line2 > .bill-line-text:not(.has-value) { display: none; }
  #btn-show-client-bill { gap: 4px; }
  #btn-show-client-bill svg { width: 16px; height: 16px; }
  #bill-status {
    min-height: 32px;
    padding: 4px 10px;
    font-size: 0.74rem;
    letter-spacing: 0.04em;
    gap: 5px;
  }
}
/* When line 1 would not fit (fitBillHead in app.js measures it): Show
   drops to the eye alone, its name still read out by screen readers from
   the button's label. */
.bill-head-compact #btn-show-client-bill span { display: none; }
.bill-head-compact #btn-show-client-bill { padding-left: 7px; padding-right: 7px; }
/* And if even that is not enough (a long language on a small phone), the
   back button keeps its arrow and drops its word -- again only then. */
#btn-back-to-tabs { gap: 5px; }
.bill-head-tight #btn-back-to-tabs .back-word { display: none; }
.bill-head-tight #btn-back-to-tabs { padding-left: 11px; padding-right: 11px; }
/* Last of all, a long status on a small phone ("PARCIALMENTE PAGA" at
   375): the status and the bubbles tighten their letters and padding. */
.bill-head-tighter #bill-status { font-size: 0.68rem; letter-spacing: 0; padding: 4px 6px; gap: 4px; }
.bill-head-tighter .bill-side > * { padding-left: 6px; padding-right: 6px; }
.bill-head-tighter .bill-line1, .bill-head-tighter .bill-side { gap: 3px; }
/* The narrowest phones (320px), in Japanese or Portuguese: with the bell
   (2026-10-10) the bar is short of room even once the name has given all
   it can, so the nav words give way too, cut with an ellipsis, rather
   than the bar growing wider than the phone (which pushed the New Tab
   button off the screen). The bell keeps its badges in view. */
/* Up to 430px (every iPhone), not only the narrowest: at 390px the
   Owner's bar was 20-odd px too wide, the settings button half off the
   right edge and the page wobbling sideways (QA, 2026-10-19). */
@media (max-width: 430px) {
  #topbar { gap: 3px; padding-left: 6px; padding-right: 6px; }
  #topbar > div { gap: 2px; }
  #topbar button { padding: 6px 6px; }
  #topbar .icon-btn { min-width: 32px; }
  .brand-pos { letter-spacing: 0.12em; }
}
@media (max-width: 430px) {
  #topbar > div > button { flex: 0 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  /* The name gives way first, down to a tappable sliver: it is the way
     out (Logout lives under it), so it never shrinks to nothing. The
     button must shrink inside its anchor too, or it pokes past the edge.
     The account menu is a sibling of the button, so it is unaffected. */
  #topbar .menu-anchor { flex: 0 10 auto; min-width: 36px; }
  #topbar .menu-anchor > .account-btn { min-width: 36px; max-width: 100%; overflow: hidden; }
  #topbar .bell-btn { flex: none; overflow: visible; }
}
@media (max-width: 359px) {
  /* On the very smallest phones only, the dot goes too; the colour and the
     word still say where the tab stands. */
  .bill-head-tighter #bill-status::before { display: none; }
}
/* The smallest phones (an original iPhone SE is 320 wide). */
@media (max-width: 359px) {
  #bill-status { padding: 4px 8px; letter-spacing: 0; font-size: 0.7rem; }
  #btn-back-to-tabs, .bill-chip, #bill-category-badge { padding: 4px 7px; }
}
.show-client-btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  margin: 0;
  border-color: #d4af37;
  white-space: nowrap;
}
.show-client-btn svg { display: block; flex: none; }
.client-running-note {
  text-align: center;
  color: var(--text-muted);
  font-size: 0.9rem;
  margin: 10px 0 0;
}

/* The account menu's one-button Appearance: icon, then the look it is on. */
.theme-cycle-btn { display: inline-flex; align-items: center; justify-content: center; gap: 8px; white-space: nowrap; }
/* Wide enough that "Appearance: Auto" (and its Japanese and Portuguese)
   stays on one line. */
#account-menu { min-width: 210px; }
.theme-cycle-btn .theme-opt-icon { display: inline-flex; }

/* The five account-menu bubbles, one size. Training, Appearance and
   Language are icons only, so nothing about their contents may set their
   size or nudge them off centre: the same width, the same height on every
   screen, and the three icons all 22px, dead centre. (The Appearance icon
   used to keep the 8px gap it had when its word sat beside it, which put
   it 4px left of centre.) #topbar in front, because `#topbar button` sets
   its own height at phone widths and would otherwise win. */
#topbar #account-menu > button {
  box-sizing: border-box;
  width: 100%;
  height: 44px;
  min-height: 44px;
  margin: 0;
  padding: 0 14px;
  gap: 0;
  font-size: 0.95rem;
  line-height: 1;
}
#topbar #account-menu .theme-opt-icon { margin: 0; width: 22px; height: 22px; vertical-align: middle; }
#topbar #account-menu > button svg { width: 22px; height: 22px; display: block; flex: none; }

/* --- New tab: the floating + on the tab list ----------------------------
   Bottom right, where a thumb already rests. White and gold, lit from the
   top left like a polished button, with a slow glint crossing it now and
   then -- noticeable without being a distraction on a busy floor. */
.new-tab-fab {
  position: fixed;
  right: calc(18px + env(safe-area-inset-right, 0px));
  bottom: calc(22px + env(safe-area-inset-bottom, 0px));
  z-index: 30;
  width: 66px;
  height: 66px;
  min-height: 0;
  margin: 0;
  padding: 0;
  border-radius: 50%;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  border: 1px solid rgba(212, 175, 55, 0.7);
  background: radial-gradient(circle at 32% 26%, #ffffff 0%, #fbf8ef 42%, #ece2c4 100%);
  box-shadow:
    0 8px 20px rgba(0, 0, 0, 0.38),
    0 0 0 4px rgba(212, 175, 55, 0.16),
    0 0 18px rgba(212, 175, 55, 0.35),
    inset 0 2px 3px rgba(255, 255, 255, 0.95),
    inset 0 -4px 8px rgba(168, 121, 31, 0.22);
  transition: transform 0.12s ease, box-shadow 0.12s ease;
}
.new-tab-fab:hover { background: radial-gradient(circle at 32% 26%, #ffffff 0%, #fdfaf2 45%, #efe6ca 100%); }
.new-tab-fab:active { transform: scale(0.94); }
.new-tab-fab svg { position: relative; z-index: 1; filter: drop-shadow(0 1px 0 rgba(120, 84, 16, 0.35)); }
.new-tab-fab::after {
  content: "";
  position: absolute;
  inset: -40%;
  background: linear-gradient(115deg, transparent 40%, rgba(255, 255, 255, 0.85) 50%, transparent 60%);
  transform: translateX(-120%);
  animation: fab-glint 4.5s ease-in-out infinite;
  pointer-events: none;
}
@keyframes fab-glint {
  0%, 70% { transform: translateX(-120%); }
  100% { transform: translateX(120%); }
}
@media (prefers-reduced-motion: reduce) {
  .new-tab-fab::after { animation: none; }
}
/* Breakage, bottom left: New Tab's mirror, in a darker rose gold. */
.breakage-fab {
  position: fixed;
  left: calc(18px + env(safe-area-inset-left, 0px));
  bottom: calc(22px + env(safe-area-inset-bottom, 0px));
  z-index: 30;
  width: 66px;
  height: 66px;
  min-height: 0;
  margin: 0;
  padding: 0;
  border-radius: 50%;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  border: 1px solid rgba(183, 110, 93, 0.75);
  background: radial-gradient(circle at 32% 26%, #fff7f4 0%, #f6e3dc 42%, #e2bfb3 100%);
  box-shadow:
    0 8px 20px rgba(0, 0, 0, 0.38),
    0 0 0 4px rgba(183, 110, 93, 0.18),
    0 0 18px rgba(183, 110, 93, 0.38),
    inset 0 2px 3px rgba(255, 255, 255, 0.9),
    inset 0 -4px 8px rgba(127, 68, 54, 0.26);
  transition: transform 0.12s ease, box-shadow 0.12s ease;
}
.breakage-fab.hidden { display: none !important; }
.breakage-fab:active { transform: scale(0.94); }
.breakage-fab svg { position: relative; z-index: 1; filter: drop-shadow(0 1px 0 rgba(90, 40, 30, 0.35)); }
.breakage-fab::after {
  content: "";
  position: absolute;
  inset: -40%;
  background: linear-gradient(115deg, transparent 40%, rgba(255, 255, 255, 0.8) 50%, transparent 60%);
  transform: translateX(-120%);
  animation: fab-glint 4.5s ease-in-out infinite;
  animation-delay: 2.2s;
  pointer-events: none;
}
@media (prefers-reduced-motion: reduce) {
  .breakage-fab::after { animation: none; }
}
/* The switch toggled for an iPhone haptic tick: there, but never seen. */
.haptic-tick { position: fixed; left: -200px; top: 0; width: 1px; height: 1px; overflow: hidden; opacity: 0; pointer-events: none; }

/* The lights on the tab list (bill_traffic_light). */
#screen-tabs .tab-card .tab-card-main { flex: 1 1 auto; }
@media (max-width: 480px) {
  /* Open, Active and All (History) on one line on a phone. On the
     narrowest phones in Japanese or Portuguese the three do not fit: the
     row then slides sideways within itself rather than widening the whole
     page (which pushed the New Tab button off the screen, 2026-10-10).
     The padding keeps the glow around a lit bubble from being clipped. */
  #screen-tabs .bubble-row { flex-wrap: nowrap; gap: 8px; max-width: 100%; overflow-x: auto; padding: 6px 4px;
    margin: 4px -4px; scrollbar-width: none; -webkit-overflow-scrolling: touch; }
  #screen-tabs .bubble-row::-webkit-scrollbar { display: none; }
  #screen-tabs .bubble-row .bubble { padding: 10px 14px; font-size: 0.95rem; white-space: nowrap; flex: none; }
}
.tab-light {
  flex: 0 0 auto;
  width: 14px;
  height: 14px;
  border-radius: 50%;
  align-self: center;
  background: var(--light-yellow);
  box-shadow: 0 0 8px var(--light-yellow);
}
.tab-card.light-green .tab-light, .light-dot-green { background: var(--light-green); box-shadow: 0 0 8px var(--light-green); }
.tab-card.light-yellow .tab-light, .light-dot-yellow { background: var(--light-yellow); box-shadow: 0 0 8px var(--light-yellow); }
.tab-card.light-red .tab-light, .light-dot-red { background: var(--light-red); box-shadow: 0 0 10px var(--light-red); }
.tab-card.light-red { box-shadow: inset 4px 0 0 var(--light-red); }
.tabs-legend {
  display: flex; flex-wrap: wrap; gap: 6px 16px;
  margin: 4px 0 12px; font-size: 0.85rem; color: var(--text-muted);
}
.tabs-legend .legend-item { display: inline-flex; align-items: center; gap: 6px; }
.tabs-legend .tab-light { width: 10px; height: 10px; }

/* Active: red glow while anything is still owed from an earlier night. */
.tabs-active-count {
  display: inline-block; min-width: 1.5em; margin-left: 6px; padding: 0 6px;
  border-radius: 999px; font-size: 0.85em; line-height: 1.5;
  background: var(--light-red); color: #fff; text-align: center;
}
.tabs-active-count.hidden { display: none !important; }
/* The list's name over the tabs: small and quiet. Staff know which list
   they picked -- the lit bubble says so -- and every line of height
   belongs to the tabs (the owner's ask, 2026-10-07). */
#screen-tabs #tabs-title {
  font-size: 0.95rem;
  font-weight: 600;
  color: var(--text-muted);
  margin: 2px 0 6px;
  letter-spacing: 0.01em;
}

/* Open, Active and All (History) glow with the worst light inside them. */
#screen-tabs .bubble.glow-green { border-color: var(--light-green); box-shadow: 0 0 0 2px var(--light-green), 0 0 12px var(--light-green); }
#screen-tabs .bubble.glow-yellow { border-color: var(--light-yellow); box-shadow: 0 0 0 2px var(--light-yellow), 0 0 14px var(--light-yellow); }
#screen-tabs .bubble.glow-red {
  border-color: var(--light-red);
  box-shadow: 0 0 0 2px var(--light-red), 0 0 16px var(--light-red);
  animation: red-glow 2.4s ease-in-out infinite;
}
#screen-tabs .bubble.glow-yellow .tabs-active-count { background: var(--light-yellow); color: #1b1b1b; }
#screen-tabs .bubble.glow-green .tabs-active-count { background: var(--light-green); color: #1b1b1b; }
/* Open with nothing open tonight: switched off, grey, no glow (2026-10-15). */
#screen-tabs .bubble.tabs-off { opacity: 0.45; filter: grayscale(1); box-shadow: none; }
@keyframes red-glow {
  0%, 100% { box-shadow: 0 0 0 2px var(--light-red), 0 0 8px var(--light-red); }
  50% { box-shadow: 0 0 0 2px var(--light-red), 0 0 22px var(--light-red); }
}
@media (prefers-reduced-motion: reduce) {
  #screen-tabs .bubble.glow-red { animation: none; }
}

/* The last tab card must be reachable, not hidden under the button. */
#screen-tabs #tabs-list { padding-bottom: 92px; }

/* --- Reports > Dashboard -------------------------------------------------
   The reports drawn as charts. Cards sit in a grid that is one column on
   a phone and two or three on an iPad or laptop; the takings chart runs
   the full width because it is the one that is read first. */
.dash-basis { color: var(--text-muted); font-size: 0.9rem; margin: 0 0 6px; }
.dash-thin {
  margin: 0 0 12px;
  padding: 8px 12px;
  border-radius: 10px;
  background: var(--surface-sunken);
  color: var(--text-muted);
  font-size: 0.9rem;
}
.dash-empty { color: var(--text-muted); }
.dash-kpis {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
  gap: 10px;
  margin: 0 0 14px;
}
.dash-kpi {
  background: var(--surface);
  border: 1px solid var(--border-soft);
  border-radius: 14px;
  padding: 10px 12px;
  box-shadow: var(--shadow-card);
  min-width: 0;
}
.dash-kpi-label { display: block; color: var(--text-muted); font-size: 0.8rem; }
.dash-kpi-value {
  display: block;
  font-size: 1.35rem;
  font-weight: 700;
  margin-top: 2px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.dash-kpi-sub { display: block; color: var(--text-muted); font-size: 0.78rem; margin-top: 2px; }
.dash-kpi-sub.up { color: var(--positive); }
.dash-kpi-sub.down { color: var(--danger); }
.dash-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
  gap: 14px;
}
.dash-card {
  background: var(--surface);
  border: 1px solid var(--border-soft);
  border-radius: 16px;
  padding: 12px 14px 14px;
  box-shadow: var(--shadow-card);
  min-width: 0;
}
.dash-card h4 { margin: 0 0 2px; }
.dash-note { margin: 0 0 8px; color: var(--text-muted); font-size: 0.82rem; }
#dash-card-nightly, #dash-card-busy, #dash-card-gmo { grid-column: 1 / -1; }
.dash-chart { position: relative; height: 240px; }
.dash-chart-tall { height: 300px; }
.dash-big { font-size: 1.2rem; font-weight: 700; margin: 6px 0; }
.dash-list { list-style: none; margin: 6px 0 0; padding: 0; font-size: 0.88rem; }
.dash-list li {
  display: flex;
  justify-content: space-between;
  gap: 10px;
  padding: 5px 0;
  border-top: 1px solid var(--border-faint);
}
.dash-list li:first-child { border-top: 0; }
.dash-list .muted { color: var(--text-muted); }
.dash-list .bad { color: var(--danger); font-weight: 600; }

/* The busy-hours grid: a table rather than a chart, so every cell can be
   read and the hours scroll sideways on a phone instead of shrinking. */
.dash-heat-wrap { overflow-x: auto; -webkit-overflow-scrolling: touch; }
.dash-heat { border-collapse: separate; border-spacing: 3px; font-size: 0.74rem; }
.dash-heat th { color: var(--text-muted); font-weight: 500; padding: 0 2px; white-space: nowrap; }
.dash-heat td {
  min-width: 30px;
  height: 26px;
  text-align: center;
  border-radius: 6px;
  color: var(--text);
  background: var(--surface-sunken);
}
.dash-heat td.hot { color: #1c1c1c; font-weight: 700; }

@media (max-width: 560px) {
  .dash-grid { grid-template-columns: 1fr; gap: 10px; }
  .dash-kpis { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px; }
  .dash-kpi-value { font-size: 1.1rem; }
  .dash-card { padding: 10px 10px 12px; }
  .dash-chart { height: 220px; }
  .dash-chart-tall { height: 280px; }
}

/* --- Reports > Analysis: card sales history ------------------------------
   Built from the Dashboard's pieces (cards, charts, headline figures). */
.card-analysis { margin-top: 28px; padding-top: 18px; border-top: 1px solid var(--border-soft); }
.card-analysis h3 { margin: 0 0 4px; }
.card-wide { grid-column: 1 / -1; }
.card-flags { list-style: none; margin: 0 0 12px; padding: 0; display: flex; flex-direction: column; gap: 6px; }
.card-flags li {
  padding: 8px 12px;
  border-radius: 10px;
  background: var(--surface-sunken);
  color: var(--text-muted);
  font-size: 0.88rem;
}
.card-table-wrap { overflow-x: auto; margin-top: 10px; }
.card-weekday-table td, .card-weekday-table th { white-space: nowrap; }
.card-weekday-table tr.best td { font-weight: 700; }
.card-best-inline { display: none; }
@media (max-width: 560px) {
  /* Every column that matters, without swiping: Best day folds under
     the average, and the headers may wrap onto two lines. */
  .card-weekday-table th.card-best-col, .card-weekday-table td.card-best-col { display: none; }
  .card-weekday-table .card-best-inline { display: block; font-weight: 400; white-space: normal; }
  .card-weekday-table th { white-space: normal; }
  .card-weekday-table td, .card-weekday-table th { padding-left: 6px; padding-right: 6px; }
  .card-weekday-table .card-best-inline span { white-space: nowrap; }
}
@media (max-width: 380px) {
  /* The smallest phones: a size down, so all five columns still fit. */
  .card-weekday-table td { font-size: 0.8rem; padding-left: 4px; padding-right: 4px; }
  .card-weekday-table th { font-size: 0.72rem; padding-left: 4px; padding-right: 4px; }
  .card-weekday-table .card-best-inline { font-size: 0.7rem; }
}
.card-yoy-table td { vertical-align: top; }
.card-yoy-table .card-up { color: var(--card-up-text); font-weight: 700; }
.card-yoy-table .card-down { color: var(--danger); font-weight: 700; }
.card-tod-summary { margin: 0 0 8px; }
.card-midnight { margin-top: 12px; }


/* --- Outstanding bills ---------------------------------------------------
   The strip on a bill that is signed for, the Mark-as-outstanding dialog,
   and the customer's signing screen. */

.outstanding-strip {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 10px 14px;
  margin: 12px 0;
  padding: 12px 14px;
  border-radius: 10px;
  background: var(--st-outstanding-bg);
  color: var(--st-outstanding-text);
  box-shadow: 0 0 0 2px rgba(var(--st-outstanding-glow), 0.4);
  font-weight: 600;
  line-height: 1.35;
}
.outstanding-strip.ob-overdue {
  background: var(--st-overdue-bg);
  color: var(--st-overdue-text);
  box-shadow: 0 0 0 2px rgba(var(--st-overdue-glow), 0.5),
              0 0 18px 3px rgba(var(--st-overdue-glow), 0.5);
}
.outstanding-strip-text { flex: 1 1 220px; }
.outstanding-strip button { min-height: 44px; flex: 0 0 auto; }

.ob-cant-pay-row { margin: 14px 0 0; text-align: center; }
.ob-cant-pay-row button { min-height: 44px; width: 100%; }
.ob-amount { font-size: 1.15rem; margin: 4px 0 12px; }
/* Paying now (2026-10-20): amount, then Cash / Card beside it. */
.ob-paynow { margin: 0 0 14px; }
.ob-paynow-row { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; margin: 4px 0; }
.ob-paynow-row input { flex: 1 1 140px; min-width: 0; }
.ob-paynow-row .bubble-row { margin: 0; }
.ob-paynow-bad { color: var(--danger); font-weight: 600; }
/* Mark as outstanding, right above Close Bill. */
.bottom-bar .ob-bar-bubble { display: block; margin: 0 auto 10px; min-height: 44px; }
.ob-amount strong { font-size: 1.35rem; }
.ob-section { margin: 18px 0 6px; }
.ob-help { margin: 0 0 8px; color: var(--text-muted); font-size: 0.9rem; }
.ob-reasons .bubble { flex: 1 1 auto; }
.ob-contact-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  gap: 0 12px;
}
.ob-who-picked {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  padding: 10px 12px;
  border: 1px solid var(--border);
  border-radius: 8px;
}
#ob-client-q { width: 100%; }
.ob-client-results { margin-top: 8px; }
.ob-consent { display: flex; align-items: center; gap: 10px; margin: 14px 0 4px; }
.ob-consent input { width: 22px; height: 22px; flex: 0 0 auto; }
.ob-warning {
  margin: 12px 0 0;
  padding: 10px 12px;
  border-radius: 8px;
  background: var(--st-partial-bg);
  color: var(--st-partial-text);
  font-weight: 600;
}

/* The signing screen. Phone first: one column, everything within reach
   of a fingertip, nothing smaller than 44px to press. The same layout
   simply gets wider on an iPad, either way up. */
#screen-sign { padding: 0 0 32px; }
.sign-top {
  max-width: 640px;
  margin: 12px auto 0;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
}
.sign-langs { display: flex; gap: 6px; flex: 1 1 auto; }
.sign-langs .bubble { min-height: 44px; padding: 0 14px; flex: 1 1 0; white-space: nowrap; }
.sign-staff-row { max-width: 640px; margin: 18px auto 0; text-align: right; }
.sign-staff-btn {
  white-space: nowrap;
  min-height: 44px;
  padding: 0 14px;
  background: none;
  border: 1px solid var(--border);
  color: var(--text-muted);
  border-radius: 999px;
}
.sign-sheet, .sign-form { max-width: 640px; }
.sign-form { margin-top: 16px; font-size: 1.05rem; }
.client-totals .sign-payby dt,
.client-totals .sign-payby dd { color: var(--text); font-weight: 700; }
.sign-agreement {
  margin: 0 0 18px;
  font-size: 1.15rem;
  line-height: 1.6;
  font-weight: 600;
}
.sign-label { display: block; font-weight: 600; margin: 0 0 6px; }
#sign-name { width: 100%; min-height: 52px; font-size: 1.2rem; margin: 0 0 16px; box-sizing: border-box; }
.sign-pad-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
.sign-clear { min-height: 44px; padding: 0 16px; }
.sign-pad-wrap { position: relative; margin: 6px 0 16px; }
.sign-pad {
  display: block;
  width: 100%;
  height: 220px;
  box-sizing: border-box;
  background: var(--sign-pad-bg);
  border: 2px dashed var(--sign-pad-edge);
  border-radius: 10px;
  /* The finger draws; it must not scroll or zoom the page underneath. */
  touch-action: none;
  -webkit-user-select: none;
  user-select: none;
  cursor: crosshair;
}
.sign-pad-hint {
  position: absolute;
  left: 0; right: 0; top: 50%;
  transform: translateY(-50%);
  text-align: center;
  color: var(--sign-pad-hint);
  pointer-events: none;
  font-size: 1.05rem;
}
.sign-confirm { width: 100%; min-height: 56px; font-size: 1.2rem; }
.sign-confirm:disabled { opacity: 0.45; cursor: not-allowed; }
.sign-error { color: var(--danger); font-weight: 600; margin: 12px 0 0; min-height: 1.2em; }
.sign-done {
  max-width: 520px;
  margin: 18vh auto 0;
  padding: 0 16px;
  text-align: center;
}
.sign-done-check { font-size: 4rem; margin: 0; color: var(--st-paid-bg); }
.sign-done-text { font-size: 1.4rem; font-weight: 700; line-height: 1.5; margin: 12px 0 32px; }
@media (min-width: 700px) {
  .sign-pad { height: 300px; }
}
@media (max-width: 560px) {
  .sign-top, .sign-staff-row { padding: 0 12px; }
  .sign-langs .bubble { padding: 0 8px; font-size: 0.95rem; }
  .sign-sheet, .sign-form { margin-left: 12px; margin-right: 12px; padding: 20px 16px; }
}


/* ---------------------------------------------------------------------
   Complimentary mode
   ---------------------------------------------------------------------
   The same idea as Training -- a lit rim and notches nothing can cover --
   because the danger is the same: a tap on this screen does something
   different from usual. Driven by body.comp-mode alone. */
#comp-glow, .comp-bar { display: none; }
body.comp-mode #comp-glow {
  display: block;
  position: fixed;
  inset: 0;
  z-index: 2147483000;
  pointer-events: none;
  box-shadow:
    inset 0 0 10px 0 rgba(var(--comp-rgb), 0.85),
    inset 0 0 28px 4px rgba(var(--comp-rgb), 0.35),
    inset 0 0 54px 10px rgba(var(--comp-rgb), 0.14);
}
.comp-bar {
  position: fixed;
  left: 50%;
  transform: translateX(-50%);
  z-index: 2147483001;
  align-items: center;
  justify-content: center;
  pointer-events: none;
  padding: 4px 18px;
  background: var(--comp-notch-bg);
  box-shadow: 0 0 16px rgba(var(--comp-rgb), 0.65);
}
.comp-bar span {
  color: var(--comp-notch-ink);
  font-weight: 800;
  font-size: 0.72rem;
  letter-spacing: 0.2em;
  white-space: nowrap;
}
#comp-bar-top { top: 0; border-radius: 0 0 13px 13px; }
#comp-bar-bottom { bottom: 0; border-radius: 13px 13px 0 0; }
body.comp-mode .comp-bar { display: flex; }

/* Both at once (a trainee giving a drink away): one rim, magenta, because
   Complimentary is what changes the next tap; Training keeps the top
   notch and its darker page, COMPLIMENTARY takes the bottom notch. */
body.training-mode.comp-mode #training-glow,
body.training-mode.comp-mode #training-bar-bottom,
body.training-mode.comp-mode #comp-bar-top { display: none !important; }

/* Never on the customer's screens. */
body.client-view #comp-glow,
body.client-view .comp-bar { display: none !important; }

.comp-color-rows .bubble-swatch { display: inline-flex; align-items: center; justify-content: center;
  min-width: 34px; height: 24px; border-radius: 6px; font-weight: 800; vertical-align: middle; }

/* Complimentary cover charge: the switch and what it opens. */
.entrance-comp { border-top: 1px solid var(--border-faint); padding-top: 12px; margin-top: 12px; }
.entrance-comp-toggle { display: flex; align-items: center; gap: 10px; font-weight: 700; }
.entrance-comp-toggle input { width: 22px; height: 22px; flex: 0 0 auto; }
.entrance-comp-help { color: var(--text-muted); font-size: 0.9em; margin: 4px 0 8px; }
#entrance-comp-fields select, #entrance-comp-fields input { width: 100%; margin-bottom: 8px; }

/* --- Outstanding bills page and its detail view --------------------------
   Built from the tokens already in the three theme blocks: the status
   chips carry the colour, the rest is the ordinary card and table look. */
.ob-page-help { margin: 0 0 12px; color: var(--text-muted); font-size: 0.92rem; line-height: 1.45; }
.ob-summary { margin: 0 0 12px; }
.ob-status-filter { margin: 0 0 10px; }
.ob-filter-row { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; margin: 0 0 10px; }
.ob-filter-row input[type="search"] { flex: 1 1 220px; min-width: 0; min-height: 44px; }
.ob-filter-row button { min-height: 44px; flex: 0 0 auto; }
.ob-filters {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
  gap: 0 12px;
  padding: 12px 14px 4px;
  margin: 0 0 10px;
  border: 1px solid var(--border-soft);
  border-radius: 10px;
  background: var(--surface);
}
.ob-filters.hidden { display: none !important; }
.ob-filters .field input, .ob-filters .field select { width: 100%; min-width: 0; }
.ob-count { margin: 4px 0 8px; color: var(--text-muted); font-size: 0.9rem; }
#ob-table td small { color: var(--text-muted); }
#ob-table .status-chip { margin: 2px 0; }
#ob-table tr.ob-row-overdue > td:first-child { box-shadow: inset 4px 0 0 var(--st-overdue-bg); }
#ob-table button[data-ob-open] { min-height: 40px; }
#ob-table .ob-nowrap { white-space: nowrap; }
/* How long a debt has been owed: quiet past the chase-after setting,
   red past the escalate setting. */
.ob-age {
  display: inline-block; margin: 2px 0; padding: 3px 10px; border-radius: 999px;
  border: 1px solid var(--border); color: var(--text-muted);
  font-size: 0.8rem; font-weight: 700; white-space: nowrap;
}
.ob-age-escalate { border-color: var(--danger); color: var(--danger); }
/* Ten columns do not fit the back office's width in any orientation or
   language, so above phone size the list is cards too, four across.
   Labels come from labelStackedTable. */
@media (min-width: 561px) {
  #ob-table, #ob-table tbody { display: block; width: 100%; }
  #ob-table thead { display: block; }
  #ob-table thead tr { display: flex; flex-wrap: wrap; gap: 6px; padding: 8px 12px; border-bottom: 1px solid var(--border-faint); }
  #ob-table thead th { display: none; }
  #ob-table thead th.sortable {
    display: inline-block; padding: 6px 12px; border: 1px solid var(--border);
    border-radius: 999px; font-size: 0.85rem; background: var(--surface);
  }
  #ob-table thead th.sortable.sorted { border-color: var(--accent); }
  #ob-table tr {
    display: grid; grid-template-columns: repeat(4, minmax(0, 1fr));
    gap: 8px 14px; padding: 12px 14px; border-bottom: 1px solid var(--border-faint);
  }
  #ob-table td { padding: 0; border: 0; min-width: 0; overflow-wrap: anywhere; }
  #ob-table td[data-label]::before { content: attr(data-label); display: block; font-size: 0.75rem; color: var(--text-muted); }
  #ob-table td:first-child { grid-column: 1 / -1; font-weight: 600; }
  #ob-table td:first-child::before { display: none; }
  #ob-table td.stack-full { grid-column: 1 / -1; }
  #ob-table td.ob-status-cell { grid-column: span 2; }
  #ob-table td.ob-status-cell .status-chip { white-space: nowrap; }
  #ob-table td.stack-actions { grid-column: 1 / -1; display: flex; justify-content: flex-end; }
  #ob-table tr.ob-row-overdue { box-shadow: inset 4px 0 0 var(--st-overdue-bg); }
  #ob-table tr.ob-row-overdue > td:first-child { box-shadow: none; }
}
@media (max-width: 560px) {
  .ob-filter-row input[type="search"] { flex-basis: 100%; }
  /* On the phone cards the status gets the whole line, so the two chips
     sit side by side instead of breaking into tall pills. */
  #ob-table td.ob-status-cell { grid-column: 1 / -1; }
  #ob-table td.ob-status-cell .status-chip { white-space: nowrap; }
  /* The overdue mark moves to the card's own edge, clear of the name. */
  #ob-table tr.ob-row-overdue { box-shadow: inset 4px 0 0 var(--st-overdue-bg); }
  #ob-table tr.ob-row-overdue > td:first-child { box-shadow: none; }
  .ob-filter-row button { flex: 1 1 0; min-width: 0; padding-left: 10px; padding-right: 10px; }
}

.ob-d-meta { margin: 0 0 10px; color: var(--text-muted); line-height: 1.8; }
.ob-d-amounts {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(130px, 1fr));
  gap: 8px 14px;
  margin: 0 0 6px;
}
.ob-d-amounts > div { padding: 8px 10px; border-radius: 8px; background: var(--surface-sunken); min-width: 0; }
.ob-d-amounts dt { font-size: 0.82rem; color: var(--text-muted); margin: 0 0 2px; }
.ob-d-amounts dd { margin: 0; font-weight: 600; overflow-wrap: anywhere; }
.ob-d-balance dd { font-size: 1.25rem; font-weight: 800; }

.ob-sig {
  border: 1px solid var(--border-soft);
  border-radius: 10px;
  padding: 10px 12px;
  margin: 0 0 10px;
}
.ob-sig-head { display: flex; flex-wrap: wrap; justify-content: space-between; gap: 4px 12px; margin: 0 0 6px; }
.ob-sig-head span { color: var(--text-muted); font-size: 0.9rem; }
/* The ink is always black on white, whatever the theme: it is a copy of
   what the customer saw and signed. */
.ob-sig-img {
  display: block;
  width: 100%;
  max-width: 480px;
  height: auto;
  background: var(--sign-pad-bg);
  border: 1px solid var(--sign-pad-edge);
  border-radius: 6px;
  margin: 0 0 8px;
}
.ob-sig-meta, .ob-sig-check { margin: 0 0 6px; font-size: 0.9rem; color: var(--text-muted); }
.ob-sig-text {
  margin: 0 0 6px;
  font-size: 0.85rem;
  line-height: 1.45;
  white-space: pre-wrap;
  max-height: 9em;
  overflow: auto;
  padding: 6px 8px;
  background: var(--surface-sunken);
  border-radius: 6px;
}
.ob-sig-voided { opacity: 0.7; border-style: dashed; }
.ob-sig-voided .ob-sig-img { filter: grayscale(1); }
.ob-sig-void { margin: 0; color: var(--danger); font-size: 0.9rem; }

.ob-d-deliver { display: flex; flex-wrap: wrap; gap: 8px; margin: 10px 0 0; }
.ob-d-deliver select { flex: 1 1 130px; min-width: 0; min-height: 44px; }
.ob-d-deliver input { flex: 2 1 180px; min-width: 0; min-height: 44px; }
.ob-d-deliver button { flex: 1 1 auto; min-height: 44px; }
.ob-d-actions { flex-wrap: wrap; }
.ob-d-actions button { min-height: 44px; }

/* Settings seen but not changeable (a manager, by default). */
.settings-readonly-note {
  margin: 0 0 14px; padding: 10px 14px; border-radius: 10px;
  border: 1px solid var(--border); background: var(--surface-sunken);
  color: var(--text-muted); font-weight: 600;
}
#admin-settings.settings-readonly input:disabled,
#admin-settings.settings-readonly select:disabled { opacity: 0.75; cursor: not-allowed; }

/* --- Custom drinks -------------------------------------------------------
   A drink typed in at the till. Its bubble is dashed so it reads as "not
   a real category" next to the ones the owner set up; the count says how
   many are waiting for their recipe. Colors are existing tokens only. */
.custom-drinks-bubble { border-style: dashed; }
/* No color set in Admin: a light gray so it still stands out next to the
   colored categories (the owner's ask), with the dashed edge kept. */
#menu-categories .custom-drinks-bubble:not(.has-color) {
  background: var(--custom-cat-bg); color: var(--custom-cat-ink); border-color: var(--custom-cat-edge);
}
.custom-drinks-bubble.has-color { border-style: solid; }
.custom-drinks-count {
  display: inline-block;
  min-width: 1.5em;
  margin-left: 4px;
  padding: 0 6px;
  border-radius: 999px;
  background: var(--accent);
  color: var(--accent-text);
  font-size: 0.8em;
  line-height: 1.5;
  text-align: center;
}
.custom-drink-new-row { margin: 0 0 12px; }
.custom-drink-new-row .primary { min-height: 48px; }

.custom-drinks-panel {
  margin: 0 0 24px;
  padding: 12px 14px;
  border: 1px solid var(--border);
  border-radius: 12px;
  background: var(--surface);
}
.custom-drinks-panel h3 { margin-top: 0; }
/* A red count on a folded section's title: how many things inside still
   need a person (custom drinks to finish, DB prices typed at the till). */
.section-badge {
  display: inline-block; min-width: 22px; height: 22px; padding: 0 7px; margin-left: 8px;
  border-radius: 999px; background: var(--danger, #c0392b); color: #fff;
  font-size: 0.8rem; font-weight: 700; line-height: 22px; text-align: center; vertical-align: middle;
}
details.custom-drinks-panel > summary { margin: 0; }
.custom-drink-need {
  display: inline-block;
  padding: 2px 8px;
  border-radius: 999px;
  background: var(--danger-fill);
  color: var(--danger-text);
  font-size: 0.9em;
}
.custom-drink-ready { font-weight: 600; }
.custom-drink-ready::before { content: "\2713  "; }
.custom-drink-actions { white-space: nowrap; }
.custom-drink-actions button + button { margin-left: 6px; }
.custom-drink-note {
  margin: 0 0 12px;
  padding: 10px 12px;
  border-left: 4px solid var(--accent);
  background: var(--surface-sunken);
  border-radius: 6px;
}
/* Stock that would go below zero: worth a second look at the count. */
.custom-drink-negative { color: var(--danger); font-weight: 800; }

/* --- Schedule grid (Staff Salary) ----------------------------------------
   One row per person, one column per night. The grid scrolls sideways in
   its own box with the names pinned, so a month fits a phone without the
   page itself scrolling. Shift types are told apart by tint and by word,
   never by color alone. Existing tokens only. */
.sched-section { margin: 0 0 28px; }
.sched-head { align-items: center; flex-wrap: wrap; gap: 8px; }
.sched-head h3 { margin: 0; }
.sched-view { margin: 0; }
.sched-nav { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin: 10px 0 6px; }
.sched-nav #sched-prev, .sched-nav #sched-next { min-width: 44px; min-height: 44px; font-size: 1.3rem; }
.sched-nav strong { min-width: 10em; text-align: center; }

.sched-scroll {
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  max-width: 100%;
  border: 1px solid var(--border);
  border-radius: 10px;
  background: var(--surface);
}
.sched-grid { border-collapse: separate; border-spacing: 0; width: 100%; }
.sched-grid th, .sched-grid td {
  padding: 3px;
  text-align: center;
  border-bottom: 1px solid var(--border-faint);
  white-space: nowrap;
}
.sched-grid .sched-name {
  position: sticky;
  left: 0;
  z-index: 2;
  background: var(--surface);
  text-align: left;
  min-width: 6.5rem;
  max-width: 9rem;
  border-right: 1px solid var(--border);
}
.sched-grid .sched-name button {
  width: 100%;
  min-height: 44px;
  text-align: left;
  white-space: normal;
  line-height: 1.15;
  border-radius: 8px;
  padding: 4px 8px;
}
.sched-date button {
  display: flex;
  flex-direction: column;
  align-items: center;
  width: 100%;
  min-height: 44px;
  padding: 2px 4px;
  border-radius: 8px;
  line-height: 1.1;
}
.sched-wd { font-size: 0.75rem; color: var(--text-muted); }
.sched-dn { font-weight: 700; }
.sched-date.sched-tonight button { box-shadow: inset 0 -3px 0 var(--accent); }

.sched-cell button {
  width: 100%;
  min-width: 3.9rem;
  min-height: 48px;
  padding: 3px 2px;
  border-radius: 8px;
  border: 1px solid var(--border-soft);
  background: var(--surface);
  color: var(--text);
  font-size: 0.8rem;
  line-height: 1.15;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
}
.sched-cell .sched-t { font-weight: 700; }
.sched-cell .sched-a { color: var(--text-muted); font-variant-numeric: tabular-nums; }
.sched-cell.sched-t-full button { background: rgba(var(--pay-glow), 0.16); }
.sched-cell.sched-t-half button { background: rgba(var(--pay-glow), 0.07); }
.sched-cell.sched-t-manual button { background: var(--surface-sunken); border-color: var(--accent); }
.sched-cell.sched-t-absent .sched-t { color: var(--danger); }
/* After tonight: on the schedule, owed nothing yet. */
.sched-cell.sched-planned button { border-style: dashed; }
.sched-cell.sched-planned .sched-t { font-style: italic; }
.sched-total, .sched-daytotal { font-variant-numeric: tabular-nums; padding: 3px 8px; }
.sched-grid tfoot th, .sched-grid tfoot td { border-top: 2px solid var(--border); border-bottom: 0; }

/* The letter is the thing to read on a square: F, H, C or A. */
.sched-cell .sched-t { font-size: 1.15rem; line-height: 1.1; }
.sched-cell .sched-t small { font-size: 0.7rem; font-weight: 600; }
.sched-legend { margin: 8px 0 0; font-size: 0.9rem; color: var(--text-muted); }

/* The shift popup. Four big choices, each with its letter, so the same
   letter the square will show is the thing that was tapped. */
.sched-types {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 10px;
  margin: 12px 0;
}
.sched-type {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  min-height: 84px;
  padding: 10px 8px;
  border-radius: 14px;
  border: 1px solid var(--border);
  background: var(--surface);
  color: var(--text);
  font-weight: 600;
}
.sched-type small { color: var(--text-muted); font-weight: 500; min-height: 1em; }
.sched-type-l { font-size: 1.7rem; font-weight: 800; line-height: 1; }
.sched-type.sched-t-full { background: rgba(var(--pay-glow), 0.16); }
.sched-type.sched-t-half { background: rgba(var(--pay-glow), 0.07); }
.sched-type.sched-t-manual { border-color: var(--accent); }
.sched-type.sched-t-absent .sched-type-l { color: var(--danger); }
.sched-type:disabled { opacity: 0.45; }
.sched-now { margin: 0 0 4px; font-weight: 600; }
.sched-tick-bar { display: flex; gap: 8px; margin: 6px 0; }
.sched-ticks { gap: 8px; max-height: 40vh; overflow-y: auto; }
.sched-tick { min-height: 44px; }
.sched-tick-now {
  display: inline-block;
  margin-left: 4px;
  padding: 0 6px;
  border-radius: 999px;
  border: 1px solid currentColor;
  font-size: 0.75em;
  font-weight: 800;
}
#sched-custom-fields { margin: 4px 0 12px; }

/* Equipment: shelf and back side by side in the count dialogs. */
.equip-count-pair { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px; }
.equip-split { color: var(--text-muted); white-space: nowrap; }

/* New Tab popup: table and sales-by side by side, DB names as chips. */
.new-tab-two { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px; }
.new-tab-two select { width: 100%; min-height: 44px; }
.new-tab-db-add { display: flex; gap: 8px; align-items: stretch; }
.new-tab-db-add input { flex: 1 1 auto; min-width: 0; min-height: 40px; padding: 8px; }
.new-tab-db-add button { flex: 0 0 auto; min-height: 40px; padding: 6px 12px; margin: 0; white-space: nowrap; }
.new-tab-db-list { display: flex; flex-wrap: wrap; gap: 6px; margin: 6px 0 0; }
.new-tab-db-list:empty { display: none; }
.new-tab-db-chip {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 3px 4px 3px 10px;
  border-radius: 999px;
  background: var(--accent);
  color: var(--accent-text);
  font-size: 0.9rem;
  font-weight: 600;
}
.new-tab-db-chip .link-btn { color: var(--accent-text); min-width: 32px; min-height: 32px; padding: 0; }
@media (max-width: 560px) {
  #modal .new-tab-two .field { margin-bottom: 8px; }
  #modal .new-tab-two select { min-height: 40px; padding: 6px; }
  #modal .new-tab-sheet small.new-tab-hint-db { display: none; }
}
/* Short phones: the same popup, a little tighter, so Start and Skip stay
   on screen. */
@media (max-width: 560px) and (max-height: 700px) {
  #modal .new-tab-sheet h3 { margin: 0 0 4px; font-size: 1.1rem; }
  #modal .new-tab-sheet .field { margin-bottom: 6px; }
  #modal .new-tab-sheet .field input,
  #modal .new-tab-two select { min-height: 36px; padding: 5px 8px; }
  #modal .new-tab-types .bubble,
  #modal .party-grid .bubble { min-height: 36px; }
  #modal .new-tab-sheet small { display: none; }
  #modal .new-tab-actions { margin-top: 6px; }
  #modal .new-tab-actions button { min-height: 44px; }
  /* The field names move inside the boxes (placeholders, and the first
     line of each list), which gives back a line per field. */
  #modal .new-tab-sheet .nt-lab { display: none; }
  #modal .new-tab-types .bubble { font-size: 0.85rem; padding: 2px 4px; }
  #modal .new-tab-db-chip { padding: 1px 2px 1px 10px; }
  #modal .new-tab-db-chip .link-btn { min-width: 28px; min-height: 28px; }
}

/* Settings > Sales by: the owner's list of answers. */
.src-row .src-note { color: var(--text-muted); font-size: 0.85em; margin-left: 6px; }
.src-row [data-src-move] { min-width: 40px; }
.src-add-row { display: flex; gap: 8px; margin-top: 10px; max-width: 460px; }
.src-add-row input { flex: 1 1 auto; min-width: 0; }
.src-add-row button { flex: 0 0 auto; margin: 0; }

/* Outstanding: a bill saved with no way to reach the customer. */
.ob-nocontact {
  display: inline-block; font-size: 0.75em; font-weight: 600;
  padding: 1px 8px; border-radius: 999px; vertical-align: middle;
  color: var(--danger); border: 1px solid var(--danger);
}

/* New Tab popup: people, table and Sales by as small tap bubbles (the
   owner's rule: taps, not drop-downs). Five across for the numbered
   ones, wrapping for the named ones. */
#modal .nt-grid { display: grid; grid-template-columns: repeat(5, minmax(0, 1fr)); gap: 6px; margin: 2px 0; }
#modal .nt-flow { display: flex; flex-wrap: wrap; gap: 6px; margin: 2px 0; }
#modal .nt-staff-picks { margin-top: 6px; padding-top: 6px; border-top: 1px dashed var(--border-soft); }
#modal .bubble.nt-chip {
  display: flex; align-items: center; justify-content: center; text-align: center;
  min-height: 36px; margin: 0; padding: 4px 10px;
  font-size: 0.92rem; line-height: 1.15; border-radius: 10px;
}
/* Start and Skip stay at the bottom of the popup while it scrolls, so
   they are always one tap away however tall the choices get. */
#modal .new-tab-actions {
  position: sticky; bottom: -20px; z-index: 1;
  margin: 10px -20px -20px; padding: 10px 20px 14px;
  background: var(--surface);
  border-top: 1px solid var(--border-soft);
}
@media (max-width: 560px) and (max-height: 700px) {
  #modal .bubble.nt-chip { min-height: 32px; font-size: 0.85rem; padding: 2px 6px; }
  #modal .nt-grid, #modal .nt-flow { gap: 5px; }
}
/* The 10+ party bubble is a drop-down dressed as a bubble. */
#modal .nt-grid select.bubble.party-more {
  width: 100%; -webkit-appearance: none; appearance: none;
  text-align: center; text-align-last: center;
  border: 2px solid var(--bubble-edge); border-radius: 10px;
  background: var(--surface-hover); color: var(--text); font-weight: 600;
  padding: 4px 6px; margin: 0;
}
#modal .nt-grid select.bubble.party-more.selected {
  background: var(--accent); color: var(--accent-text); border-color: var(--accent);
}

/* --- Order screen: drinks to ADD look different from lines ON the tab.
   A drink button is outlined, with its price and a round "+" on the
   right; a tab line stays a flat card. (The owner found the two too
   alike.) */
.menu-item-btn {
  display: flex; align-items: center; gap: 10px;
  text-align: left;
  background: var(--surface-hover);
  border: 2px solid var(--bubble-edge);
  color: var(--text);
  padding: 8px 10px 8px 14px;
}
.menu-item-btn .mi-name { flex: 1 1 auto; min-width: 0; font-weight: 600; }
.menu-item-btn .mi-price { flex: 0 0 auto; font-weight: 700; white-space: nowrap; }
/* Priced on the night: a word where the price would be. */
.menu-item-btn .ask-price-tag { font-weight: 600; font-style: italic; color: var(--text-muted); }
.menu-item-btn .mi-add {
  flex: 0 0 auto; width: 34px; height: 34px; border-radius: 50%;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--accent); color: var(--accent-text);
  font-size: 1.3rem; font-weight: 700; line-height: 1;
}
.menu-item-btn:disabled .mi-add { opacity: 0.35; }

/* Whole bottles: a small gold "Bottle" tag, and a quiet gold edge -- on
   the order screen and on the tab. Moderately subtle on purpose. */
.bottle-tag {
  display: inline-block; vertical-align: middle;
  padding: 1px 7px; border-radius: 999px;
  font-size: 0.72rem; font-weight: 700; letter-spacing: 0.02em;
  color: var(--tier-gold-ink);
  background: rgba(var(--tier-gold-rgb), 0.85);
}
.menu-item-btn.is-bottle { border-color: rgba(var(--tier-gold-rgb), 0.55); }
.bill-item-row.is-bottle-line {
  box-shadow: var(--shadow-row), inset 0 0 0 2px rgba(var(--tier-gold-rgb), 0.5);
  background: linear-gradient(0deg, rgba(var(--tier-gold-rgb), 0.07), rgba(var(--tier-gold-rgb), 0.07)), var(--surface);
}

/* A–Z / Price, and the half-price shisha switch, above a drink list. */
.menu-sort { display: flex; flex-wrap: wrap; gap: 6px; margin: 0 0 10px; }
.menu-sort-btn, .shisha-half-chip {
  min-height: 36px; padding: 4px 14px; margin: 0; border-radius: 999px;
  border: 2px solid var(--bubble-edge); background: var(--surface); color: var(--text);
  font-weight: 600; font-size: 0.9rem;
}
.menu-sort-btn.selected { background: var(--accent); color: var(--accent-text); border-color: var(--accent); }
/* Bottles / Drinks: two slim chips above the list, not a screen of their
   own. The selected one takes the category's color. */
.kind-row { display: flex; gap: 6px; margin: 0 0 8px; }
.kind-chip {
  flex: 1 1 0; min-height: 38px; padding: 4px 12px; margin: 0; border-radius: 999px;
  border: 2px solid var(--bubble-edge); background: var(--surface); color: var(--text);
  font-weight: 700; font-size: 0.92rem; display: inline-flex; align-items: center; justify-content: center; gap: 6px;
}
.kind-chip.selected { background: var(--accent); color: var(--accent-text); border-color: var(--accent); }
.kind-chip .kind-count { font-weight: 600; font-size: 0.8em; opacity: 0.75; }
.kind-chip.selected.has-color { border-color: transparent; }
.shisha-half-chip { margin-left: auto; border-color: rgba(var(--tier-gold-rgb), 0.7); }
.shisha-half-chip.selected { background: rgba(var(--tier-gold-rgb), 0.9); color: var(--tier-gold-ink); border-color: transparent; }
.half-tag {
  display: inline-block; vertical-align: middle; padding: 0 6px; border-radius: 6px;
  background: rgba(var(--tier-gold-rgb), 0.9); color: var(--tier-gold-ink); font-weight: 700; font-size: 0.8rem;
}

/* Four bubbles above the drinks now: Credit to, Complimentary, Bottle
   Set, Cover Charge. As many columns as are showing (Bottle Set hides
   when the bar has no set charge); two by two on the narrowest phones. */
.tab-action-trio.tab-action-four { grid-template-columns: none; grid-auto-flow: column; grid-auto-columns: minmax(0, 1fr); }
/* A quarter of a phone is narrower than "Complimentary": the English
   caption carries a soft hyphen, so it breaks "Compli-mentary" only when
   it has to. Slightly tighter padding and value text so "Adicionar"
   still fits in Portuguese. */
#screen-bill .tab-action-trio.tab-action-four > .bubble { padding-left: 3px; padding-right: 3px; }
.tab-action-four .bubble-cap { max-width: 100%; hyphens: manual; -webkit-hyphens: manual; }
/* Japanese: break only where the caption has a zero-width space
   (担当|スタッフ, ボトル|セット), never in the middle of a word. */
.tab-action-four .bubble-cap:lang(ja) { word-break: keep-all; font-size: 0.74em; }
.tab-action-four .bubble-value { font-size: 0.86em; }
@media (max-width: 359px) {
  .tab-action-trio.tab-action-four { grid-auto-flow: row; grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* "Staff +" on Sales by: the + says it opens the staff names. */
/* The "+" after Staff on the Sales by bubble: gold, like the Log In
   button, so it reads as "tap to pick who" (the owner, 2026-10-12). */
.staff-plus {
  display: inline-flex; align-items: center; justify-content: center;
  width: 1.35em; height: 1.35em; margin-left: 5px; border-radius: 50%;
  font-weight: 800; line-height: 1; font-size: 0.95em;
  color: var(--login-gold-ink);
  background: linear-gradient(135deg, var(--login-gold-hi) 0%, var(--login-gold) 55%, var(--login-gold-deep) 100%);
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.25);
}

/* Cover charge: a picked drink lights up -- filled, ringed, with a tick
   (and how many) -- so it is plain what the cover already includes.
   Stays lit while its kind is full rather than greying out with the rest. */
.entrance-item { position: relative; border: 2px solid var(--bubble-edge); }
.entrance-item.picked,
.entrance-item.picked:disabled,
/* #modal .bubble (an ID rule) would otherwise win on fill and opacity. */
#modal .bubble.entrance-item.picked,
#modal .bubble.entrance-item.picked:hover:not(:disabled),
#modal .bubble.entrance-item.picked:disabled {
  background: var(--accent);
  color: var(--accent-text);
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--surface), 0 0 0 5px var(--accent);
  opacity: 1;
}
.entrance-item.picked small { opacity: 0.9; }
.entrance-picked-badge {
  position: absolute; top: -8px; right: -8px;
  min-width: 24px; height: 24px; padding: 0 6px; border-radius: 12px;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--surface); color: var(--text); border: 2px solid var(--accent);
  font-size: 0.8rem; font-weight: 800; line-height: 1;
}
/* What it includes tonight: each pick as its own clear bubble. */
.entrance-row.entrance-chip {
  border: 2px solid var(--accent); border-radius: 999px;
  padding: 6px 6px 6px 14px; background: var(--surface-hover);
}
.entrance-chip .entrance-chosen { flex: 1; min-width: 0; }
.entrance-later { color: var(--text-muted); font-size: 0.9em; margin: 4px 0; }
/* Make a custom drink right in the cover's picker. */
.entrance-new-custom { display: flex; gap: 6px; flex-wrap: wrap; margin: 8px 0; }
.entrance-new-custom input { flex: 1 1 120px; min-width: 0; }
/* On the tab: the cover's Edit button, and what is still to pick. */
.btn-cover-edit { padding: 4px 12px; font-weight: 600; }
.cover-unpicked { color: var(--danger); font-weight: 600; font-size: 0.88em; }

/* Cover charge: "- n + cart" under the tapped drink, across the whole
   grid, looking like the order screen's row (own class, so the till's
   own clean-up of .qty-row never removes it mid-pick). */
.entrance-qty {
  grid-column: 1 / -1;
  display: flex; align-items: center; flex-wrap: wrap; gap: 8px;
  padding: 8px 10px; background: var(--surface-sunken); border-radius: 14px;
}
.entrance-qty .qty-total { flex: 1; color: var(--text-muted); font-size: 0.9em; }
/* A picked drink: name and price over "covered / +¥ on the tab", with
   its own - n + and the x on the right. A grid, so a narrow phone never
   wraps the parts over each other. */
.entrance-row.entrance-chip {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto auto;
  grid-template-areas: "name qty x" "note qty x";
  align-items: center;
  column-gap: 8px;
  row-gap: 2px;
  border-radius: 18px;
  padding: 8px 8px 8px 14px;
}
.entrance-chip .entrance-chosen { grid-area: name; }
.entrance-chip .entrance-over,
.entrance-chip .entrance-covered { grid-area: note; white-space: normal; }
.entrance-chip .entrance-chip-qty { grid-area: qty; }
.entrance-chip .entrance-remove { grid-area: x; }
.entrance-chip-qty { display: inline-flex; align-items: center; gap: 6px; }
.entrance-chip-qty .qty-step { min-width: 36px; min-height: 36px; padding: 0; }
.entrance-chip-qty .qty-count { min-width: 1.4em; text-align: center; font-weight: 700; }

/* --- The basket ---------------------------------------------------------
   Pinned to the bottom of the screen while a tab is open and something
   has been tapped. Compact on purpose -- smaller type, tight rows, the
   lines scroll past a few -- so the menu above keeps the room. The screen
   gets the bar's height as bottom padding (--basket-h, set by the till)
   so nothing hides behind it. */
.basket-bar {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 7;
  margin: 0 auto; max-width: 1100px;
  padding: 8px 12px calc(10px + env(safe-area-inset-bottom, 0px));
  background: var(--surface);
  border-top: 2px solid var(--accent);
  border-radius: 18px 18px 0 0;
  box-shadow: 0 -8px 24px rgba(0, 0, 0, 0.18);
  font-size: 0.86rem;
}
body.has-basket #screen-bill { padding-bottom: calc(var(--basket-h, 0px) + 12px); }
body.has-basket .bottom-bar { bottom: var(--basket-h, 0px); }
.basket-head { display: flex; align-items: center; justify-content: space-between; gap: 8px; margin-bottom: 4px; }
.basket-title { font-weight: 700; }
.basket-clear {
  margin: 0; min-height: 30px; padding: 2px 12px; font-size: 0.82rem; border-radius: 999px;
  background: transparent; border: 1px solid var(--bubble-edge); color: var(--text-muted);
}
.basket-lines { max-height: 34vh; overflow-y: auto; -webkit-overflow-scrolling: touch; }
.basket-line {
  display: grid; grid-template-columns: minmax(0, 1fr) auto auto;
  grid-template-areas: "name qty total" "chips chips chips";
  align-items: center; column-gap: 8px; row-gap: 4px;
  padding: 5px 0; border-bottom: 1px solid var(--border-soft);
}
.basket-line:last-child { border-bottom: 0; }
.basket-name { grid-area: name; min-width: 0; font-weight: 600; overflow-wrap: anywhere; }
.basket-qty { grid-area: qty; display: inline-flex; align-items: center; gap: 4px; }
.basket-qty .qty-step { min-width: 36px; min-height: 36px; font-size: 1.15rem; border-radius: 10px; padding: 0; }
.basket-qty .qty-count { min-width: 1.6em; font-size: 1rem; }
.basket-total { grid-area: total; min-width: 5.5em; text-align: right; font-weight: 700; font-variant-numeric: tabular-nums; }
.basket-chips { grid-area: chips; display: flex; flex-wrap: wrap; gap: 4px; align-items: center; }
.basket-who-label { color: var(--text-muted); font-size: 0.8rem; }
.basket-chip {
  margin: 0; min-height: 28px; padding: 2px 10px; font-size: 0.8rem; border-radius: 999px;
  border: 1px solid var(--bubble-edge); background: var(--surface); color: var(--text);
}
.basket-chip.selected { background: var(--accent); color: var(--accent-text); border-color: var(--accent); }
/* The slide, a size down from the payment one. */
.basket-slide { margin-top: 6px; }
/* How many of a drink are in the basket, on its button. */
.menu-item-btn .mi-add.has-count { font-size: 1rem; }
.menu-item-btn.in-basket { border-color: var(--accent); }

/* Credit to / whose DB drink: the two chips on a basket line, the popup
   they open, and the pencil on a rung line. */
.basket-chip.basket-attr { color: var(--text-muted); }
.basket-chip.basket-attr.selected { color: var(--accent-text); }
.attr-h { margin: 12px 0 4px; font-size: 0.95rem; color: var(--text-muted); }
.attr-grid { margin: 6px 0 8px; }
.attr-grid > .bubble { min-height: 44px; font-size: 0.92rem; padding: 6px 10px; }
#attr-db-q { width: 100%; margin: 4px 0 6px; }
.attr-new { gap: 6px; align-items: center; margin: 0 0 8px; }
.attr-new input { flex: 1; min-width: 0; }
.attr-new button { margin: 0; }
.attr-help { color: var(--text-muted); font-size: 0.86em; margin: 4px 0 0; }
.attr-grid.attr-off > .bubble { opacity: 0.45; }
#attr-db-off { margin: -4px 0 8px; }
.attr-tag {
  display: inline-block; vertical-align: middle; padding: 0 7px; border-radius: 999px;
  font-size: 0.78em; font-weight: 700; border: 1px solid var(--bubble-edge); color: var(--text-muted);
}
.btn-line-attr { min-width: 34px; font-size: 1.05em; line-height: 1; padding: 4px 8px; }

/* --- Recipe lines and the ingredient picker (Admin › Menu › Edit) ------ */
.recipe-line {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 10px;
  padding: 6px 0;
  border-bottom: 1px solid var(--border-faint);
}
.recipe-line.is-suspicious .recipe-line-text strong { color: var(--warn-mid); }
.recipe-warn { color: var(--warn-mid); font-size: 0.85em; }
#btn-open-ing-picker { margin-top: 8px; }
.ing-picker {
  margin-top: 8px;
  padding: 10px;
  border: 1px solid var(--border);
  border-radius: 12px;
  background: var(--surface-sunken);
}
.ing-picker .ing-search { width: 100%; margin: 0 0 8px; }
.ing-cats { flex-wrap: wrap; gap: 6px; }
.ing-results {
  display: flex;
  flex-direction: column;
  gap: 4px;
  margin-top: 8px;
  max-height: 320px;
  overflow-y: auto;
}
.ing-result {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 8px;
  text-align: left;
  padding: 10px 12px;
  border: 1px solid var(--border-soft);
  border-radius: 10px;
  background: var(--surface);
  color: var(--text);
}
.ing-result small, .ing-chosen small { color: var(--text-muted); }
.ing-chosen { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; margin-bottom: 8px; }
.ing-mode, .ing-fractions { gap: 6px; margin: 6px 0; }

/* --- Suggested recipes (Admin › Menu) ---------------------------------- */
.sug-all { display: block; margin: 6px 0; }
.sug-list { display: flex; flex-direction: column; gap: 6px; max-height: 55vh; overflow-y: auto; }
.sug-row {
  display: flex; gap: 10px; align-items: flex-start;
  padding: 8px 10px; border: 1px solid var(--border-soft); border-radius: 10px; background: var(--surface);
}
.sug-row.is-ambiguous { border-color: var(--warn-mid); }
.sug-body { display: flex; flex-direction: column; gap: 2px; }
.sug-name { font-weight: 700; }
.sug-line { color: var(--text-muted); }
.sug-line select { max-width: 100%; }
.sug-skip { padding: 4px 0; color: var(--text-muted); }
#modal .sug-row { display: flex; flex-direction: row; }
#modal .sug-row > .sug-tick { flex: 0 0 auto; width: 20px; height: 20px; margin: 2px 0 0; }
#modal .sug-all > input { width: 18px; height: 18px; vertical-align: middle; margin-right: 6px; }

/* --- Recipe checks banner and recipe mass edit (Admin › Menu) ---------- */
.recipe-check-banner {
  display: block; width: 100%; text-align: left; margin: 0 0 12px;
  padding: 12px 14px; border-radius: 12px; font-weight: 600;
  border: 1px solid var(--warn-mid); background: var(--surface); color: var(--warn-mid);
}
.recipe-check-banner.hidden { display: none !important; }
.sug-replaces { color: var(--text-muted); font-size: 0.85em; font-style: italic; }
#menu-recipes-rows { display: flex; flex-direction: column; gap: 8px; }
.rmass-row {
  padding: 10px 12px; border: 1px solid var(--border-soft); border-radius: 12px; background: var(--surface);
}
.rmass-row.is-flagged { border-color: var(--warn-mid); }
.rmass-head { display: flex; flex-wrap: wrap; align-items: baseline; gap: 8px; margin-bottom: 6px; }
.rmass-glass { display: flex; align-items: center; gap: 8px; margin-bottom: 8px; font-size: 0.9rem; color: var(--text-muted); }
.rmass-glass select { margin: 0; padding: 4px 8px; min-height: 0; font-size: 0.9rem; max-width: 260px; }
.glass-table input[type="number"] { width: 90px; margin: 0; padding: 6px 8px; }
.glass-table input[type="text"] { min-width: 140px; margin: 0; padding: 6px 8px; }
.glass-table td.glass-mixer { color: var(--text-muted); white-space: nowrap; }
.glass-table tr.inactive { opacity: 0.55; }
.glass-table .glass-remove.confirming { background: var(--warn-high); color: #fff; }
.rmass-head small { color: var(--text-muted); }
.rmass-head .rmass-ok { margin-left: auto; }
.rmass-lines { display: flex; flex-direction: column; gap: 4px; }
.rmass-line { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.rmass-line .rmass-stock { flex: 1 1 140px; min-width: 0; }
.rmass-line .rmass-amount { width: 90px; }
.rmass-line .rmass-unit small { color: var(--text-muted); }
.rmass-line.is-suspicious .rmass-stock { color: var(--warn-mid); }
.rmass-remove { min-width: 40px; }
.rmass-remove.confirming { background: var(--danger-fill); color: var(--danger-text); }

/* --- Tabs › All (History): months, then a compact list ----------------- */
#tabs-list.history-mode { display: flex; flex-direction: column; gap: 6px; }
#tabs-list.history-mode #history-body { display: flex; flex-direction: column; gap: 6px; }
.history-search { padding: 0 0 2px; }
.history-search .sc-search { width: 100%; box-sizing: border-box; }
.history-results .history-month-body { display: block; }
.history-month { border: 1px solid var(--border-soft); border-radius: 12px; background: var(--surface); overflow: hidden; }
.history-month-head {
  display: flex; align-items: center; gap: 10px; width: 100%;
  padding: 10px 14px; background: none; border: none; color: var(--text); text-align: left; font-size: 1rem;
}
.history-month-name { font-weight: 700; }
.history-month-count { color: var(--text-muted); font-size: 0.9em; }
.history-caret { margin-left: auto; color: var(--text-muted); }
.history-month-body { overflow-x: auto; }
.history-month:not(.open) .history-month-body { display: none; }
.history-table { width: 100%; border-collapse: separate; border-spacing: 0; font-size: 0.8rem; }
.history-table th, .history-table td { padding: 4px 6px; border-top: 1px solid var(--border-faint); text-align: left; vertical-align: middle; }
.history-table th { color: var(--text-muted); font-weight: 600; white-space: nowrap; cursor: pointer; user-select: none; }
.history-table th.sorted { color: var(--text); }
.history-table td small { color: var(--text-muted); }
.history-table .num { text-align: right; }
.history-table .nowrap { white-space: nowrap; }
.history-table tbody tr { cursor: pointer; }
.history-table tbody tr:hover { background: var(--surface-hover); }
.history-table .tab-light { width: 10px; height: 10px; }
.history-table .status-chip { font-size: 0.72rem; padding: 1px 7px; }
.history-table .client-code { font-size: 0.75em; }
.history-mail { padding: 2px 8px; min-height: 0; font-size: 0.9rem; }
.history-loading { padding: 8px 14px; color: var(--text-muted); }
.history-table tr.light-green .tab-light { background: var(--light-green); box-shadow: 0 0 6px var(--light-green); }
.history-table tr.light-yellow .tab-light { background: var(--light-yellow); box-shadow: 0 0 6px var(--light-yellow); }
.history-table tr.light-red .tab-light { background: var(--light-red); box-shadow: 0 0 8px var(--light-red); }
.history-table .tab-light { display: inline-block; }
/* On a phone the tab lists (Open, Active, History) keep when, the NAME,
   table, people, price and status; the cells marked ph-hide wait for a
   wider screen. Named rather than counted (2026-10-20): counting columns
   broke every time one was added. */
@media (max-width: 560px) {
  .history-table .ph-hide { display: none; }
}
/* The name is what staff look for first (the owner, 2026-10-20): bold,
   and never squeezed thin by the other columns. */
.history-table .tab-name strong { font-weight: 800; }
.history-table td.tab-name { min-width: 7.5rem; }
@media (max-width: 560px) { .history-table td.tab-name { min-width: 5.5rem; } }
.tabs-table-scroll { overflow-x: auto; -webkit-overflow-scrolling: touch; max-width: 100%; }
/* A tablet holds every column: a little tighter between 561 and 1100px. */
@media (min-width: 561px) and (max-width: 1100px) {
  .history-table th, .history-table td { padding-left: 5px; padding-right: 5px; font-size: 0.85rem; }
  .history-table .status-chip { font-size: 0.75rem; padding-left: 8px; padding-right: 8px; }
}
.history-table .tab-print { padding: 2px 8px; min-height: 0; font-size: 0.95rem; }
/* History bulk edit (2026-10-20): ticked rows, and the bar at the bottom. */
.history-table.history-picking tbody tr.hb-picked td { background: var(--surface-hover); }
.history-table input.hb-pick, .history-table input.hb-all { width: 22px; height: 22px; margin: 0; }
.history-bulk-bar {
  /* Over the round buttons (30), under any dialog (50). */
  position: fixed; left: 50%; transform: translateX(-50%); bottom: 16px; z-index: 40;
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap; justify-content: center;
  width: min(560px, calc(100% - 32px)); padding: 10px 14px; border-radius: 16px;
  background: var(--surface); color: var(--text); border: 1px solid var(--border-soft);
  box-shadow: 0 10px 30px rgba(0, 0, 0, 0.25);
}
.history-bulk-bar #history-bulk-count { font-weight: 700; margin-right: auto; }
.history-bulk-bar button { min-height: 44px; }
#history-bulk-btn.selected { background: var(--accent); color: var(--accent-text); border-color: var(--accent); }

/* --- Bottles to add or fix (Suggest simple recipes) -------------------- */
.bfix-box { margin: 10px 0; padding: 10px 12px; border: 1px solid var(--warn-mid); border-radius: 12px; }
.bfix-box h4 { margin: 0 0 4px; }
.bfix-row { display: flex; gap: 10px; align-items: flex-start; padding: 8px 0; border-top: 1px solid var(--border-faint); }
#modal .bfix-row .bfix-on { width: 20px; height: 20px; margin-top: 2px; }
.bfix-body { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 4px; }
.bfix-what { font-weight: 600; }
.bfix-fields { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; }
#modal .bfix-fields .bfix-name { flex: 1 1 160px; min-width: 0; }
#modal .bfix-fields .bfix-size { width: 90px; }
.bfix-ok { color: var(--positive); font-size: 0.85em; }
.bfix-drinks { color: var(--text-muted); }

/* "+ Add ingredient": the opposite of the page -- white on a dark page,
   dark on a light one -- so it stands out in both themes. Built from the
   theme's own text and background tokens, so all three theme blocks are
   covered without new ones. */
#btn-open-ing-picker {
  background: var(--text);
  color: var(--bg);
  border: 1px solid var(--text);
  border-radius: 999px;
  padding: 8px 16px;
  font-weight: 600;
  text-decoration: none;
}
#btn-open-ing-picker:hover { opacity: 0.85; }

.copy-recipe-fold { margin-top: 10px; }
.copy-recipe-fold summary { color: var(--text-muted); cursor: pointer; font-size: 0.9em; }
.copy-recipe-fold .fold-search { width: 100%; margin: 4px 0; }

/* --- Recipe mass edit as a compact list -------------------------------- */
.contrast-bubble {
  background: var(--text); color: var(--bg); border: 1px solid var(--text);
  border-radius: 999px; padding: 4px 12px; font-weight: 600; font-size: 0.8rem;
  line-height: 1.2; min-height: 0; white-space: nowrap; cursor: pointer;
}
.contrast-bubble:hover { opacity: 0.85; }
.contrast-bubble.selected { background: transparent; color: var(--text); }
.rmass-tools { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; margin: 6px 0; }
.rmass-tools .fold-search { flex: 1 1 240px; margin: 0; }
#menu-recipes-rows { display: block; overflow-x: auto; }
.rmass-table { width: 100%; border-collapse: separate; border-spacing: 0; font-size: 0.82rem; }
.rmass-table th, .rmass-table td { padding: 4px 6px; border-top: 1px solid var(--border-faint); text-align: left; vertical-align: middle; }
.rmass-table th { color: var(--text-muted); font-weight: 600; white-space: nowrap; cursor: pointer; user-select: none; }
.rmass-table th.sorted { color: var(--text); }
.rmass-table .num { text-align: right; }
.rmass-tr.is-flagged td.rmass-name strong { color: var(--warn-mid); }
.rmass-tr.open { background: var(--surface-hover); }
/* Two lines a drink, at most: long names and recipes are cut, not wrapped. */
.rmass-table .rmass-name strong, .rmass-table .rmass-clamp {
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; overflow: hidden;
}
.rmass-table .rmass-name { min-width: 120px; }
.rmass-table .rmass-cat { color: var(--text-muted); white-space: nowrap; }
.rmass-table .rmass-sum { color: var(--text-muted); max-width: 420px; }
.rmass-table .rmass-check .tab-light { display: inline-block; width: 10px; height: 10px; box-shadow: 0 0 6px var(--light-yellow); }
.rmass-table .rmass-none { color: var(--text-faint); font-size: 0.9em; white-space: nowrap; }
.rmass-table .rmass-actions { white-space: nowrap; text-align: right; }
.rmass-table .rmass-actions .contrast-bubble + .contrast-bubble { margin-left: 4px; }
.rmass-expand > td { background: var(--surface-hover); padding: 6px 10px 10px; }
.rmass-expand .rmass-row { border: none; padding: 0; background: transparent; }
@media (max-width: 560px) {
  .rmass-table .rmass-cat, .rmass-table th:nth-child(2),
  .rmass-table td:nth-child(4), .rmass-table th:nth-child(4) { display: none; }
}

/* --- What the POS changed in the owner's stock setup (folded) ---------- */
.setup-changes { border: 1px solid var(--border-faint); border-radius: 10px; padding: 8px 12px; margin: 8px 0 12px; }
.setup-changes summary { cursor: pointer; font-weight: 600; }
.setup-changes h4 { margin: 10px 0 4px; font-size: 0.9rem; }
.setup-list { margin: 0; padding-left: 18px; columns: 2 260px; column-gap: 24px; font-size: 0.85rem; }
.setup-list li { break-inside: avoid; margin: 2px 0; }
.setup-list small { color: var(--text-muted); }
.setup-check { color: var(--warn-mid); font-weight: 600; }

/* --- Price paid on a purchase; an item's price history ---------------- */
.adjust-price-row { display: flex; gap: 8px; flex-wrap: wrap; }
.adjust-price-row input { flex: 1 1 120px; min-width: 0; }
.cost-history { margin-top: 6px; font-size: 0.85rem; }
.cost-history summary { cursor: pointer; color: var(--text-muted); }
.cost-history ul { margin: 4px 0 0; padding-left: 18px; }
.cost-history small { color: var(--text-muted); }

/* --- Today (the RJ logo) ------------------------------------------------ */
#brand { cursor: pointer; }
#brand:focus-visible { outline: 2px solid var(--accent); outline-offset: 4px; border-radius: 6px; }
.today-date { color: var(--text-muted); margin: -4px 0 12px; }
.today-info { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 12px; }
.today-card { padding: 12px 14px; min-width: 0; max-width: none; margin: 0; }
.today-card h3 { margin: 0 0 6px; font-size: 0.95rem; color: var(--text-muted); font-weight: 600; }
.today-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 4px; }
.today-time, .today-small { color: var(--text-muted); font-size: 0.85rem; }
.today-small { margin: 4px 0 0; }
.today-empty { color: var(--text-faint); margin: 0; font-size: 0.9rem; }
.today-note { margin: 6px 0 0; font-size: 0.85rem; color: var(--text-muted); }
.today-big { font-size: 1.3rem; font-weight: 700; margin: 4px 0 0; }
.today-level { display: inline-block; border-radius: 999px; padding: 2px 12px; font-weight: 700; font-size: 0.85rem;
  background: var(--surface-sunken); color: var(--text); border: 1px solid var(--border-soft); }
.today-level-busy { background: var(--text); color: var(--bg); border-color: var(--text); }
.today-level-quiet { color: var(--text-muted); }
.today-h2 { font-size: 1.05rem; margin: 18px 0 8px; }
/* Compact, so every shortcut fits on one screen: icon beside the words. */
.today-shortcuts { display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 8px; }
.today-sc { position: relative; display: flex; flex-direction: row; align-items: center; justify-content: flex-start; gap: 8px;
  min-height: 52px; border-radius: 12px; font-weight: 600; font-size: 0.9rem; line-height: 1.2; text-align: left;
  padding: 6px 26px 6px 10px; margin: 0; }
.today-sc-icon { font-size: 1.3rem; line-height: 1; flex: none; }
.today-sc-badge { position: absolute; top: 4px; right: 6px; min-width: 20px; height: 20px; padding: 0 6px; border-radius: 999px;
  background: var(--text); color: var(--bg); font-size: 0.75rem; line-height: 20px; text-align: center; }
/* The five shortcut windows (2026-10-09). */
.sc-search { width: 100%; margin: 0 0 8px; }
.sc-list { display: flex; flex-direction: column; gap: 4px; max-height: 52vh; overflow-y: auto; -webkit-overflow-scrolling: touch; }
.sc-row { display: flex; align-items: center; gap: 8px; padding: 6px 10px; border: 1px solid var(--border-faint);
  border-radius: 10px; background: var(--surface); font-size: 0.9rem; }
.sc-row > .sc-row-main { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 1px; }
.sc-row small { color: var(--text-muted); }
.sc-row button { min-height: 0; padding: 4px 12px; font-size: 0.8rem; margin: 0; flex: none; }
.sc-row input[type="number"] { width: 90px; flex: none; margin: 0; padding: 4px 6px; }
.sc-row.changed { border-color: var(--accent); }
.sc-row.is-done .sc-row-main > span { text-decoration: line-through; color: var(--text-muted); }
.sc-row.is-off { opacity: 0.8; }
.cash-grid { display: grid; grid-template-columns: 1fr auto; gap: 4px 12px; font-size: 0.9rem; margin: 4px 0 10px; }
.cash-grid .cash-total { font-weight: 700; border-top: 1px solid var(--border-faint); padding-top: 4px; }
.cash-diff { font-size: 1.2rem; font-weight: 700; margin: 4px 0 8px; }
.cash-diff.short { color: var(--danger); }
/* On the till: sold out tonight. */
.menu-item-btn.is-sold-out { opacity: 0.45; }
.menu-item-btn.is-sold-out .mi-name { text-decoration: line-through; }
.sold-out-tag { display: inline-block; font-size: 0.7rem; font-weight: 700; border: 1px solid var(--border);
  border-radius: 999px; padding: 0 6px; margin-left: 4px; text-decoration: none; }
.today-expenses { display: flex; flex-direction: column; gap: 8px; }
.today-exp-total { margin: 0; font-weight: 700; }
.today-exp-total small { font-weight: 400; color: var(--text-muted); }
.today-exp { display: flex; flex-wrap: wrap; gap: 8px 12px; align-items: center; padding: 10px 12px;
  border: 1px solid var(--border-faint); border-radius: 12px; background: var(--surface); }
.today-exp.removed { opacity: 0.55; }
.today-exp-main { display: flex; flex-direction: column; gap: 2px; flex: 1 1 200px; min-width: 0; }
.today-exp-main small { color: var(--text-muted); }
.today-exp-note { font-style: italic; }
.today-exp-photos { display: flex; flex-wrap: wrap; gap: 6px; }
.exp-thumb { width: 56px; height: 56px; object-fit: cover; border-radius: 8px; border: 1px solid var(--border-soft);
  background: var(--surface-sunken); cursor: pointer; }
.exp-prev { position: relative; display: inline-block; }
.exp-prev-x { position: absolute; top: -8px; right: -8px; width: 24px; height: 24px; min-height: 0; padding: 0;
  border-radius: 999px; line-height: 1; }
.exp-delivery { margin: 6px 0; display: flex; flex-direction: column; gap: 4px; }
.exp-dl { display: flex; align-items: center; gap: 8px; padding: 4px 8px; border-radius: 8px; background: var(--surface-sunken); }
.exp-dl.unmatched { border-left: 3px solid var(--bell-high); }
.exp-dl-name { flex: 1 1 auto; min-width: 0; font-size: 0.9rem; overflow-wrap: anywhere; }
.exp-dl-name small { color: var(--text-muted); }
.exp-dl-item { flex: 0 1 46%; min-width: 140px; max-width: 100%; margin: 0; min-height: 0; padding: 4px 6px; font-size: 0.85rem; }
.exp-photo-btn { display: inline-block; cursor: pointer; border: 1px dashed var(--border); border-radius: 999px;
  padding: 8px 16px; margin-bottom: 6px; }
.exp-photo-full { max-width: 100%; max-height: 75vh; display: block; margin: 0 auto; border-radius: 8px; }
.exp-detail-head { display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 12px; margin: 0 0 4px; }
.exp-detail-head strong { font-size: 1.4rem; }
.exp-lines { width: 100%; border-collapse: collapse; margin: 10px 0; font-size: 0.95rem; }
.exp-lines th, .exp-lines td { text-align: left; padding: 6px 8px; border-bottom: 1px solid var(--border-faint); vertical-align: top; }
.exp-lines th { font-size: 0.8rem; color: var(--text-muted); font-weight: 600; }
.exp-lines td.num, .exp-lines th.num { text-align: right; white-space: nowrap; }
.exp-lines td small { color: var(--text-muted); display: block; }
.exp-detail-photos { display: flex; flex-direction: column; gap: 10px; margin-top: 10px; }
.sched-start input { max-width: 160px; }
.today-weather { margin: 6px 0 0; font-size: 0.85rem; color: var(--text); }

/* --- Inventory: compact rows, bulk edit (2026-10-09) -------------------- */
.inv-table td, .inv-table th { padding: 4px 8px; font-size: 0.85rem; line-height: 1.25; vertical-align: middle; }
.inv-table td:last-child { white-space: nowrap; text-align: right; }
.inv-table td:last-child button { padding: 4px 12px; min-height: 0; font-size: 0.8rem; margin: 0 0 0 4px; }
.inv-table td:first-child { font-weight: 600; }
.inv-bulk-scroll { overflow-x: auto; }
.inv-bulk-table input, .inv-bulk-table select { width: 100%; min-width: 70px; padding: 4px 6px; font-size: 0.85rem; margin: 0; }
.inv-bulk-table td:first-child input { min-width: 160px; }
.inv-bulk-actions { gap: 8px; margin-top: 8px; }
/* One line a row: nothing wraps, a long name is cut with "…" (title shows it). */
.inv-table:not(.inv-bulk-table) td { white-space: nowrap; }
.inv-table:not(.inv-bulk-table) td:first-child { max-width: 220px; overflow: hidden; text-overflow: ellipsis; }
.inv-scroll { overflow-x: auto; -webkit-overflow-scrolling: touch; border-radius: 8px; }
.inv-scroll > .admin-table { border-radius: 0; }

/* --- Pay: salaries, Staff DB, DB / PR (2026-10-09) ---------------------- */
.today-pay-alert { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; padding: 8px 12px; margin: 0 0 12px;
  border: 1px solid var(--danger); border-radius: 12px; background: var(--surface); font-size: 0.9rem; }
.today-pay-alert span { flex: 1 1 220px; }
.today-pay-alert button { min-height: 0; padding: 6px 14px; margin: 0; }
/* Below the top bar, never over it (2026-10-22): over it, it hid the bell and
   the nav until the payout was answered. --topbar-h is measured by app.js. */
.pay-banner { position: fixed; left: 50%; transform: translateX(-50%); top: calc(var(--topbar-h, 56px) + 8px); z-index: 900; width: min(640px, calc(100% - 32px));
  display: flex; flex-direction: column; gap: 6px; }
.pay-banner-row { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; padding: 10px 12px; border-radius: 12px;
  background: var(--surface); color: var(--text); border: 2px solid var(--accent); box-shadow: 0 6px 24px rgba(0, 0, 0, 0.25); }
.pay-banner-row span { flex: 1 1 220px; font-weight: 600; }
.pay-banner-row button { min-height: 0; padding: 6px 14px; margin: 0; }
.pay-amount { font-weight: 700; white-space: nowrap; flex: none; }
.pay-row .sc-row-main small { white-space: normal; }
.pay-more { margin-top: 10px; }
.pay-more summary { cursor: pointer; color: var(--text-muted); font-weight: 600; margin-bottom: 6px; }
.pay-sign-head { display: flex; align-items: center; justify-content: space-between; margin: 6px 0 4px; font-size: 0.9rem; color: var(--text-muted); }
.pay-sign-head button { min-height: 0; padding: 4px 12px; margin: 0; }
.pay-sign { display: block; width: 100%; height: 160px; border-radius: 10px; border: 1px solid var(--border); background: #ffffff;
  touch-action: none; }
.pay-big { font-size: 1.6rem; font-weight: 700; margin: 4px 0; }
.pay-sig-view { position: fixed; inset: 0; z-index: 1000; background: rgba(0, 0, 0, 0.6); display: flex; flex-direction: column;
  align-items: center; justify-content: center; gap: 12px; padding: 16px; }
.pay-sig-view img { max-width: min(560px, 100%); background: #ffffff; border-radius: 10px; }
/* Pay reminders and reports: a fold like the rest of the Staff page
   (2026-10-14), no card around it any more. */
.pay-settings { max-width: none; margin: 0; padding: 0; }
.pay-settings h3 { margin-top: 0; }
/* The schedule section is a fold container now: its own margin goes. */
#admin-salary .sched-section { margin: 0; }
.pay-settings input[type="email"], .pay-settings input[type="date"] { max-width: 360px; }
.pay-type-select { min-width: 110px; padding: 4px 6px; margin: 0; font-size: 0.85rem; }

/* --- Today, 2026-10-09 (2): shortcuts first, the information much smaller */
#screen-today .today-shortcuts { margin: 0 0 12px; }
#screen-today .today-info { grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 6px; }
#screen-today .today-info-last { margin-top: 12px; }
#screen-today .today-card { padding: 6px 10px; border-radius: 10px; }
#screen-today .today-card h3 { font-size: 0.72rem; margin: 0 0 2px; text-transform: uppercase; letter-spacing: 0.03em; }
#screen-today .today-card p, #screen-today .today-card li { font-size: 0.8rem; }
#screen-today .today-card .today-big { font-size: 0.95rem; margin: 0; }
#screen-today .today-card .today-level { font-size: 0.7rem; padding: 0 8px; }
#screen-today .today-card .today-note, #screen-today .today-card .today-weather,
#screen-today .today-card .today-small { font-size: 0.72rem; margin: 2px 0 0; }
#screen-today .today-card .today-list { gap: 1px; }
#screen-today .today-h2 { font-size: 0.9rem; margin: 12px 0 6px; }

/* --- Log an expense on one phone screen; breakage and stock lists ---- */
#modal .exp-title { margin: 0 0 6px; }
#modal .exp-top { display: flex; gap: 8px; align-items: center; }
#modal .exp-top .exp-photo-btn { margin: 0; padding: 8px 12px; white-space: nowrap; flex: none; font-size: 0.85rem; }
#modal .exp-top input { flex: 1 1 auto; min-width: 0; margin: 0; }
#modal .exp-bubbles { gap: 4px; margin: 6px 0; }
#modal .exp-bubbles .bubble { padding: 4px 10px; min-height: 0; font-size: 0.8rem; margin: 0; }
#modal .exp-two { display: flex; gap: 8px; }
#modal .exp-two > * { flex: 1 1 0; min-width: 0; margin: 0; }
#modal .exp-two .field { margin: 0 0 6px; }
#modal .exp-two input { width: 100%; margin: 0; }
#modal #exp-previews:empty { display: none; }
#modal #exp-previews .exp-thumb { width: 40px; height: 40px; }
#modal .exp-lines { max-height: 26vh; overflow-y: auto; margin: 6px 0 0; border-top: 1px solid var(--border-faint); }
#modal .exp-line { display: flex; align-items: center; gap: 6px; padding: 3px 0; border-bottom: 1px solid var(--border-faint); font-size: 0.8rem; }
#modal .exp-line-name { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
#modal .exp-line-cat { flex: 0 1 130px; min-width: 0; padding: 2px 4px; margin: 0; font-size: 0.75rem; }
#modal .exp-line-amount { width: 76px; flex: none; padding: 2px 4px; margin: 0; font-size: 0.8rem; }
#modal .exp-line-x { min-height: 0; padding: 0 8px; margin: 0; }
#modal .exp-lines .field-help { margin: 4px 0; }
#modal #exp-reading { margin: 4px 0 0; }
.brk-row { cursor: pointer; }
.brk-row .brk-quick { font-weight: 700; padding: 4px 12px; }
.bubble.area-bar.selected { background: var(--accent); color: var(--bg); border-color: var(--accent); }
.bubble.area-kitchen.selected { background: var(--warn-mid); color: #fff; border-color: var(--warn-mid); }
.res-day { margin: 10px 0 2px; font-size: 0.85rem; color: var(--text-muted); }
button.sc-row { width: 100%; text-align: left; min-height: 0; margin: 0; }


/* --- The bell (2026-10-09) ---------------------------------------------- */
#topbar .bell-btn { position: relative; display: inline-flex; align-items: center; justify-content: center; line-height: 1; padding: 6px 10px; }
#topbar .bell-btn svg { display: block; flex: none; }
#topbar .bell-btn.has-unread { border-color: var(--bell-unread); }
/* Red: everything unread. Blue: tasks still to do. */
.bell-n, .bell-t { position: absolute; min-width: 18px; height: 18px; padding: 0 5px; border-radius: 9px;
  color: #ffffff; font-size: 0.7rem; font-weight: 700; line-height: 18px; text-align: center; }
.bell-n { top: -6px; right: -6px; background: var(--danger); }
.bell-t { bottom: -6px; right: -6px; background: var(--bell-unread); }
.bell-head { display: flex; align-items: center; justify-content: space-between; gap: 8px; flex-wrap: wrap; }
.bell-head h3 { margin: 0; display: inline-flex; align-items: center; gap: 8px; }
.bell-head h3 .bell-ico { flex: none; }
.bell-head-actions { display: flex; gap: 6px; }
.bell-head-actions button { min-height: 0; padding: 6px 12px; margin: 0; }
.bell-bar { display: flex; align-items: center; justify-content: space-between; gap: 8px; flex-wrap: wrap; margin: 10px 0 6px; }
.bell-bubbles, .bell-sorts { display: flex; gap: 6px; flex-wrap: wrap; }
.bell-bulk { justify-content: flex-start; margin-top: 0; }
.bell-bulk .primary { margin: 0; min-height: 36px; padding: 6px 14px; }
.bell-pick-box { width: 24px; height: 24px; margin: 0; accent-color: var(--accent); }
.bell-list.selecting .bell-row { cursor: pointer; }
.bell-bubble, .bell-sort { min-height: 0; margin: 0; padding: 5px 12px; border-radius: 999px; border: 1px solid var(--border);
  background: var(--surface); color: var(--text); font-size: 0.85rem; }
.bell-bubble.active, .bell-sort.active { background: var(--accent); border-color: var(--accent); color: var(--accent-text); }
/* Inside the bell the bubbles carry the same colors as the top-bar badges
   (the owner, 2026-10-13): red for unread POS notices, blue for tasks still
   open, gold for payments due. */
.bell-bubble-n.bell-bubble-task { background: var(--bell-unread); }
.bell-bubble-n.bell-bubble-pay { background: var(--login-gold, #c9a227); }
.bell-bubble-n { display: inline-block; min-width: 18px; padding: 0 5px; border-radius: 9px; background: var(--danger);
  color: #ffffff; font-size: 0.72rem; font-weight: 700; line-height: 18px; text-align: center; }
.bell-list { display: flex; flex-direction: column; gap: 6px; max-height: 60vh; overflow-y: auto; -webkit-overflow-scrolling: touch; }
/* "To do in the POS" (2026-10-13): one tappable row per kind of unfinished
   work, the count first, the arrow last. */
.bell-postasks { margin: 0 0 10px; }
.bell-postask { display: flex; align-items: center; gap: 10px; width: 100%; margin: 0 0 6px; padding: 8px 10px; text-align: left;
  border: 1px solid var(--border-soft); border-radius: 10px; background: var(--surface); color: var(--text); font-weight: 600; }
.bell-postask-n { flex: none; min-width: 26px; height: 26px; padding: 0 8px; border-radius: 999px; background: var(--danger); color: #fff;
  font-size: 0.85rem; line-height: 26px; text-align: center; }
.bell-postask-t { flex: 1 1 auto; min-width: 0; }
.bell-postask-t small { display: block; font-weight: 400; color: var(--text-muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.bell-postask-go { flex: none; color: var(--text-muted); font-size: 1.3rem; }
.bell-row { display: flex; align-items: center; gap: 10px; padding: 8px 10px; border: 1px solid var(--border-soft);
  border-radius: 10px; background: var(--surface); cursor: pointer; }
.bell-row:hover { background: var(--surface-hover); }
.bell-row-high { border-left: 4px solid var(--bell-high); }
.bell-row-urgent { border-left: 4px solid var(--danger); }
.bell-dot { flex: none; width: 9px; height: 9px; border-radius: 50%; background: transparent; }
.bell-row.unread .bell-dot { background: var(--bell-unread); }
.bell-row.unread .bell-title { font-weight: 700; }
.bell-main { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.bell-title { overflow-wrap: anywhere; }
.bell-meta { font-size: 0.8rem; color: var(--text-muted); }
.bell-side { flex: none; }
.bell-side button { min-height: 0; padding: 5px 10px; margin: 0; font-size: 0.8rem; white-space: nowrap; }
.bell-lock { font-size: 0.75rem; color: var(--text-muted); white-space: nowrap; }
.bell-lvl { display: inline-block; margin-right: 6px; padding: 0 6px; border-radius: 6px; font-size: 0.72rem; font-weight: 700;
  vertical-align: 1px; color: #ffffff; }
.bell-lvl-high { background: var(--bell-high); color: var(--bell-high-ink); }
.bell-lvl-urgent { background: var(--danger); }
.bell-done { color: var(--light-green); font-weight: 600; }
.bell-over { color: var(--danger); font-weight: 700; }
.bell-task { color: var(--text-muted); font-weight: 600; }
.bell-due.over { color: var(--danger); font-weight: 700; }
.bell-body { white-space: pre-wrap; overflow-wrap: anywhere; padding: 10px 12px; border-radius: 10px; background: var(--surface-sunken);
  margin: 8px 0; }
.bell-lock-note { color: var(--danger); font-weight: 600; }
.bell-field { margin: 10px 0; }
.bell-label { display: block; font-size: 0.9rem; color: var(--text-muted); margin-bottom: 4px; }
.bell-levels { display: flex; gap: 6px; flex-wrap: wrap; }
.bell-pick { display: inline-flex; align-items: center; margin: 0; cursor: pointer; }
.bell-pick input { position: absolute; opacity: 0; pointer-events: none; }
.bell-lvl-pill { padding: 6px 14px; border-radius: 999px; border: 2px solid var(--border); font-weight: 600; font-size: 0.9rem; }
.bell-lvl-pill.bell-lvl-normal { background: var(--surface); color: var(--text); }
.bell-lvl-pill.bell-lvl-high, .bell-lvl-pill.bell-lvl-urgent { background: var(--surface); color: var(--text); }
.bell-pick input:checked + .bell-lvl-pill { border-color: var(--accent); background: var(--accent); color: var(--accent-text); }
.bell-pick input:checked + .bell-lvl-pill.bell-lvl-high { border-color: var(--bell-high); background: var(--bell-high); color: var(--bell-high-ink); }
.bell-pick input:checked + .bell-lvl-pill.bell-lvl-urgent { border-color: var(--danger); background: var(--danger); color: #ffffff; }
.bell-pick input:focus-visible + .bell-lvl-pill { outline: 2px solid var(--accent); outline-offset: 2px; }
.bell-hint { font-size: 0.8rem; margin: 8px 0 0; }
.bell-textarea { display: block; }
.bell-textarea textarea { display: block; width: 100%; box-sizing: border-box; margin-top: 4px; }
.bell-check { display: flex; align-items: center; gap: 8px; margin: 4px 0; }
.bell-people { display: grid; grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); gap: 2px 10px; margin: 4px 0 0 4px; }
.bell-due-row { display: flex; gap: 10px; flex-wrap: wrap; margin-top: 4px; }
.bell-due-row label { flex: 1 1 140px; }
.bell-sent { border: 1px solid var(--border-soft); border-radius: 10px; padding: 8px 10px; background: var(--surface); }
.bell-sent summary { cursor: pointer; display: flex; flex-direction: column; gap: 2px; }
.bell-people-status { list-style: none; margin: 8px 0 0; padding: 0; display: flex; flex-direction: column; gap: 4px; }
.bell-people-status li { display: flex; justify-content: space-between; gap: 8px; font-size: 0.9rem; }
.celebrate { position: fixed; inset: 0; z-index: 1200; background: rgba(0, 0, 0, 0.72); display: flex; align-items: center;
  justify-content: center; padding: 16px; }
.celebrate canvas { position: absolute; inset: 0; width: 100%; height: 100%; }
.celebrate-card { position: relative; max-width: 420px; width: 100%; padding: 24px 20px; border-radius: 16px; text-align: center;
  background: var(--surface); color: var(--text); box-shadow: 0 10px 40px rgba(0, 0, 0, 0.4); }
.celebrate-emoji { font-size: 3rem; line-height: 1; }
.celebrate-text { font-size: 1.35rem; font-weight: 700; margin: 12px 0 16px; overflow-wrap: anywhere; }

/* --- Order alcohol, comp shots, pull to refresh (2026-10-10) ---------- */
.od-qty { display: inline-flex; align-items: center; gap: 4px; flex: none; }
.od-qty button { min-height: 0; width: 36px; height: 36px; padding: 0; margin: 0; border-radius: 50%; font-size: 1.1rem; line-height: 1; }
.od-qty input { width: 56px; min-height: 36px; margin: 0; padding: 4px; text-align: center; }
#od-note { width: 100%; box-sizing: border-box; margin: 8px 0 0; }
.od-recent { margin-top: 10px; }
.od-recent summary { cursor: pointer; color: var(--text-muted); font-weight: 600; }
.od-recent-row { display: flex; align-items: center; justify-content: space-between; gap: 8px; padding: 6px 0; font-size: 0.9rem; border-bottom: 1px solid var(--border-faint); }
.od-recent-row button { min-height: 0; padding: 4px 10px; margin: 0; font-size: 0.85rem; }
.comp-favs .bubble { font-weight: 700; }
.sc-row.selected { outline: 2px solid var(--accent); }
.sc-row.static { cursor: default; }
/* The one about to go out: framed in the accent color and labelled, so it
   never reads like one of the already-given rows underneath. */
.comp-pick { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin: 10px 0; padding: 10px 12px; border-radius: 12px;
  background: var(--surface); border: 2px solid var(--accent); }
.comp-pick-label { flex: 0 0 100%; font-size: 0.72rem; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase; color: var(--accent); }
.comp-pick-name { flex: 1 1 140px; font-weight: 700; font-size: 1.15rem; }
.comp-table-title { margin: 8px 0 2px; font-size: 0.8rem; font-weight: 700; letter-spacing: 0.04em; text-transform: uppercase; color: var(--text-muted); }
.comp-tables .bubble.comp-no-table { font-style: italic; }
.comp-pick-table { flex: 0 0 100%; color: var(--text-muted); }
.comp-pick-table-missing { color: var(--warn-high); font-weight: 600; }
.comp-pick .primary { margin: 0; }
/* Already given tonight: under a dashed line, greyed, each row ticked. */
.comp-done { margin-top: 16px; padding-top: 10px; border-top: 2px dashed var(--border); }
.comp-done-title { margin: 0 0 6px; font-size: 0.8rem; font-weight: 700; letter-spacing: 0.04em; text-transform: uppercase; color: var(--text-muted); }
.comp-done-row { background: var(--surface-sunken); border-color: transparent; color: var(--text-muted); }
.comp-done-row .sc-row-main > span { font-weight: 500; }
.comp-done-tick { flex: none; width: 22px; height: 22px; border-radius: 999px; display: inline-flex; align-items: center; justify-content: center;
  font-size: 0.8rem; font-weight: 700; background: var(--border); color: var(--surface); }
.ptr { position: fixed; left: 0; right: 0; top: 0; z-index: 850; display: flex; justify-content: center; pointer-events: none;
  transform: translateY(calc(var(--pull, 0px) - 44px)); opacity: 0; transition: opacity 0.15s; }
.ptr.show { opacity: 1; }
.ptr span { padding: 6px 14px; border-radius: 999px; background: var(--surface); color: var(--text-muted); font-size: 0.85rem; font-weight: 600;
  border: 1px solid var(--border); box-shadow: 0 4px 16px rgba(0, 0, 0, 0.18); }
.ptr.ready span { color: var(--accent-text); background: var(--accent); border-color: var(--accent); }

/* Face ID set-up (the name menu) and the staff-only unlock (2026-10-11). */
.pk-list { list-style: none; margin: 0 0 12px; padding: 0; }
.pk-row { display: grid; grid-template-columns: 1fr auto; gap: 2px 10px; align-items: center;
  padding: 8px 0; border-top: 1px solid var(--border-faint); }
.pk-row .pk-label { font-weight: 600; }
.pk-row small { grid-column: 1; color: var(--text-muted); }
.pk-row .pk-remove { grid-column: 2; grid-row: 1 / span 2; min-height: 0; padding: 6px 12px; margin: 0; }
.pk-empty { color: var(--text-muted); padding: 6px 0; }

/* Admin › Expenses as a compact table (2026-10-11). */
.exp-controls { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
.exp-controls #adm-exp-month { flex: 0 0 auto; }
.exp-controls .sc-search { flex: 1 1 200px; margin: 0; }
.exp-bubbles { margin: 6px 0 8px; }
.exp-bubbles .bubble small { font-weight: 400; opacity: 0.8; }
.exp-table-wrap { overflow-x: auto; }
.exp-table { width: 100%; border-collapse: separate; border-spacing: 0; font-size: 0.82rem; }
.exp-table th, .exp-table td { padding: 5px 6px; border-top: 1px solid var(--border-faint); text-align: left; vertical-align: middle; }
.exp-table th { color: var(--text-muted); font-weight: 600; white-space: nowrap; cursor: pointer; user-select: none; }
.exp-table th.sorted { color: var(--text); }
.exp-table td small { color: var(--text-muted); }
.exp-table .num { text-align: right; }
.exp-table .nowrap { white-space: nowrap; }
.exp-table tbody tr { cursor: pointer; }
.exp-table tbody tr:hover { background: var(--surface-hover); }
.exp-table tbody tr.removed { opacity: 0.55; }
.exp-cell-photos { white-space: nowrap; }
.exp-thumb-sm { width: 28px; height: 28px; border-radius: 5px; margin-right: 3px; vertical-align: middle; }
.exp-thumb-lg { width: 96px; height: 96px; }
/* On a phone the who and item-count columns wait in the details, so the
   amount and the photos stay on screen without scrolling sideways. */
@media (max-width: 600px) {
  .exp-table .col-who, .exp-table .col-items { display: none; }
  .exp-table { font-size: 0.78rem; }
  .exp-table th, .exp-table td { padding: 5px 4px; }
}

/* Settings folded into sections (2026-10-12). */
/* The Settings pane is a flex column so its folds can be put A to Z with
   `order` (sortSettingsFolds). The Admin-only groups of folds are plain
   divs that hide and show as one; `display: contents` lets their folds
   take part in the ordering, and .hidden still wins (it is !important). */
#admin-settings { display: flex; flex-direction: column; align-items: stretch; }
#admin-settings > #export-section, #admin-settings > #settings-comp-colors,
#admin-settings > #qr-displays-section, #admin-settings > #api-keys-section { display: contents; }
.fold { border: 1px solid var(--border-soft); border-radius: 12px; background: var(--surface); margin: 6px 0; }
.fold > summary { list-style: none; cursor: pointer; padding: 10px 14px; font-weight: 700; display: flex; align-items: center; gap: 8px; user-select: none; }
.fold > summary::-webkit-details-marker { display: none; }
.fold > summary::before { content: "\25B8"; color: var(--text-muted); font-size: 0.9em; }
.fold[open] > summary::before { content: "\25BE"; }
.fold.fold-danger > summary { color: var(--danger, #b00020); }
.fold-body { padding: 0 14px 12px; }
.fold-body > h3:first-child, .fold-body > h4:first-child { margin-top: 4px; }
.fold-body h3, .fold-body h4 { margin-top: 12px !important; }
.btn-save-settings-copy { margin-top: 10px; }
@media (max-width: 700px) {
  /* The back-office bubbles three to a row on a phone, so the whole list
     is on screen above the section. */
  .admin-nav { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 6px; }
  .admin-nav .admin-tab { padding: 8px 4px; font-size: 0.78rem; }
}

/* Old receipts read for deliveries; the closing questions. */
.exp-scan { border: 1px solid var(--border-faint); border-radius: 10px; padding: 8px 10px; margin: 8px 0; }
.exp-scan.done { opacity: 0.6; }
.exp-scan-head { margin: 0 0 6px; display: flex; flex-direction: column; }
.exp-scan-head small { color: var(--text-muted); }
.exp-dl-mark { margin-right: 4px; }
.lq-bubbles .bubble { padding: 10px 14px; font-size: 0.95rem; display: inline-flex; flex-direction: column; align-items: center; gap: 2px; }
.lq-bubbles .bubble small { font-weight: 400; opacity: 0.8; }
.lq-bubbles .bubble.done { opacity: 0.5; }

/* --- The tab's lines, compact (the owner, 2026-10-12) --------------------- */
.bill-items-head { align-items: center; margin-top: 6px; }
.bill-items-head h3 { margin: 0; }
.bill-items-head .small { min-height: 0; padding: 4px 10px; font-size: 0.8rem; margin: 0; }
.bill-lines { gap: 4px; }
.bill-item-row.compact { padding: 6px 10px; font-size: 0.92rem; border-radius: 10px; align-items: center; gap: 8px; }
.bill-item-row.compact .bl-main { min-width: 0; flex: 1 1 auto; display: flex; flex-direction: column; gap: 1px; }
.bill-item-row.compact .bl-name { display: flex; align-items: baseline; gap: 6px; flex-wrap: wrap; line-height: 1.2; }
.bill-item-row.compact .bl-name strong { font-size: 0.98rem; }
.bill-item-row.compact .bl-qty { color: var(--text-muted); font-weight: 600; }
.bill-item-row.compact.grouped .bl-qty { color: var(--text); background: var(--surface-sunken); border-radius: 999px; padding: 0 7px; }
.bill-item-row.compact .bl-meta { color: var(--text-muted); font-size: 0.74rem; line-height: 1.25; }
.bill-item-row.compact .bl-side { flex: none; display: flex; flex-direction: column; align-items: flex-end; gap: 3px; }
.bill-item-row.compact .bl-total { font-size: 0.98rem; white-space: nowrap; }
.bill-item-row.compact .bl-btns { display: flex; gap: 4px; }
.bill-item-row.compact .bl-btn { min-height: 0; min-width: 32px; height: 28px; padding: 0 8px; margin: 0; font-size: 0.95rem; line-height: 1; border-radius: 8px; }
.bill-item-row.compact .btn-undo.bl-btn { font-size: 0.8rem; min-width: 52px; }
.bill-item-row.compact .btn-void.bl-btn { font-size: 1.1rem; font-weight: 700; }
.bill-item-row.compact .cover-unpicked { font-size: 0.78rem; }
.staff-letter { color: var(--staff-db); font-weight: 700; }
.basket-chip.staff-pick { color: var(--staff-db); border-color: var(--staff-db); }
.basket-chip.staff-pick.selected { background: var(--staff-db); border-color: var(--staff-db); color: #fff; }
/* Cancelled lines folded away under one handle. */
.voided-fold { margin-top: 4px; }
.voided-fold > summary { list-style: none; cursor: pointer; color: var(--text-muted); font-size: 0.82rem; padding: 6px 10px; user-select: none; }
.voided-fold > summary::-webkit-details-marker { display: none; }
.voided-fold > summary::before { content: "\25B8 "; }
.voided-fold[open] > summary::before { content: "\25BE "; }
.voided-fold .bill-item-row { margin-top: 4px; }
/* Room under the order slide so a thumb reaching for it never lands on
   the phone's own home gesture (the owner, 2026-10-12). */
.basket-bar { padding-bottom: calc(30px + env(safe-area-inset-bottom, 0px)); }
.basket-slide { margin-bottom: 6px; }
/* The expense form's camera: big, outlined, lit red like a record light. */
.exp-photo-btn.exp-camera {
  display: inline-flex; align-items: center; gap: 8px; min-height: 52px; padding: 8px 16px;
  border: 2px solid var(--rec, #d93025); color: var(--rec, #d93025); border-radius: 14px; font-weight: 700;
  background: var(--rec-bg, rgba(217, 48, 37, 0.08)); box-shadow: 0 0 0 4px rgba(217, 48, 37, 0.12); cursor: pointer;
}
.exp-photo-btn.exp-camera svg { width: 30px; height: 30px; flex: none; }
.exp-photo-btn.exp-camera:active { transform: scale(0.97); }

/* Reports (2026-10-12): Dashboard cards that open for their details, the
   shared sortable table with its search box, the Revenue page's month
   folds, and the Analysis page's figures and sectioned briefing. Colors
   are the existing chart tokens, so all three looks are covered. */
.dash-card.dash-drill { cursor: pointer; position: relative; transition: border-color 0.15s, transform 0.15s; }
.dash-card.dash-drill:hover, .dash-card.dash-drill:focus-visible { border-color: var(--accent); outline: none; }
.dash-card.dash-drill:active { transform: scale(0.995); }
.dash-drill-hint { display: block; margin-top: 8px; font-size: 0.78rem; color: var(--accent); text-align: right; }
.dash-kpis-inset { margin: 6px 0 10px; grid-template-columns: repeat(auto-fill, minmax(120px, 1fr)); gap: 8px; }
.dash-kpis-inset .dash-kpi { padding: 8px 10px; box-shadow: none; background: var(--surface-sunken); border-color: transparent; }
.dash-kpis-inset .dash-kpi-value { font-size: 1.1rem; }
#dash-card-expenses { grid-column: 1 / -1; }

/* The drill-down dialog: wider than a form, a chart on top, the full
   table under it. :has() so the width follows the content, since
   openModal replaces the dialog's innerHTML and nothing else. */
#modal:has(.drill) { max-width: 960px; width: min(96vw, 960px); }
.drill h3 { margin-top: 0; }
.drill-sub { color: var(--text-muted); margin: 0 0 8px; font-size: 0.9rem; }
.drill-chart { position: relative; height: 320px; margin: 8px 0 14px; }
.drill h5 { margin: 16px 0 4px; font-size: 0.95rem; }
.drill .dash-kpis { margin-bottom: 10px; }

/* Sortable table with a search box (makeSortableTable in app.js). The
   header arrows reuse .admin-table th.sortable. */
.stbl { margin-top: 6px; }
.stbl-head { display: flex; flex-wrap: wrap; gap: 8px 12px; align-items: center; justify-content: space-between; margin-bottom: 6px; }
.stbl-search {
  width: 100%; max-width: 360px; min-height: 40px; padding: 6px 12px; border-radius: 10px;
  border: 1px solid var(--border-soft); background: var(--surface); color: var(--text); font-size: 0.95rem;
}
.stbl-count { color: var(--text-muted); font-size: 0.85rem; white-space: nowrap; }
.stbl-wrap { overflow-x: auto; -webkit-overflow-scrolling: touch; }
.stbl-table { width: 100%; }
.stbl-table th { white-space: nowrap; }
.stbl-table th.num, .stbl-table td.num { text-align: right; font-variant-numeric: tabular-nums; }
.stbl-table tfoot td { font-weight: 700; border-top: 2px solid var(--border-soft); border-bottom: 0; }
.stbl-table tbody tr.stbl-row { cursor: pointer; }
.stbl-table tbody tr.stbl-row:hover, .stbl-table tbody tr.stbl-row:focus-visible { background: var(--surface-alt); outline: none; }
.stbl-empty { color: var(--text-muted); padding: 10px 0; }

/* Revenue: the graphs, then the nights folded by month. */
.rev-grid { margin: 14px 0; }
.rev-wide { grid-column: 1 / -1; }
.rev-list-head { display: flex; flex-wrap: wrap; gap: 8px 14px; align-items: center; justify-content: space-between; margin: 10px 0 8px; }
.rev-list-head h4 { margin: 0; }
.rev-month { border: 1px solid var(--border-soft); border-radius: 14px; background: var(--surface); margin-bottom: 10px; overflow: hidden; }
.rev-month > summary {
  list-style: none; display: flex; justify-content: space-between; align-items: center; gap: 10px;
  padding: 12px 14px; cursor: pointer; font-weight: 600; min-height: 48px;
}
.rev-month > summary::-webkit-details-marker { display: none; }
.rev-month > summary::before { content: "\25B8"; color: var(--accent); margin-right: 8px; display: inline-block; transition: transform 0.15s; }
.rev-month[open] > summary::before { transform: rotate(90deg); }
.rev-month-name { flex: 1; }
.rev-month-total { color: var(--text-muted); font-weight: 500; font-size: 0.9rem; white-space: nowrap; }
.rev-month-body { padding: 0 10px 10px; }

/* Analysis: the year picker, the figures, and the briefing in sections
   -- each category with its own color down the left edge. */
.analysis-year { display: inline-flex; align-items: center; gap: 8px; margin-top: 6px; flex-wrap: wrap; }
.analysis-year select {
  min-height: 40px; border-radius: 10px; padding: 4px 10px; border: 1px solid var(--border-soft);
  background: var(--surface); color: var(--text); font-size: 0.95rem;
}
.an-figures { margin: 14px 0; }
.an-figures h4 { margin: 0 0 2px; }
.an-figures .dash-grid { margin-top: 10px; }
.an-wide { grid-column: 1 / -1; }
.an-briefing-title { margin: 18px 0 8px; }
.an-sections { display: grid; gap: 12px; grid-template-columns: repeat(auto-fill, minmax(320px, 1fr)); }
.an-sec {
  --an-color: var(--accent);
  background: var(--surface); border: 1px solid var(--border-soft); border-left: 6px solid var(--an-color);
  border-radius: 14px; padding: 12px 14px; box-shadow: var(--shadow-card); min-width: 0;
}
.an-sec h5 {
  margin: 0 0 8px; font-size: 0.9rem; color: var(--an-color); text-transform: uppercase; letter-spacing: 0.05em;
  display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap;
}
.an-sec h5 .an-sec-raw { color: var(--text-muted); font-weight: 400; text-transform: none; letter-spacing: 0; font-size: 0.85rem; }
.an-sec p { margin: 0 0 8px; line-height: 1.6; }
.an-sec ul { margin: 0; padding-left: 20px; }
.an-sec li { margin: 0 0 6px; line-height: 1.5; }
.an-sec-summary { grid-column: 1 / -1; --an-color: var(--chart-1); font-size: 1.05rem; }
.an-sec-costs { --an-color: var(--chart-3); }
.an-sec-stock { --an-color: var(--chart-2); }
.an-sec-staff { --an-color: var(--chart-4); }
.an-sec-customers { --an-color: var(--chart-5); }
.an-sec-cards { --an-color: var(--chart-6); }
.an-sec-suggestions { --an-color: var(--accent); }
.analysis-history .an-sections { grid-template-columns: 1fr; }
@media (max-width: 560px) {
  .drill-chart { height: 240px; }
  .an-sections { grid-template-columns: 1fr; }
  .stbl-search { max-width: none; }
  .rev-month > summary { padding: 10px 12px; }
}

/* --- anchor: reports-a --- */
/* --- Reports: one sortable, searchable list per table (sortableList) ----
   The owner's rule for every list in the POS: a search box and headers
   that sort. The table scrolls sideways inside its own wrapper on a phone,
   so the search box stays put and the headers stay under a thumb. */
.rlist { margin: 6px 0 16px; }
.rlist-tools { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.rlist-tools .fold-search { margin: 6px 0; flex: 1 1 200px; min-width: 0; position: static; max-width: 420px; }
.rlist-count { color: var(--text-muted); font-size: 0.85em; white-space: nowrap; }
.rlist-scroll { overflow-x: auto; -webkit-overflow-scrolling: touch; border-radius: 8px; }
.rlist-scroll > .admin-table { border-radius: 0; }
.rlist th.sortable { padding-top: 12px; padding-bottom: 12px; min-height: 44px; }
.rlist th.num, .rlist td.num { text-align: right; }
.rlist td { white-space: nowrap; }
.rlist td.wrap { white-space: normal; min-width: 160px; }
.rlist-error { color: var(--danger); margin: 8px 0; }
.rlist-note { color: var(--text-muted); margin: 0 0 6px; }
.rlist-heading { margin: 18px 0 2px; }

/* --- anchor: reports-b --- */
/* --- The owner's price rules of 2026-10-12 ---------------------------------
   The Log In button at the right of its row, in the logo's gold; the
   Setup checks dialog; the cover bubble on a Friends tab; the staff
   bubbles in the discount dialog. */
.login-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  flex-wrap: wrap;
  margin-top: 14px;
}
.login-row label.checkbox-row { margin-top: 0; flex: 1 1 auto; }
#btn-login.login-gold {
  margin: 0 0 0 auto;
  background: linear-gradient(170deg, var(--login-gold-hi) 0%, var(--login-gold) 38%,
                              var(--login-gold-deep) 62%, var(--login-gold-hi) 100%);
  color: var(--login-gold-ink);
  border-color: var(--login-gold-deep);
  font-weight: 700;
  letter-spacing: 0.02em;
  text-shadow: 0 1px 0 rgba(255, 255, 255, 0.35);
  box-shadow: 0 1px 0 rgba(0, 0, 0, 0.25), 0 3px 8px rgba(0, 0, 0, 0.18);
}
#btn-login.login-gold:hover {
  background: linear-gradient(170deg, var(--login-gold-hi) 0%, var(--login-gold) 30%,
                              var(--login-gold-deep) 70%, var(--login-gold-hi) 100%);
  color: var(--login-gold-ink);
}
/* The cover bubble on a Friends tab: still a button, read as not offered. */
#btn-entrance-charge.cover-not-offered .bubble-value { color: var(--text-muted); font-weight: 500; }
/* The "for / by" staff bubbles under the discount reasons. */
.discount-staff-label { display: block; margin-top: 8px; font-size: 0.85em; color: var(--text-muted); }
.discount-staff-row .bubble { font-size: 0.98rem; padding: 10px 16px; }
/* Setup checks: plain sections, each with its count. */
.setup-checks-section { margin: 14px 0 0; padding-top: 10px; border-top: 1px solid var(--border-soft); }
.setup-checks-section h4 { margin: 0 0 6px; font-size: 1rem; }
.setup-checks-section h5 { margin: 10px 0 4px; font-size: 0.9rem; color: var(--text-muted); font-weight: 600; }
.setup-checks-section .muted { color: var(--text-muted); font-weight: 400; }
.setup-checks-section .warn-text { color: var(--warn-high); font-weight: 600; }
.setup-checks-none { color: var(--text-muted); margin: 4px 0; }
.setup-checks-list { margin: 4px 0 0; padding-left: 20px; }
.setup-checks-list li { margin: 2px 0; }
.setup-checks-table { font-size: 0.95em; }
.setup-checks-table td, .setup-checks-table th { padding: 6px 8px; }
/* --- anchor: pricing --- */
/* Free flow: the time left reads at a glance, on a phone too (2026-10-12).
   The chip on the tab list and the banner's clock both carry the word
   ("left" / "over"), so a bare number never has to be interpreted. */
.ff-chip { font-size: 0.95rem; padding: 3px 11px; }
.ff-banner .ff-clock { font-size: 1.5rem; }
.ff-extend-total { font-weight: 700; font-size: 1.1rem; }
@media (max-width: 600px) {
  .ff-banner { flex-direction: column; align-items: stretch; }
  .ff-banner-main { flex-wrap: wrap; }
  .ff-banner .ff-clock { font-size: 1.7rem; }
  .ff-banner-actions button { flex: 1 1 auto; min-height: 44px; }
}

/* A plan made at the table: the hour bubbles and the name it will get. */
#entrance-ffplan-hours .bubble { min-width: 64px; }
.ff-plan-preview { font-weight: 600; margin: 6px 0 10px; min-height: 1.2em; }

/* --- anchor: freeflow --- */
/* --- Admin › Expenses: the analysis panel (2026-10-12) --------------------- */
.exp-range { gap: 6px; }
.exp-range input[type="date"] { flex: 1 1 130px; min-width: 0; }
.exp-range-dash { color: var(--text-muted); }
.exp-analysis { margin: 8px 0 12px; }
.exp-an-head { display: flex; flex-wrap: wrap; justify-content: space-between; align-items: center; gap: 6px; margin-bottom: 6px; }
.exp-an-head .exp-bubbles { margin: 0; }
.exp-an-head .bubble { padding: 6px 12px; font-size: 0.85rem; }
.exp-kpis { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 8px; }
.exp-kpi { background: var(--surface); border: 1px solid var(--border-faint); border-radius: 12px; padding: 10px 12px; min-width: 0; }
.exp-kpi .k { color: var(--text-muted); font-size: 0.78rem; }
.exp-kpi .v { font-size: 1.25rem; font-weight: 700; line-height: 1.2; white-space: nowrap; }
.exp-kpi .s { color: var(--text-muted); font-size: 0.76rem; }
.exp-kpi .v.up { color: var(--danger); }
.exp-kpi .v.down { color: var(--ok, #2e7d32); }
.exp-charts { display: grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); gap: 10px; margin-top: 10px; }
.exp-chart { background: var(--surface); border: 1px solid var(--border-faint); border-radius: 12px; padding: 10px; min-width: 0; }
.exp-chart h4 { margin: 0 0 4px; font-size: 0.9rem; }
.exp-chart .hint { color: var(--text-muted); font-size: 0.74rem; margin: 0 0 4px; }
.exp-chart .pie-box { position: relative; height: 210px; cursor: pointer; }
.exp-chart .bar-box { position: relative; height: 210px; }
.exp-chart.wide { grid-column: 1 / -1; }
.exp-legend { list-style: none; margin: 6px 0 0; padding: 0; display: flex; flex-wrap: wrap; gap: 4px 10px; font-size: 0.78rem; }
.exp-legend li { display: inline-flex; align-items: center; gap: 5px; cursor: pointer; }
.exp-legend .dot { width: 10px; height: 10px; border-radius: 50%; display: inline-block; flex: none; }
.exp-zoom .pie-box { position: relative; height: min(60vh, 420px); }
.exp-zoom .exp-slice { margin: 8px 0 0; font-weight: 700; min-height: 1.4em; }
.exp-zoom .exp-legend li.selected { text-decoration: underline; }
/* Months as folds in the list. */
.exp-month { border: 1px solid var(--border-soft); border-radius: 12px; background: var(--surface); margin: 6px 0; }
.exp-month > summary { list-style: none; cursor: pointer; padding: 9px 12px; display: flex; align-items: center; justify-content: space-between; gap: 8px; user-select: none; }
.exp-month > summary::-webkit-details-marker { display: none; }
.exp-month > summary .name { font-weight: 700; }
.exp-month > summary .name::before { content: "\25B8 "; color: var(--text-muted); }
.exp-month[open] > summary .name::before { content: "\25BE "; }
.exp-month > summary small { color: var(--text-muted); }
.exp-month > summary strong { white-space: nowrap; }
.exp-month .exp-table-wrap { padding: 0 6px 6px; }
/* Go-live checks in Settings. */
.golive-list { list-style: none; margin: 10px 0 0; padding: 0; display: flex; flex-direction: column; gap: 6px; }
.golive-list li { display: flex; gap: 8px; align-items: flex-start; padding: 8px 10px; border: 1px solid var(--border-faint); border-radius: 10px; background: var(--surface); }
.golive-mark { flex: none; }
.golive-detail { color: var(--text-muted); font-size: 0.85rem; }
.golive-names > summary { cursor: pointer; font-size: 0.8rem; color: var(--text-muted); }
.golive-names small { color: var(--text-muted); }
.golive-todo { border-color: var(--danger) !important; }
/* --- anchor: expenses --- */

/* Go-live checks → bulk edit (2026-10-12): the lit column and the row tools. */
button.tiny { min-height: 0; padding: 4px 10px; font-size: 0.85rem; line-height: 1.3; }
.golive-actions { margin-top: 6px; }
.golive-names .tiny { margin-left: 6px; }
.bulk-tools { gap: 10px; align-items: center; flex-wrap: wrap; margin: 6px 0 8px; }
.bulk-focus-note { color: var(--text-muted); font-size: 0.85rem; }
.bulk-table th.bulk-focus, .bulk-table td.bulk-focus { background: color-mix(in srgb, var(--accent) 14%, transparent); }
.bulk-table th.bulk-focus { box-shadow: inset 0 -2px 0 var(--accent); }
.bulk-table .bulk-num { width: 6.5em; }
.bulk-rowtools { white-space: nowrap; }
.bulk-rowtools .tiny { padding: 2px 7px; margin-right: 3px; font-size: 0.95rem; }
.dbfix-table td { vertical-align: top; }
.dbfix-paid td { color: var(--text-muted); }
/* Free flow: the hours dial on Extend (2026-10-12). */
.ff-dial { display: flex; align-items: center; gap: 12px; margin: 6px 0; }
.ff-dial-btn { min-height: 52px; min-width: 64px; font-size: 1.6rem; line-height: 1; padding: 0; border-radius: 14px; }
.ff-dial-value { flex: 1; text-align: center; font-size: 1.4rem; font-weight: 600; }
/* The custom-length wheel (2026-10-22): two snapping columns, hours and
   minutes, three rows tall; the middle row, under the band, is the value. */
.ff-wheel { position: relative; display: grid; grid-template-columns: 1fr 1fr; gap: 8px; margin: 6px 0;
  --wheel-row: 44px; }
.ff-wheel-col { height: calc(var(--wheel-row) * 3); overflow-y: auto; scroll-snap-type: y mandatory;
  -webkit-overflow-scrolling: touch; overscroll-behavior: contain; scrollbar-width: none;
  padding: var(--wheel-row) 0; box-sizing: border-box; border: 1px solid var(--border); border-radius: 12px;
  background: var(--surface-sunken); position: relative; z-index: 1; }
.ff-wheel-col::-webkit-scrollbar { display: none; }
.ff-wheel-item { height: var(--wheel-row); line-height: var(--wheel-row); scroll-snap-align: center;
  text-align: center; font-size: 1.15rem; color: var(--text-muted); cursor: pointer; user-select: none; }
.ff-wheel-item.on { color: var(--text); font-weight: 700; font-size: 1.3rem; }
/* The Extend confirm screen: the form steps aside while the question is up. */
#modal.ff-confirming > :not(#ff-comp-confirm) { display: none !important; }
.ff-wheel-band { position: absolute; left: 0; right: 0; top: var(--wheel-row); height: var(--wheel-row);
  border-top: 2px solid var(--accent); border-bottom: 2px solid var(--accent); pointer-events: none; z-index: 2; }
/* The "?" help assistant (2026-10-12). */
/* (the ? moved into the user menu, 2026-10-12) */
.help-log { max-height: 46vh; overflow-y: auto; display: flex; flex-direction: column; gap: 8px; padding: 6px 2px; }
.help-msg { padding: 8px 12px; border-radius: 14px; max-width: 92%; font-size: 0.95rem; line-height: 1.4; }
.help-user { align-self: flex-end; background: var(--accent); color: var(--accent-text); }
.help-assistant { align-self: flex-start; background: var(--surface-alt); color: var(--text); }
.help-wait { color: var(--text-muted); font-style: italic; }
.help-empty { color: var(--text-muted); margin: 8px 0; }
.help-ask { display: flex; gap: 6px; align-items: stretch; margin-top: 10px; }
.help-ask input { flex: 1; min-width: 0; }
.help-ask button { flex: none; }
.help-faq { display: flex; flex-direction: column; gap: 6px; margin-top: 8px; max-height: 40vh; overflow-y: auto; }
.help-faq-q { text-align: left; white-space: normal; min-height: 0; padding: 8px 12px; }
.help-ask-anyway { align-self: flex-start; min-height: 0; padding: 6px 10px; font-size: 0.85rem; }
/* The notice banners (2026-10-12): traffic-light tones, a fold to one line, 3 h away. */
.notice-banner { padding: 8px 16px; color: var(--text); background: color-mix(in srgb, var(--light-yellow) 18%, var(--surface)); border-left: 6px solid var(--light-yellow); }
.notice-banner.tone-red { background: color-mix(in srgb, var(--light-red) 18%, var(--surface)); border-left-color: var(--light-red); }
.notice-banner.tone-green { background: color-mix(in srgb, var(--light-green) 18%, var(--surface)); border-left-color: var(--light-green); }
.notice-banner.tone-blue { background: color-mix(in srgb, var(--light-blue) 18%, var(--surface)); border-left-color: var(--light-blue); }
.notice-banner + .notice-banner { border-top: 1px solid var(--border-faint); }
.notice-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
.notice-head strong { font-size: 0.95rem; }
.notice-head strong::before { content: ""; display: inline-block; width: 10px; height: 10px; border-radius: 50%; margin-right: 8px; vertical-align: middle; background: var(--light-yellow); box-shadow: 0 0 6px var(--light-yellow); }
.notice-banner.tone-red .notice-head strong::before { background: var(--light-red); box-shadow: 0 0 6px var(--light-red); }
.notice-banner.tone-green .notice-head strong::before { background: var(--light-green); box-shadow: 0 0 6px var(--light-green); }
.notice-banner.tone-blue .notice-head strong::before { background: var(--light-blue); box-shadow: 0 0 6px var(--light-blue); }
.notice-btns { display: flex; gap: 6px; flex: none; }
.notice-banner .notice-btns button { min-height: 0; padding: 4px 12px; font-size: 0.85rem; margin: 0; background: var(--surface); color: var(--text); border: 1px solid var(--border-faint); }
.notice-banner ul { margin: 6px 0 0; padding-left: 20px; }
.notice-banner ul.hidden { display: none; }
.notice-banner li { margin: 3px 0; font-size: 0.92rem; }
.notice-banner .notice-meta { opacity: 0.75; font-size: 0.85em; }
.notice-banner .notice-link { background: none; border: none; padding: 0; min-height: 0; color: inherit; font: inherit; text-decoration: underline; cursor: pointer; }
.notice-banner.collapsed { padding: 2px 12px; }
.notice-banner.collapsed .notice-head strong { font-size: 0.82rem; font-weight: 600; }
.notice-banner.collapsed .notice-btns button { padding: 1px 9px; font-size: 0.78rem; }
/* Traffic: each section folds, and Details opens it as a graph (2026-10-12). */
.rep-fold > summary { display: flex; align-items: center; gap: 10px; }
.rep-fold > summary .rep-fold-title { flex: 1; }
.rep-fold > summary button.tiny { font-weight: 500; }
.rep-fold .rep-help { color: var(--text-muted); margin: 4px 0 8px; font-size: 0.92em; }
.rep-drill-canvas { width: 100%; height: 260px; }
.rep-drill-kpis { display: flex; flex-wrap: wrap; gap: 8px 18px; margin: 8px 0 10px; color: var(--text-muted); font-size: 0.9rem; }
.rep-drill-kpis strong { color: var(--text); }
/* Today's cards open their screen (2026-10-12). */
.today-card.clickable { cursor: pointer; position: relative; }
.today-card.clickable::after { content: "\203A"; position: absolute; right: 10px; top: 6px; color: var(--text-muted); font-size: 1.2rem; }
.today-card.clickable:hover { border-color: var(--accent); }
/* History: the calendar beside the search (2026-10-12). */
.history-search { display: flex; gap: 8px; align-items: center; }
.history-search .sc-search { flex: 1; min-width: 0; }
.history-day-btn { position: relative; display: inline-flex; align-items: center; justify-content: center; width: 44px; height: 44px; border: 1px solid var(--border-soft); border-radius: 12px; background: var(--surface); font-size: 1.3rem; cursor: pointer; flex: none; }
.history-day-btn input { position: absolute; inset: 0; opacity: 0; width: 100%; height: 100%; cursor: pointer; }
.history-month-head { display: flex; align-items: center; gap: 10px; padding: 8px 12px; }
/* The ¥ and FREE shortcuts above breakage (2026-10-12). */
.breakage-fab.fab-yen { bottom: calc(22px + 78px + env(safe-area-inset-bottom, 0px)); border-color: rgba(168, 121, 31, 0.8);
  background: radial-gradient(circle at 32% 26%, #fff9e6 0%, var(--login-gold-hi) 45%, var(--login-gold) 100%);
  box-shadow: 0 8px 20px rgba(0, 0, 0, 0.38), 0 0 0 4px rgba(212, 175, 55, 0.18), 0 0 18px rgba(212, 175, 55, 0.4), inset 0 2px 3px rgba(255, 255, 255, 0.9), inset 0 -4px 8px rgba(120, 90, 20, 0.28); }
.breakage-fab.fab-free { bottom: calc(22px + 156px + env(safe-area-inset-bottom, 0px)); border-color: rgba(31, 157, 85, 0.75);
  background: radial-gradient(circle at 32% 26%, #f0fff5 0%, #cdeedb 45%, #9fd9b8 100%);
  box-shadow: 0 8px 20px rgba(0, 0, 0, 0.38), 0 0 0 4px rgba(31, 157, 85, 0.16), 0 0 18px rgba(31, 157, 85, 0.35), inset 0 2px 3px rgba(255, 255, 255, 0.9), inset 0 -4px 8px rgba(20, 90, 50, 0.26); }
.fab-glyph { position: relative; z-index: 1; font-weight: 800; font-size: 1.8rem; color: #3b2a05; text-shadow: 0 1px 0 rgba(255, 255, 255, 0.7); }
.fab-free .fab-glyph { color: #0f4d2a; }
.fab-glyph-sm { font-size: 1rem; letter-spacing: 0.02em; }
.pay-remote { margin: 14px 0 4px; }
/* Inventory bulk edit: one price column per supplier (2026-10-12). */
.inv-bulk-table th.inv-bulk-sup { white-space: nowrap; }
.inv-bulk-table th.inv-bulk-sup small { font-weight: 500; color: var(--text-muted); }
.inv-bulk-table td.inv-bulk-sup input { width: 6.5em; }
.inv-bulk-table td.inv-bulk-sup.in-use { background: color-mix(in srgb, var(--accent) 12%, transparent); }
/* Price trends and the reorder mute (2026-10-12). */
.price-trends { display: flex; gap: 8px; margin-top: 2px; white-space: nowrap; }
.price-trend { font-size: 0.75rem; color: var(--text-muted); }
.price-trend.up { color: var(--light-red); }
.price-trend.down { color: var(--light-green); }
.price-trend small { font-size: 0.7rem; color: var(--text-muted); }
.inv-bulk-table td.inv-bulk-mute { text-align: center; }
.od-cash { margin: 4px 0 8px; font-weight: 600; }
.od-muted { color: var(--text-muted); font-weight: 400; }
/* The week at a glance: blue → dark red (2026-10-12). */
.heatmap td.heat-cell { text-align: center; font-variant-numeric: tabular-nums; }
.heatmap td.heat-blue { background: color-mix(in srgb, var(--light-blue) 45%, var(--surface)); }
.heatmap td.heat-green { background: color-mix(in srgb, var(--light-green) 55%, var(--surface)); }
.heatmap td.heat-yellow { background: color-mix(in srgb, var(--light-yellow) 70%, var(--surface)); }
.heatmap td.heat-red { background: color-mix(in srgb, var(--light-red) 75%, var(--surface)); color: #fff; }
.heatmap td.heat-darkred { background: var(--heat-darkred); color: #fff; font-weight: 700; }
.heat-legend { display: flex; flex-wrap: wrap; gap: 6px 14px; align-items: center; }
.heat-legend-title { font-weight: 600; }
.heat-key { display: inline-flex; align-items: center; gap: 5px; white-space: nowrap; }
.heat-swatch { display: inline-block; width: 14px; height: 14px; border-radius: 3px; }
.heat-swatch.heat-blue { background: color-mix(in srgb, var(--light-blue) 45%, var(--surface)); }
.heat-swatch.heat-green { background: color-mix(in srgb, var(--light-green) 55%, var(--surface)); }
.heat-swatch.heat-yellow { background: color-mix(in srgb, var(--light-yellow) 70%, var(--surface)); }
.heat-swatch.heat-red { background: color-mix(in srgb, var(--light-red) 75%, var(--surface)); }
.heat-swatch.heat-darkred { background: var(--heat-darkred); }
/* Projected vs actual (2026-10-12). */
.fva .fva-sub { margin: 12px 0 4px; font-size: 0.95rem; }
.fva-table td { white-space: nowrap; }
.fva-err { color: var(--text-muted); }
.fva-err.over { color: var(--light-red); }
.fva-err.under { color: var(--light-blue); }
.today-lastnight { margin: 6px 0 0; font-size: 0.85rem; color: var(--text-muted); }

/* Ordering window on the Order screen, and scheduled bell messages (2026-10-12). */
.od-window { margin: 4px 0 6px; font-size: 0.92rem; color: var(--text-muted); }
.od-open { color: var(--ok, #2e7d32); font-weight: 600; }
.bell-sched-head { margin: 10px 0 4px; font-size: 0.95rem; }
.bell-sched li { align-items: center; }
.glass-serving { white-space: nowrap; }

/* Log an expense: "What for" and "Paid with" as two labeled sections (2026-10-12). */
.exp-section { border-top: 1px solid var(--border-faint); padding-top: 6px; margin-top: 8px; }
.exp-section-label { display: block; font-size: 0.74rem; text-transform: uppercase; letter-spacing: 0.04em; color: var(--text-muted); margin-bottom: 2px; }
.exp-section .exp-bubbles { margin-top: 2px; }

/* The Pay screen (2026-10-12): one-line rows, the total on top, the calendar. */
.pay-tabs .bubble small { font-weight: 400; opacity: 0.8; margin-left: 2px; }
.pay-cal-btn { display: inline-flex; align-items: center; padding: 6px 12px; margin-left: auto; }
.pay-cal-btn svg { display: block; }
.pay-total { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; padding: 8px 12px; margin: 4px 0 8px;
  border: 1px solid var(--border-faint); border-radius: 12px; background: var(--surface); }
.pay-total strong { font-size: 1.25rem; }
.pay-total small { color: var(--text-muted); }
.pay-sort { margin: 0 0 6px; gap: 6px; }
.sc-row.pay-line { display: flex; flex-direction: row; align-items: center; gap: 10px; cursor: pointer; }
.sc-row.pay-line.static { cursor: default; }
.sc-row.pay-line .pay-line-name { font-weight: 700; flex: 0 1 auto; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.sc-row.pay-line .pay-line-when { color: var(--text-muted); flex: 0 1 auto; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.sc-row.pay-line .pay-line-status { color: var(--text-muted); font-size: 0.85rem; flex: 1 1 auto; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.sc-row.pay-line .pay-amount { margin-left: auto; }
.sc-row.pay-line.is-done .pay-line-name { text-decoration: line-through; color: var(--text-muted); }
@media (max-width: 600px) { .sc-row.pay-line .pay-line-status { display: none; } }
table.pay-table th[data-psort] { cursor: pointer; user-select: none; }
table.pay-table th[data-psort].sorted::after { content: " ↕"; opacity: 0.6; }
table.pay-table tr.inactive { opacity: 0.55; }
table.pay-table tr.hidden { display: none; }
.pc-head { align-items: center; gap: 8px; flex-wrap: wrap; }
.pc-head .pc-modes { margin: 0 0 0 auto; gap: 6px; }
.pc-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 4px; margin: 8px 0 4px; }
.pc-w { text-align: center; font-size: 0.72rem; color: var(--text-muted); padding: 2px 0; }
.pc-day { position: relative; min-height: 52px; border: 1px solid var(--border-faint); border-radius: 10px; background: var(--surface);
  padding: 4px 4px 2px; text-align: left; font: inherit; color: inherit; display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.pc-day.empty { border: none; background: transparent; }
.pc-day.has { border-color: var(--border); }
.pc-day.today .pc-n { color: var(--accent); font-weight: 700; }
.pc-day.selected { outline: 2px solid var(--accent); }
.pc-day .pc-n { font-size: 0.78rem; }
.pc-mark { display: block; font-size: 0.68rem; line-height: 1.2; padding: 1px 4px; border-radius: 6px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; color: #fff; }
.pc-mark small { opacity: 0.85; margin-left: 3px; }
.pc-mark.pc-salary { background: #b8891f; }
.pc-mark.pc-staff_db { background: #2f6fd1; }
.pc-mark.pc-dbpr { background: #7b4fc7; }
.pc-mark.due { box-shadow: 0 0 0 1.5px #d03d3d inset; }
/* Credit-to shortcuts between "← Categories" and the category name (2026-10-12). */
.menu-panel .category-item-header { flex-wrap: wrap; gap: 6px; }
.credit-shortcuts { display: inline-flex; flex-wrap: wrap; gap: 6px; flex: 1 1 auto; justify-content: center; min-width: 0; }
.credit-shortcuts .bubble.tiny { padding: 4px 10px; font-size: 0.8rem; min-height: 0; }
.credit-shortcuts .bubble.selected { background: var(--accent); color: var(--accent-text); border-color: var(--accent); }
#pc-day .sc-row.pay-line { flex-wrap: wrap; }
#pc-day .sc-row.pay-line .pay-line-name { white-space: normal; }
#pc-day .sc-row.pay-line .pay-line-when, #pc-day .sc-row.pay-line .pay-line-status { white-space: normal; }

/* The user page is a gear; the name moved inside the menu (2026-10-12). */
#topbar .account-btn { flex-direction: row; align-items: center; justify-content: center; padding: 0 10px; min-width: 44px; }
#topbar .account-btn svg { display: block; }
.account-who { display: flex; flex-direction: column; align-items: center; gap: 1px; padding: 6px 8px 4px; line-height: 1.15;
  border-bottom: 1px solid var(--border-faint); margin-bottom: 4px; }
.account-menu .help-btn { gap: 8px; font-weight: 600; }
/* The "?" drawn at the same 22 px and line weight as the other icons in
   the gear menu (the owner, 2026-10-15). */
.account-menu .help-btn .help-mark { display: inline-flex; align-items: center; justify-content: center; width: 22px; height: 22px;
  box-sizing: border-box; border-radius: 999px; border: 1.7px solid currentColor; font-weight: 700; font-size: 13px; line-height: 1; }

/* Bell: Payments due (2026-10-12). */
.bell-pay-h { margin: 10px 0 4px; font-size: 0.95rem; }
.bell-pay-h small { color: var(--text-muted); font-weight: 400; }
.bell-pay .sc-row.pay-line button { min-height: 0; padding: 4px 12px; font-size: 0.8rem; }
.pay-sched-btn { margin-left: auto; }
.pay-sched-btn + .pay-cal-btn { margin-left: 0; }
/* Re-order with priorities and the till floor (2026-10-12). */
.od-row .od-pick { flex: none; width: 22px; height: 22px; margin: 0; min-height: 0; }
.od-row.od-off .sc-row-main { opacity: 0.55; }
.od-prio { display: inline-block; margin-left: 6px; padding: 1px 7px; border-radius: 999px; font-size: 0.72rem;
  font-weight: 700; letter-spacing: 0.02em; vertical-align: 1px; color: #fff; }
.od-prio-critical { background: var(--danger); }
.od-prio-high { background: var(--warn-mid); }
.od-trimmed { margin: 10px 0 0; padding: 8px 10px; border: 1px dashed var(--border-faint); border-radius: 10px; color: var(--text-muted); }
.od-trimmed-head { margin: 0 0 4px; font-weight: 600; }
.od-trimmed-row { display: flex; justify-content: space-between; align-items: baseline; gap: 8px; padding: 3px 0; font-size: 0.9rem; opacity: 0.8; }
.od-trimmed-row small { white-space: nowrap; }
.od-delivery { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 10px; margin: 10px 0 0; }
.od-delivery label { font-weight: 600; }
.od-delivery-inputs { display: inline-flex; gap: 6px; flex-wrap: wrap; }
.od-delivery-inputs input { margin: 0; min-width: 0; width: auto; }
/* Supplier price list photo (2026-10-12): the check list and "Which bottle?". */
.pl-field { display: flex; flex-direction: column; gap: 4px; font-size: 0.85rem; color: var(--text-muted); margin: 8px 0; }
.pl-field select, .pl-field input { color: var(--text); margin: 0; }
.pl-head { display: flex; flex-wrap: wrap; gap: 10px; }
.pl-head .pl-field { flex: 1 1 160px; min-width: 0; }
.pl-list { max-height: none; }
.sc-row.pl-row { align-items: flex-start; flex-wrap: wrap; }
.sc-row.pl-row .pl-tick { width: 22px; height: 22px; margin: 2px 0 0; flex: none; }
.sc-row.pl-row .pl-printed { font-weight: 600; overflow-wrap: anywhere; }
.sc-row.pl-row .pl-ours { color: var(--text); overflow-wrap: anywhere; }
.sc-row.pl-row .pl-was { color: var(--text-muted); white-space: nowrap; }
.sc-row.pl-row .pl-pct { font-weight: 700; white-space: nowrap; }
.sc-row.pl-row .pl-none-text { color: var(--text-muted); font-style: italic; }
.sc-row.pl-row.pl-doubt { border-color: var(--warn-mid); }
.sc-row.pl-row.is-skipped { opacity: 0.55; }
.sc-row.pl-row.is-skipped .pl-printed { text-decoration: line-through; }
.pl-row-btns { display: flex; gap: 6px; flex: none; margin-left: auto; }
.pl-zoom { border: 1px solid var(--border); border-radius: 10px; overflow: hidden; margin: 0 0 10px; background: var(--surface-alt); }
.pl-zoom canvas { display: block; width: 100%; height: auto; max-height: 45vh; object-fit: contain; }
.sc-row.pl-pick { cursor: pointer; }

/* Bell bar (the owner, 2026-10-12): the three filters in one row of equal
   bubbles, the actions (order, select, mark read) in a second, greyer row
   behind a division; everything fits a phone. */
.bell-bar.bell-filters, .bell-bar.bell-actions { display: grid; gap: 6px; margin: 8px 0 0; }
.bell-bar.bell-filters { grid-template-columns: repeat(auto-fit, minmax(0, 1fr)); }
.bell-bar.bell-actions { grid-template-columns: repeat(4, minmax(0, 1fr)); border-top: 1px solid var(--border-faint); padding-top: 6px; margin-bottom: 6px; }
@media (max-width: 480px) { .bell-bar.bell-actions { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
.bell-filters .bell-bubble, .bell-actions .bell-sort { width: 100%; min-height: 36px; display: inline-flex; align-items: center; justify-content: center;
  gap: 4px; text-align: center; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; padding: 5px 8px; font-size: 0.84rem; }
.bell-actions .bell-sort { background: var(--surface-hover); border-color: var(--border-faint); color: var(--text-muted); }
.bell-actions .bell-sort.active { background: var(--text); border-color: var(--text); color: var(--bg); }
.bell-actions .bell-sort.bell-go { background: var(--accent); border-color: var(--accent); color: var(--accent-text); }
.bell-actions .bell-sort:disabled { opacity: 0.5; }
.bell-p { position: absolute; bottom: -6px; left: -6px; min-width: 18px; height: 18px; padding: 0 5px; border-radius: 9px;
  background: var(--login-gold, #c9a227); color: #1a1400; font-size: 0.7rem; font-weight: 700; line-height: 18px; text-align: center; }
.bell-pay-h.bell-pay-overdue { color: var(--danger); }
/* Schedule payment: a list with search and order, equal-width fields, a gold button. */
.ps-list { max-height: 32vh; }
.sc-row.pay-line.ps-row.selected { outline: 2px solid var(--accent); }
.ps-fields { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 10px; margin-top: 8px; }
.ps-fields label { display: flex; flex-direction: column; gap: 4px; min-width: 0; }
.ps-fields input { width: 100%; min-width: 0; box-sizing: border-box; margin: 0; }
@media (max-width: 520px) { .ps-fields { grid-template-columns: 1fr; } }
.gold-btn, #modal .gold-btn, .bubble.gold-btn {
  background: linear-gradient(170deg, var(--login-gold-hi) 0%, var(--login-gold) 38%, var(--login-gold-deep) 62%, var(--login-gold-hi) 100%);
  color: var(--login-gold-ink); border-color: var(--login-gold-deep); font-weight: 700; text-shadow: 0 1px 0 rgba(255, 255, 255, 0.35); }

/* How did the re-order go? (2026-10-12): big tappable stars, the chosen
   one's meaning under them, then the three questions as bubbles. */
.rp-stars { display: flex; justify-content: center; gap: 6px; margin: 6px 0 2px; }
#modal .rp-star { min-width: 56px; min-height: 56px; padding: 0; font-size: 2.6rem; line-height: 1; background: none;
  border: none; color: var(--poll-star-off); cursor: pointer; }
#modal .rp-star.on { color: var(--poll-star); }
.rp-meaning { text-align: center; margin: 2px 0 10px; color: var(--text-muted); min-height: 1.4em; }
.rp-meaning.chosen { color: var(--text); font-weight: 600; }
.rp-q { margin: 10px 0 4px; font-weight: 600; }
.rp-bubbles { margin-bottom: 2px; }
#rp-comment { width: 100%; box-sizing: border-box; margin-top: 10px; }
.rp-feedback { margin: 12px 0 0; padding: 8px 10px; border: 1px solid var(--border-soft); border-radius: 10px; }
.rp-fb-head { margin: 0 0 4px; font-weight: 600; }
.rp-fb-avg { margin: 0 0 6px; font-size: 1.05rem; }
.rp-fb-row { display: flex; justify-content: space-between; gap: 8px; padding: 2px 0; font-size: 0.9rem; }
.rp-fb-row span { color: var(--text-muted); }
.rp-fb-comment { padding: 4px 0; font-size: 0.9rem; border-top: 1px solid var(--border-faint); }
.rp-fb-comment small { display: block; color: var(--text-muted); }
.rp-fb-stars { color: var(--poll-star); }
/* Price research (2026-10-12): our bottles against the cheapest online. */
#modal:has(.prr) { max-width: 940px; width: min(96vw, 940px); }
/* 2026-10-15: one line a bottle, as many on one screen as fit. The box
   scrolls both ways so the header row and the bottle column hold (and the
   column slides away while swiping right, like every table). */
.prr-scroll { overflow: auto; -webkit-overflow-scrolling: touch; margin: 6px 0; max-height: 62vh; isolation: isolate; }
.prr-table { width: 100%; font-size: 0.85rem; }
.prr-table th, .prr-table td { padding: 3px 6px; white-space: nowrap; line-height: 1.25; }
.prr-table th { font-size: 0.78rem; }
.prr-table td.num, .prr-table th.num { text-align: right; }
.prr-table td.prr-name span { display: inline-block; max-width: 9.5em; overflow: hidden; text-overflow: ellipsis; vertical-align: bottom; font-weight: 600; }
.prr-table tr.prr-flag td { --row-tint: rgba(var(--tier-gold-rgb), 0.14); background: rgba(var(--tier-gold-rgb), 0.14); }
.prr-table .prr-down { color: var(--positive); font-weight: 700; }
.prr-table .prr-up { color: var(--danger); }
.prr-table .prr-dim { color: var(--text-faint); }
.prr-table .prr-ours { font-weight: 700; }
.prr-table .prr-link a { text-decoration: none; font-weight: 700; padding: 0 4px; }
.prr-table .prr-act { padding: 1px 2px; }
.prr-table .prr-act button { min-height: 28px; padding: 2px 7px; margin: 0 1px; }
.prr-table .prr-re { font-size: 0.7em; }
.prr-ok { color: var(--positive); font-weight: 700; }
.prr-fold > summary { cursor: pointer; font-weight: 700; padding: 4px 0; }
.prr-tools { gap: 6px; flex-wrap: wrap; margin: 6px 0; }
.prr-tools button { margin: 0; }
/* The summary: one fact a line, a thin rule between them. */
.prr-summary { margin: 6px 0 8px; border: 1px solid var(--border-soft); border-radius: 10px; overflow: hidden; }
.prr-sum-line { padding: 6px 10px; font-size: 0.9rem; line-height: 1.35; }
.prr-sum-line + .prr-sum-line { border-top: 1px solid var(--border-soft); }
.prr-sum-head { font-weight: 700; background: var(--surface-sunken); }
.prr-caveats { font-size: 0.82rem; }
/* The details / 🔎 / names popup, over the research (straight under body). */
.prr-pop-backdrop { position: fixed; inset: 0; z-index: 2000; background: rgba(0, 0, 0, 0.45); display: flex; align-items: center; justify-content: center; padding: 16px; }
.prr-pop { background: var(--surface); color: var(--text); border-radius: 14px; padding: 14px 16px; width: min(560px, 100%); max-height: 86vh; overflow: auto; box-shadow: 0 10px 40px rgba(0, 0, 0, 0.35); }
.prr-pop h4 { margin: 0 0 8px; }
.prr-pop input[type="text"] { width: 100%; }
.prr-d-row { display: flex; justify-content: space-between; gap: 12px; padding: 5px 0; font-size: 0.9rem; }
.prr-d-row + .prr-d-row { border-top: 1px solid var(--border-soft); }
.prr-d-row > span:first-child { color: var(--text-muted); flex: 0 0 auto; }
.prr-d-row > span:last-child { text-align: right; overflow-wrap: anywhere; }
.prr-n-row { padding: 6px 0; }
.prr-n-row + .prr-n-row { border-top: 1px solid var(--border-soft); }
.prr-n-edit { display: flex; gap: 6px; align-items: center; margin-top: 4px; }
.prr-n-edit input { flex: 1 1 auto; min-width: 0; }
.prr-n-edit button { margin: 0; flex: 0 0 auto; }
/* Print: only the research table and its summary. */
#prr-print-sheet { display: none; }
@media print {
  body.print-prr > *:not(#prr-print-sheet) { display: none !important; }
  body.print-prr #prr-print-sheet { display: block; color: #000; background: #fff; }
  #prr-print-sheet table { width: 100%; border-collapse: collapse; font-size: 9pt; }
  #prr-print-sheet th, #prr-print-sheet td { border: 1px solid #999; padding: 2px 4px; position: static !important; transform: none !important; opacity: 1 !important; background: #fff !important; color: #000 !important; }
  #prr-print-sheet a { color: #000; }
}
.prr-meta { color: var(--text-muted); font-size: 0.9em; margin: 4px 0; }
.prr-earlier { margin: 10px 0; }
.prr-earlier button { display: block; width: 100%; text-align: left; margin: 4px 0; }
/* --- Tonight's QR and clocking in (2026-10-12) ------------------------------ */
.checkin-tag { font-size: 0.8rem; font-weight: 600; white-space: nowrap; flex: 0 0 auto; }
.checkin-tag.ok { color: var(--checkin-ok); }
.checkin-tag.wait { color: var(--checkin-wait); }
.shift-qr { display: flex; flex-direction: column; align-items: center; gap: 6px; text-align: center; }
.shift-qr-img { width: min(320px, 80vw); height: auto; aspect-ratio: 1 / 1; image-rendering: pixelated;
  background: #fff; border-radius: 12px; padding: 8px; box-sizing: border-box; }
.shift-qr-date { font-size: 1.15rem; font-weight: 700; margin: 4px 0 0; }
.shift-qr-board { margin-top: 14px; }
.shift-qr-board h4 { margin: 10px 0 6px; }
.shift-qr-board .sc-row { display: flex; gap: 10px; align-items: center; justify-content: space-between; }
.clockout-people .bubble { display: inline-flex; flex-direction: column; align-items: flex-start; gap: 2px; }
.clockout-people .bubble small { font-weight: 500; }
.clockout-people .bubble:disabled { opacity: 0.55; }
.checkin-result { text-align: center; padding: 10px 4px; }
.checkin-result .checkin-big { font-size: 1.6rem; font-weight: 700; line-height: 1.3; margin: 10px 0; }
.checkin-result .checkin-icon { font-size: 3rem; }
.shift-qr-rotate { font-weight: 600; margin: 0; }
.checkin-scan video { display: block; width: 100%; max-height: 50vh; aspect-ratio: 3 / 4; object-fit: cover;
  background: var(--surface-sunken); border-radius: 12px; }
.scan-fallback { font-style: italic; }
.qr-display-row { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 8px 0; }
.checkin-tag.bad { color: var(--checkin-bad); }
.qr-display-actions { display: flex; gap: 8px; flex-wrap: wrap; justify-content: flex-end; }
.allhere-answer { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; margin: 16px 0 4px; }
.allhere-big { font-size: 1.3rem; font-weight: 700; padding: 18px 10px; min-height: 64px; }
.allhere-done { font-weight: 600; }
.allhere-person { border: 1px solid var(--border); border-radius: 12px; padding: 10px 12px; margin: 8px 0; }
.allhere-person.on { border-color: var(--checkin-wait); }
.allhere-tick { display: flex; gap: 8px; align-items: center; }
.allhere-person .bubble-row { margin-top: 8px; }
.allhere-note { width: 100%; box-sizing: border-box; margin-top: 8px; }
/* Face ID / Touch ID: signing in under the PIN form, and Switch user in
   the gear menu (2026-10-12). */
#btn-login-face.login-face {
  display: flex; align-items: center; justify-content: center; gap: 10px;
  width: 100%; min-height: 48px; margin-top: 14px;
  background: var(--login-face-bg); color: var(--login-face-ink);
  border: 1.5px solid var(--login-face-edge); font-weight: 600;
}
#btn-login-face svg { flex: none; display: block; }
.account-menu .menu-check {
  display: flex; align-items: center; gap: 8px; margin: 2px 0; padding: 8px 10px;
  font-size: 0.85rem; line-height: 1.25; color: inherit; text-align: left; cursor: pointer;
}
.account-menu .menu-check input { width: auto; margin: 0; flex: none; }
.su-hello { font-size: 1.15rem; font-weight: 600; margin: 4px 0 10px; }
.su-wait { color: var(--text-muted); margin: 10px 0 16px; }
.su-step .checkbox-row { display: flex; align-items: center; gap: 8px; margin: 10px 0; color: var(--text); }
.su-step .checkbox-row input { width: auto; margin: 0; }
button.su-other { display: block; background: none; border: none; box-shadow: none; color: var(--text-muted);
  text-decoration: underline; padding: 6px 0; min-height: 0; }

/* Automatic backups (2026-10-12), inside Settings › Backup. */
.auto-backup { margin-top: 18px; padding-top: 12px; border-top: 1px solid var(--border-soft); }
.auto-backup h5 { margin: 0 0 4px; font-size: 1rem; }
.auto-backup-status { margin: 8px 0 2px; font-weight: 600; }
.auto-backup-status.ab-off { color: var(--danger); }
.auto-backup-last { margin: 2px 0 6px; color: var(--text-muted); }
.auto-backup-runs { margin-top: 12px; }
.auto-backup-runs h6 { margin: 0 0 4px; font-size: 0.85rem; color: var(--text-muted); font-weight: 600; }
.ab-run { display: grid; grid-template-columns: minmax(0, 9.5em) minmax(0, 6em) minmax(0, 6.5em) minmax(0, 1fr); gap: 8px;
  padding: 5px 0; border-bottom: 1px solid var(--border-soft); font-size: 0.9rem; align-items: baseline; }
.ab-run:last-child { border-bottom: none; }
.ab-run .ab-detail { color: var(--text-muted); overflow-wrap: anywhere; min-width: 0; }
.ab-pill { font-weight: 600; }
.ab-pill.ab-done { color: var(--backup-ok); }
.ab-pill.ab-failed { color: var(--danger); }
.ab-pill.ab-skipped, .ab-pill.ab-running { color: var(--text-muted); }
@media (max-width: 520px) {
  .ab-run { grid-template-columns: minmax(0, 1fr) auto auto; }
  .ab-run .ab-detail { grid-column: 1 / -1; }
}

/* --- Charges as a table (the owner, 2026-10-12) --------------------------- */
.charge-toolbar { gap: 10px; align-items: center; flex-wrap: wrap; margin: 0 0 10px; }
.charge-table tr.charge-group td {
  background: var(--surface-sunken); font-weight: 700; font-size: 0.85rem;
  letter-spacing: 0.02em; text-transform: uppercase; color: var(--text-muted); padding: 6px 10px;
}
.charge-kind { display: inline-block; padding: 2px 9px; border-radius: 999px; font-size: 0.8rem; font-weight: 600; white-space: nowrap; }
.charge-kind-cover { background: var(--badge-paid-bg); color: var(--badge-paid-text); }
.charge-kind-freeFlow { background: var(--badge-open-bg); color: var(--badge-open-text); }
.charge-table td.charge-actions { white-space: nowrap; }
.charge-table td.charge-actions button { margin: 0 4px 0 0; }
.charge-table.charge-bulk input[type="text"] { width: 100%; min-width: 120px; box-sizing: border-box; }
.charge-table.charge-bulk input.bulk-num { width: 90px; }
.charge-preset-row { display: flex; gap: 10px; align-items: flex-end; flex-wrap: wrap; margin: 6px 0; }
.charge-preset-row label { display: flex; flex-direction: column; gap: 2px; font-size: 0.85rem; margin: 0; }
.charge-preset-row input { width: 110px; }
.charge-preset-clock { color: var(--text-muted); font-size: 0.9rem; padding-bottom: 8px; }
.charge-preset-row button { margin-bottom: 2px; }

/* --- DB tables on a phone (the owner, 2026-10-12): one line per client in
   Who we owe, equal filter bubbles across the width, one line per sale. --- */
.db-phone-short { display: none; }
@media (max-width: 560px) {
  .db-phone-long { display: none; }
  .db-phone-short { display: inline; }
  #db-status-filter { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 6px; width: 100%; margin: 6px 0; }
  #db-status-filter .bubble { min-width: 0; padding: 9px 4px; font-size: 0.82rem; white-space: nowrap; text-align: center; }
  .db-owe th:nth-child(n+2):nth-child(-n+5), .db-owe td:nth-child(n+2):nth-child(-n+5) { display: none; }
  .db-owe th, .db-owe td { padding: 7px 6px; font-size: 0.88rem; white-space: nowrap; }
  .db-owe td:first-child { max-width: 40vw; overflow: hidden; text-overflow: ellipsis; }
  .db-owe td.db-owe-actions { text-align: right; }
  .db-owe td.db-owe-actions button { min-height: 0; padding: 5px 9px; font-size: 0.8rem; margin: 0 0 0 4px; }
  /* Paid by, Commission and Still owed stay in the bill popup and in Who
     we owe; the row keeps date, client, bottles and status. */
  .db-sales th:nth-child(3), .db-sales td:nth-child(3),
  .db-sales th:nth-child(5), .db-sales td:nth-child(5),
  .db-sales th:nth-child(6), .db-sales td:nth-child(6) { display: none; }
  .db-sales { table-layout: fixed; width: 100%; }
  .db-sales th:nth-child(1), .db-sales td:nth-child(1) { width: 40px; }
  .db-sales th:nth-child(2), .db-sales td:nth-child(2) { width: 66px; }
  .db-sales th:nth-child(7), .db-sales td:nth-child(7) { width: 56px; }
  .db-sales th, .db-sales td { padding: 6px 3px; font-size: 0.74rem; vertical-align: top; overflow-wrap: anywhere; }
  .db-sales thead th { font-size: 0.7rem; white-space: normal; }
  /* A bottle's name may wrap; its two prices never split. */
  .db-sales .db-item-line { white-space: normal; }
  .db-sales .db-item-line span { white-space: nowrap; }
  .db-sales .db-status { font-size: 0.7rem; padding: 2px 6px; white-space: nowrap; }
  .db-sales .bill-link.db-phone-short { min-height: 0; line-height: 1.3; padding: 0; margin: 0; font-size: 0.76rem; }
  .db-sales td strong { font-weight: 600; }
  .db-sales td:nth-child(2) { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .charge-table.charge-bulk input.bulk-num { width: 64px; }
}

/* Customer polls (2026-10-12): Admin › Customer polls, its builder,
   Send, Share kit and Results. */
.cpoll-head { align-items: center; gap: 12px; flex-wrap: wrap; }
.cpoll-head h3 { margin: 0; }
.cpoll-table td.num, .cpoll-table th.num { text-align: right; white-space: nowrap; }
.cpoll-table .cpoll-actions { display: flex; flex-wrap: wrap; gap: 6px; justify-content: flex-end; }
.cpoll-table .cpoll-actions button { margin: 0; }
.cpoll-name { background: none; border: 0; padding: 0; margin: 0; font: inherit; font-weight: 600; color: var(--text);
  text-align: left; text-decoration: underline; text-underline-offset: 3px; cursor: pointer; min-height: 0; }
.badge.cpoll-badge { white-space: nowrap; }
.badge.cpoll-open { background: var(--badge-paid-bg); color: var(--badge-paid-text); }
.badge.cpoll-draft { background: var(--cpoll-draft-bg); color: var(--cpoll-draft-text); }
.badge.cpoll-closed { background: var(--badge-bg); color: var(--badge-text); }
#modal:has(.cpb), #modal:has(.cpoll-results), #modal:has(.cpoll-send) { max-width: 720px; width: min(96vw, 720px); }
.cpb-langrow { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.cpb-langrow .bubble-row { margin: 6px 0; }
.cpb #cpb-title, .cpb #cpb-intro { width: 100%; box-sizing: border-box; }
.cpb-q { border: 1px solid var(--border-soft); border-radius: 12px; padding: 10px 12px; margin: 12px 0; background: var(--surface-hover); }
.cpb-q-head { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-bottom: 8px; }
.cpb-q-num { margin-right: auto; }
#modal .cpb-q-head select { margin: 0; width: auto; flex: 0 1 220px; min-width: 0; }
.cpb-tools { display: inline-flex; gap: 4px; }
.cpb-tools button { min-width: 40px; min-height: 40px; margin: 0; padding: 0 8px; }
.cpb-q > input[type="text"] { width: 100%; box-sizing: border-box; margin: 0; }
.cpb-choices { margin: 8px 0 0 14px; }
.cpb-choice { display: flex; gap: 6px; margin: 6px 0; }
.cpb-choice input { flex: 1 1 auto; min-width: 0; margin: 0; }
.cpb-choice button { min-width: 40px; margin: 0; }
#modal .bubble.cpb-addchoice { min-height: 38px; padding: 4px 14px; margin: 4px 0 0; }
.cpb-req { display: inline-flex; align-items: center; gap: 6px; margin-top: 8px; font-size: 0.92rem; }
.cpb-addhead { margin: 14px 0 0; font-weight: 600; }
.cpoll-sub { margin: -4px 0 8px; color: var(--text-muted); }
.cpoll-warn { color: var(--cpoll-warn); font-weight: 600; }
.cpoll-count, .cpoll-result { font-size: 1.05rem; font-weight: 600; margin: 8px 0; }
.cpoll-failed { color: var(--danger); }
.cpoll-manual-list { display: flex; flex-direction: column; gap: 8px; }
.cpoll-manual { border: 1px solid var(--border-soft); border-radius: 10px; padding: 8px 10px; }
.cpoll-manual-who small { color: var(--text-muted); margin-left: 4px; }
.cpoll-manual-who .badge { margin-left: 6px; }
.cpoll-link-row { display: flex; gap: 6px; margin-top: 6px; }
.cpoll-link-row input { flex: 1 1 auto; min-width: 0; margin: 0; font-size: 0.9rem; }
.cpoll-link-row button { margin: 0; white-space: nowrap; }
.cpoll-caption textarea { width: 100%; box-sizing: border-box; resize: vertical; }
.cpoll-caption button { margin-top: 4px; }
.cpoll-stats { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 8px; margin: 8px 0 14px; }
@media (max-width: 560px) { .cpoll-stats { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
.cpoll-stat { border: 1px solid var(--border-soft); border-radius: 10px; padding: 8px 10px; display: flex; flex-direction: column; }
.cpoll-stat-num { font-size: 1.4rem; font-weight: 700; }
.cpoll-stat-label { color: var(--text-muted); font-size: 0.85rem; }
.cpoll-stat small { color: var(--text-faint); font-size: 0.75rem; }
.cpoll-rq { border-top: 1px solid var(--border-faint); padding: 10px 0 4px; }
.cpoll-rq h4 { margin: 0; display: flex; gap: 8px; align-items: baseline; }
.cpoll-rq-num { flex: 0 0 auto; display: inline-flex; align-items: center; justify-content: center; width: 24px; height: 24px;
  border-radius: 50%; background: var(--accent); color: var(--accent-text); font-size: 0.8rem; }
.cpoll-bar-row { margin: 8px 0; }
.cpoll-bar-label { display: flex; justify-content: space-between; gap: 8px; font-size: 0.92rem; }
.cpoll-bar-label span:first-child { overflow-wrap: anywhere; }
.cpoll-bar-num { color: var(--text-muted); white-space: nowrap; }
.cpoll-bar-track { height: 10px; border-radius: 999px; background: var(--cpoll-bar-track); overflow: hidden; margin-top: 3px; }
.cpoll-bar-fill { height: 100%; border-radius: 999px; background: var(--cpoll-bar); }
.cpoll-avg { font-size: 1.1rem; font-weight: 600; margin: 6px 0; }
.cpoll-avg-star { color: var(--poll-star); font-size: 1.4rem; }
.cpoll-texts { list-style: none; padding: 0; margin: 6px 0; }
.cpoll-texts li { padding: 6px 0; border-bottom: 1px solid var(--border-faint); }
.cpoll-texts li span { display: block; overflow-wrap: anywhere; white-space: pre-wrap; }
.cpoll-texts small { color: var(--text-muted); }

/* Bulk edit: the four price lists side by side, a DB price under the Member
   price flagged (the owner, 2026-10-12). */
.bulk-table td.bulk-member { white-space: nowrap; color: var(--text-muted); }
.bulk-row.bulk-db-below td.bulk-member { color: var(--text); font-weight: 600; }
.bulk-db-warn { display: inline-block; margin-left: 4px; min-width: 18px; height: 18px; line-height: 18px; text-align: center;
  border-radius: 999px; background: var(--badge-void-bg); color: var(--badge-void-text); font-weight: 700; font-size: 0.8rem; }
.bulk-row.bulk-db-below input[data-bulk-field="db_price"] { border-color: var(--badge-void-text); }
#bulk-price-flags { margin: 6px 0 8px; align-items: center; }
#bulk-price-flags .bulk-flag { background: var(--badge-void-bg); color: var(--badge-void-text); border-color: var(--badge-void-text); padding: 8px 14px; font-size: 0.95rem; }
#bulk-price-flags .bulk-flag.selected { background: var(--badge-void-text); color: var(--surface); }
.bulk-flag-ok { color: var(--text-muted); font-size: 0.9rem; }
/* The commission check-up's date and its button on one line, the button
   obviously a button. */
.dbfix-since-row { gap: 10px; align-items: flex-end; flex-wrap: wrap; }
.dbfix-since-row label { display: flex; flex-direction: column; gap: 4px; margin: 0; }
.dbfix-since-row input[type="date"] { min-height: 44px; }
.dbfix-since-row .gold-btn { min-height: 44px; margin: 0; }


/* Open and Active tabs as a list (the owner, 2026-10-12): the History
   table, with tonight's running total; Active rows twice as tall. */
.open-tabs-table { font-size: 0.9rem; }
.open-tabs-table tbody tr { cursor: pointer; }
.open-tabs-table tbody tr:hover td { background: var(--surface-hover); }
.open-tabs-table th, .open-tabs-table td { padding: 10px 8px; }
.open-tabs-table.open-tabs-tall td { padding: 34px 8px; font-size: 1rem; }
.open-tabs-table .tabs-noname { color: var(--danger); font-size: 0.85em; }
.open-tabs-table .ff-chip { margin-left: 6px; }
/* On a phone: see .ph-hide above. */
@media (max-width: 560px) {
  .open-tabs-table td.tab-name strong { font-size: 1.05em; }
  .open-tabs-table th, .open-tabs-table td { padding: 8px 4px; font-size: 0.8rem; }
  .open-tabs-table.open-tabs-tall td { padding: 26px 4px; font-size: 0.9rem; }
}

/* DB / PR names on every drink (the owner, 2026-10-12): one tap credits
   the glass to that client. */
.menu-item-btn .mi-db { display: flex; flex-wrap: wrap; gap: 4px; margin-top: 4px; }
.menu-item-btn .mi-db-pick { display: inline-block; cursor: pointer;
  min-height: 0; padding: 3px 9px; border-radius: 999px; font-size: 0.78rem; font-weight: 600; margin: 0;
  background: var(--surface); border: 1.5px solid var(--accent); color: var(--text);
}
.menu-item-btn .mi-db-pick:active { background: var(--accent); color: var(--accent-text); }
.bs-all { margin: 6px 0 2px; }
.dbjoin-list { display: flex; flex-direction: column; gap: 6px; margin: 8px 0; }
.dbjoin-row { display: grid; grid-template-columns: auto 1fr auto; gap: 4px 10px; align-items: center; padding: 8px 10px;
  border: 1px solid var(--border-soft); border-radius: 10px; cursor: pointer; margin: 0; }
.dbjoin-row input { width: 22px; height: 22px; }
.dbjoin-row .dbjoin-with { grid-column: 2; color: var(--text-muted); font-size: 0.85rem; }
.dbjoin-row .dbjoin-amt { grid-column: 3; grid-row: 1; font-weight: 600; white-space: nowrap; }

/* Supplier price list from a file (2026-10-12): the upload button next to
   the camera, and the "Not in stock yet" table under the matched lines. */
#modal .exp-top .pl-file-btn { border-style: solid; }
/* Phones: the price list's two buttons one above the other, full width,
   so "Upload a spreadsheet" is never cut off (QA, 2026-10-19). */
@media (max-width: 560px) {
  #modal .exp-top:has(.pl-file-btn) { flex-direction: column; align-items: stretch; }
  #modal .exp-top:has(.pl-file-btn) .exp-photo-btn { white-space: normal; justify-content: center; text-align: center; }
}
.pl-new-title { margin: 14px 0 4px; font-size: 1rem; }
.pl-new-tools { display: flex; gap: 8px; flex-wrap: wrap; margin: 6px 0 8px; }
.pl-new-tools button { margin: 0; }
/* The table is laid out as a grid per line so it fits the 560px dialog
   and a phone without sideways scrolling: tick | sheet name + POS name +
   buttons | price with the size under it. The headers are sort chips. */
.pl-new { width: 100%; border-collapse: collapse; font-size: 0.9rem; display: block; }
.pl-new thead, .pl-new tbody { display: block; }
.pl-new thead tr { display: flex; flex-wrap: wrap; gap: 6px; padding: 0 0 8px; }
.pl-new th.sortable { cursor: pointer; user-select: none; border: 1px solid var(--border); border-radius: 999px;
  padding: 4px 10px; font-weight: 600; font-size: 0.8rem; color: var(--text-muted); white-space: nowrap; }
.pl-new th.sortable::after { content: attr(data-arrow); display: inline-block; width: 0.9em; font-size: 0.8em; color: var(--accent); }
.pl-new th.sortable.sorted { color: var(--accent); border-color: var(--accent); }
.pl-new tbody tr { display: grid; grid-template-columns: 28px minmax(0, 1fr) minmax(0, 1fr) 92px; grid-template-areas:
  "tick printed name price" "tick printed btns price"; column-gap: 8px; row-gap: 4px; align-items: start;
  padding: 8px 0; border-bottom: 1px solid var(--border); }
.pl-new td { padding: 0; border: 0; vertical-align: top; }
.pl-nc-tick { grid-area: tick; padding-top: 2px; }
.pl-nc-printed { grid-area: printed; }
.pl-nc-name { grid-area: name; }
.pl-nc-price { grid-area: price; text-align: right; white-space: nowrap; line-height: 1.25; }
.pl-nc-btns { grid-area: btns; }
.pl-new .pl-new-printed { overflow-wrap: anywhere; line-height: 1.3; }
.pl-new .pl-new-printed small { display: block; }
.pl-new .pl-new-name { width: 100%; min-width: 0; margin: 0; padding: 6px 8px; font-size: 0.9rem; box-sizing: border-box; }
.pl-new .pl-new-tick { width: 20px; height: 20px; margin: 0; }
.pl-new-btns { display: flex; gap: 4px; flex-wrap: wrap; }
.pl-new-btns button { margin: 0; padding: 5px 9px; font-size: 0.8rem; }
.pl-added { color: var(--light-green); font-weight: 600; white-space: nowrap; }
@media (max-width: 700px) {
  .pl-new { font-size: 0.85rem; }
  .pl-new tbody tr { grid-template-columns: 26px minmax(0, 1fr) 84px; grid-template-areas:
    "tick printed price" "tick name price" "tick btns price"; }
}

/* An unfinished price list from earlier (2026-10-13). */
.pl-draft { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; justify-content: space-between;
  border: 1px solid var(--accent); border-radius: 12px; padding: 8px 10px; margin: 6px 0 10px; font-size: 0.9rem; }
.pl-draft-btns { display: flex; gap: 6px; }
.pl-draft-btns button { margin: 0; padding: 6px 12px; }

/* "Sales by:" carries a gradient-gold "+" until an answer is given, so staff
   see it opens the staff list (the owner, 2026-10-12). */
.sales-by-bubble:not(.has-source)::after {
  content: "+";
  margin-left: 6px;
  font-weight: 800;
  font-size: 1.15em;
  line-height: 1;
  background: linear-gradient(135deg, #f6e6a8 0%, #d4af37 45%, #a8791f 70%, #f3dd94 100%);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  -webkit-text-fill-color: transparent;
}

/* Which supplier an order line goes to (2026-10-13). */
.od-sup { display: flex; align-items: center; gap: 6px; width: 100%; margin-top: 4px; font-size: 0.8rem; color: var(--text-muted); }
.od-sup select { margin: 0; padding: 4px 8px; font-size: 0.8rem; max-width: 100%; }

/* Sales history from the old daily sheets (2026-10-13). */
.history-month-row { cursor: pointer; }
.history-row-flag td { background: color-mix(in srgb, var(--warn-mid, #e0a100) 10%, transparent); }
.history-flag { white-space: normal; font-size: 0.75rem; }
#history-month-table, #history-day-table { font-size: 0.9rem; }
/* The whole History tab reads at the compact size, Zentoshin included,
   and its two sections fold (the owner, 2026-10-13). */
.history-compact .admin-table { font-size: 0.85rem; }
.history-compact .admin-table td, .history-compact .admin-table th { padding: 6px 6px; }
.history-compact .totals { font-size: 0.9rem; }
/* The figures a fold used to carry in its title, now the first line of
   its open content (the owner, 2026-10-14). */
.history-span { margin: 6px 0 4px; font-weight: 600; font-size: 0.95rem; }
.history-span:empty { display: none; }
/* A day's name and note beside the Zentoshin figure, with its Edit. */
.zen-note-cell { white-space: normal; min-width: 160px; }
.zen-note-cell .zen-note-icon { margin-left: 4px; cursor: help; }
.zen-note-cell button.tiny { margin-left: 6px; vertical-align: middle; }
.zen-note-cell .zen-note-text { color: var(--text-muted); font-size: 0.85em; display: block; white-space: pre-wrap; }
.card-note-pair { border: 1px solid var(--border-soft); border-radius: 8px; padding: 10px 10px 2px; margin-bottom: 10px; }
#modal .field textarea.card-note-body {
  width: 100%; padding: 8px; border: 1px solid var(--border); border-radius: 6px;
  background: var(--surface); color: var(--text); font: inherit; resize: vertical;
}
#history-month-table td, #history-day-table td { padding: 8px 6px; }
.history-notes { margin: 6px 0 10px; padding-left: 18px; font-size: 0.88rem; color: var(--text-muted); }
.history-notes li { margin: 2px 0; }

/* Analysis figures (2026-10-13): the exact window above the numbers, the
   "tonight" tick beside it, and the owner's own From/To bar. */
.an-window { margin: 4px 0 8px; }
.an-tonight { margin-left: 10px; font-weight: 400; color: var(--text-muted); white-space: nowrap; }
.an-tonight input { vertical-align: middle; }
.analysis-window.an-custom label { margin-right: 10px; }
.analysis-window.an-custom input[type="date"] { max-width: 11em; }

/* Supplier price columns (Inventory) and the Buying prices report (2026-10-13). */
td.inv-sp { white-space: nowrap; }
td.inv-cheapest { font-weight: 700; color: var(--success, #1e7f3a); }
#buy-table { font-size: 0.88rem; overflow: visible; }
#buy-table th, #buy-table td { white-space: nowrap; vertical-align: top; }
#buy-table .bulk-sticky { position: sticky; left: 0; z-index: 2; background: var(--surface); box-shadow: 2px 0 0 var(--border-soft); }
#buy-table thead .bulk-sticky { z-index: 4; background: var(--surface-sunken); }
.buy-row { cursor: pointer; }
.buy-pct.up, td.buy-up { color: var(--danger); font-weight: 600; }
.buy-pct.down, td.buy-down { color: var(--success, #1e7f3a); font-weight: 600; }
td.buy-none { color: var(--text-muted); }

/* ---- Date wheels (datewheel.js) ------------------------------------------
   One picker for every date field: three snap-scrolling columns with the
   picked row in a band, a number box when the band is tapped, and
   Today / Clear / Done. A bottom sheet on a phone, a card on anything wider.
   Directly under <body> so it can open from inside a modal. */
input[type="date"].dw-input { cursor: pointer; caret-color: transparent; }
#datewheel-backdrop {
  position: fixed; inset: 0; background: var(--backdrop); z-index: 60;
  display: flex; align-items: flex-end; justify-content: center;
}
#datewheel {
  width: 100%; max-width: 480px; background: var(--surface); color: var(--text);
  border-radius: 18px 18px 0 0; box-shadow: 0 -8px 30px rgba(0, 0, 0, 0.25);
  padding: 6px 12px calc(12px + env(safe-area-inset-bottom, 0px));
  user-select: none; -webkit-user-select: none;
}
@media (min-width: 600px) {
  #datewheel-backdrop { align-items: center; }
  #datewheel { border-radius: 18px; padding-bottom: 12px; }
}
.dw-head { display: flex; align-items: center; justify-content: space-between; gap: 8px; min-height: 48px; }
.dw-head .dw-current { flex: 1 1 auto; text-align: center; font-size: 1.02em; }
.dw-head button, .dw-foot button {
  background: none; border: 1px solid var(--border-soft); color: var(--text); border-radius: 10px;
  padding: 8px 14px; min-height: 40px; font-size: 0.95em; font-family: inherit; cursor: pointer;
}
.dw-head .dw-done { background: var(--accent); color: var(--surface); border-color: var(--accent); font-weight: 600; }
.dw-cols { position: relative; display: flex; gap: 6px; margin: 4px 0 8px; }
.dw-col {
  position: relative; flex: 1 1 0; height: 200px; overflow-y: auto; overscroll-behavior: contain;
  scroll-snap-type: y mandatory; -webkit-overflow-scrolling: touch; scrollbar-width: none;
  mask-image: linear-gradient(to bottom, transparent, #000 25%, #000 75%, transparent);
  -webkit-mask-image: linear-gradient(to bottom, transparent, #000 25%, #000 75%, transparent);
}
.dw-col::-webkit-scrollbar { display: none; }
.dw-col-year { flex-grow: 1.3; }
.dw-item {
  height: 40px; line-height: 40px; text-align: center; scroll-snap-align: center;
  font-size: 1.15em; color: var(--text-muted); font-variant-numeric: tabular-nums; cursor: pointer;
}
.dw-item.dw-spacer { scroll-snap-align: none; cursor: default; }
.dw-item.dw-picked { color: var(--text); font-weight: 600; font-size: 1.25em; }
.dw-band {
  position: absolute; left: 0; right: 0; top: 80px; height: 40px; pointer-events: none;
  border-top: 1px solid var(--border-soft); border-bottom: 1px solid var(--border-soft);
  background: var(--surface-sunken); opacity: 0.45; border-radius: 8px;
}
.dw-type {
  position: absolute; left: 4px; right: 4px; height: 40px; box-sizing: border-box; z-index: 1;
  text-align: center; font-size: 1.2em; font-weight: 600; padding: 0 6px; margin: 0;
  border: 2px solid var(--accent); border-radius: 8px; background: var(--surface); color: var(--text);
}
.dw-foot { display: flex; gap: 8px; justify-content: center; }
.dw-foot button { flex: 1 1 0; }

/* ---- Doubles merge (menuDoublesModal) ------------------------------------ */
.dbl-group { border: 1px solid var(--border-soft); border-radius: 10px; padding: 8px 10px; margin: 8px 0; }
.dbl-name { margin: 0 0 6px; }
.dbl-name small { color: var(--text-muted); font-weight: 400; }
.dbl-item { display: flex; gap: 10px; align-items: flex-start; padding: 6px 0; cursor: pointer; }
.dbl-item input { margin-top: 4px; }
.dbl-actions { display: flex; justify-content: flex-end; margin-top: 4px; }
.pl-same { display: block; color: var(--warn-high); font-weight: 600; }

/* Pay someone on another day (the owner, 2026-10-14). */
.pay-anyone { margin: 12px 0 4px; }
.pay-anyone .field-help { margin: 4px 0 0; }

/* Inventory tables (the owner, 2026-10-14): the first column stays in
   view as a side banner while the rest scrolls sideways, so the row
   being edited is never a mystery. Since the same day every data table
   does this -- the rules are in the next section. */

/* ---- Sticky header row and first column, every data table (the owner,
   2026-10-14) -------------------------------------------------------------
   Swipe down or right and the header row and the first column stay where
   they are while the rest scrolls under them; swipe back and the table
   comes back out from under them. position: sticky does exactly this.

   What a cell sticks TO is the nearest box that scrolls. The Admin pane
   scrolls sideways (every table in it is wider than a phone), so a table
   sitting straight in the pane keeps its first column, but its header
   cannot stick: the pane never scrolls vertically, the page does. The
   tables that are tall AND wide -- inventory, inventory bulk edit, menu
   bulk edit, buying prices, the Tabs history -- therefore get a box of
   their own, no taller than the screen, that scrolls both ways; inside it
   both the header and the column hold. The Open / Active list on Tabs
   scrolls with the page, so its header sticks under the top bar.

   The first column's background is the table's surface with the row's
   own tint (hover, flagged, unsaved) painted over it as a gradient, so a
   highlighted row stays highlighted all the way to its left edge while
   the cells scrolling under it are still hidden. --topbar-h is measured
   by app.js (wireTopbarAutoHide); 56px is the bar's floor. */
:root { --topbar-h: 56px; }
:is(table.admin-table, table.history-table, table.exp-table, table.rmass-table, table.sched-grid, table.dash-heat) {
  /* A clipped table is its own scroll box and sticky sticks to nothing. */
  overflow: visible;
}
:is(table.admin-table, table.history-table, table.exp-table, table.rmass-table, table.sched-grid, table.dash-heat) thead th {
  position: sticky; top: 0; z-index: 3; background: var(--surface-sunken);
}
:is(table.admin-table, table.history-table, table.exp-table, table.rmass-table, table.sched-grid, table.dash-heat) tbody > tr:not(.perm-group-row) > :is(td, th):first-child,
:is(table.admin-table, table.history-table, table.exp-table, table.rmass-table, table.sched-grid, table.dash-heat) tfoot > tr > td:first-child {
  position: sticky; left: 0; z-index: 2;
  background: linear-gradient(var(--row-tint, transparent), var(--row-tint, transparent)) var(--surface);
  box-shadow: 2px 0 0 var(--border-soft);
}
/* Swipe away from the first column and it slides out of the way; swipe
   back a little and it slides in again (the owner, 2026-10-15: "so it
   gives us more screen space"). The header row stays put. app.js
   (wireStickyColumnHide) sets .sticky-col-hidden on the box that scrolls. */
:is(table.admin-table, table.history-table, table.exp-table, table.rmass-table, table.sched-grid, table.dash-heat, table.pw-table) :is(thead, tbody, tfoot) > tr > :is(td, th):first-child,
.bulk-sticky { transition: transform 0.18s ease, opacity 0.18s ease; }
.sticky-col-hidden :is(table.admin-table, table.history-table, table.exp-table, table.rmass-table, table.sched-grid, table.dash-heat, table.pw-table) :is(thead, tbody, tfoot) > tr > :is(td, th):first-child,
.sticky-col-hidden .bulk-sticky {
  transform: translateX(-105%); opacity: 0; pointer-events: none;
}
@media (prefers-reduced-motion: reduce) {
  .sticky-col-hidden .bulk-sticky, .bulk-sticky { transition: none; }
}
/* The corner cell holds both ways and paints over the row and the column. */
:is(table.admin-table, table.history-table, table.exp-table, table.rmass-table, table.sched-grid, table.dash-heat) thead th:first-child {
  left: 0; z-index: 4; box-shadow: 2px 0 0 var(--border-soft);
}
/* The row tints the sticky cell carries with it. */
.admin-table tr.clickable:hover, .bulk-row.bulk-touched { --row-tint: var(--surface-alt); }
.admin-table tr.row-clickable:hover, .admin-table tr.row-clickable:focus-visible { --row-tint: var(--row-hover, rgba(127, 127, 127, 0.12)); }
.history-table tbody tr:hover, .rmass-tr.open, .rmass-expand { --row-tint: var(--surface-hover); }
.history-row-flag { --row-tint: color-mix(in srgb, var(--warn-mid, #e0a100) 10%, transparent); }
/* The table's rounded bottom corner, now that the table no longer clips. */
.admin-table tbody:last-of-type > tr:last-child > td:first-child { border-bottom-left-radius: 8px; }
/* A card is not a table (.stack-on-phone, above): its first cell heads
   the card and must not stick to the left edge when the pane is scrolled
   sideways. Same specificity as the rule above, later in the file. */
@media (max-width: 560px) {
  table.stack-on-phone tbody > tr:not(.perm-group-row) > :is(td, th):first-child { position: static; box-shadow: none; background: none; }
}

/* Tall and wide: a box no taller than the screen that scrolls both ways,
   so the header row and the first column both hold. The page takes over
   at the box's end (overscroll chains vertically), sideways it does not
   (overscroll-behavior-x above). The staff Schedule joined them on
   2026-10-22: its header row scrolled away with the page. */
.inv-scroll, .inv-bulk-scroll, .bulk-scroll, .history-month-body, .sched-scroll, .cashlog-scroll {
  overflow: auto;
  -webkit-overflow-scrolling: touch;
  max-height: calc(100vh - var(--topbar-h, 56px) - 16px);
  max-height: calc(100dvh - var(--topbar-h, 56px) - 16px);
}
/* Tabs: the New Tab button floats over the bottom of the list. */
#screen-tabs .history-month-body {
  max-height: calc(100vh - var(--topbar-h, 56px) - 100px);
  max-height: calc(100dvh - var(--topbar-h, 56px) - 100px);
}
/* The Open / Active list scrolls with the page: its header sticks under
   the top bar. */
#tabs-list > table.open-tabs-table thead th { top: var(--topbar-h, 56px); }
/* The "Not in stock yet" list in the price-list dialog is laid out as
   cards and never scrolls sideways; its sort chips hold at the top of the
   dialog while the lines scroll. */
.pl-new thead { position: sticky; top: 0; z-index: 3; background: var(--surface); }

/* ---- PIN keypad on touch screens (pinpad.js) ----------------------------
   Fixed to the bottom, above every dialog, so a PIN box anywhere gets it. */
#pinpad {
  position: fixed; left: 50%; bottom: 0; transform: translateX(-50%); z-index: 70;
  width: min(360px, 100%); padding: 10px 12px calc(10px + env(safe-area-inset-bottom, 0px));
  display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px;
  background: var(--surface); border: 1px solid var(--border-soft); border-bottom: none;
  border-radius: 16px 16px 0 0; box-shadow: 0 -6px 24px rgba(0, 0, 0, 0.25);
  user-select: none; -webkit-user-select: none; touch-action: manipulation;
}
#pinpad .pp-key {
  min-height: 54px; font-size: 1.4rem; font-weight: 600; border-radius: 12px; margin: 0;
  background: var(--surface-sunken); color: var(--text); border: 1px solid var(--border-soft);
}
#pinpad .pp-key:active { background: var(--surface-alt); }
#pinpad .pp-ok { background: var(--accent); color: var(--surface); border-color: var(--accent); }
#pinpad .pp-back { font-size: 1.3rem; }
body.pinpad-open { padding-bottom: 280px; }
input.pp-input.pp-active { outline: 2px solid var(--accent); outline-offset: 1px; }

/* --- Add old bill: paper bills typed in afterwards (2026-10-14) ---------
   Colors are the existing tokens, defined in all three theme blocks. */
.history-add-paper { flex: none; min-height: 44px; padding: 0 12px; border: 1px solid var(--border-soft); border-radius: 12px;
  background: var(--surface); color: var(--text); font-size: 0.9rem; font-weight: 600; white-space: nowrap; }
.history-add-paper:hover { border-color: var(--accent); background: var(--surface-hover); }
@media (max-width: 420px) { .history-add-paper { font-size: 0.8rem; padding: 0 9px; } }
.paper-tag { display: inline-block; vertical-align: middle; padding: 0 6px; border: 1px solid var(--border-soft); border-radius: 999px;
  background: var(--surface-sunken); color: var(--text-muted); font-size: 0.7rem; font-weight: 600; letter-spacing: 0.04em;
  text-transform: uppercase; line-height: 1.5; white-space: nowrap; }
button.paper-tag { min-height: 0; margin-left: 8px; cursor: default; }
button.paper-tag.has-photo { cursor: pointer; border-style: dashed; border-color: var(--text-muted); }
button.paper-tag.has-photo:hover { color: var(--text); border-color: var(--accent); }
#bill-headline .paper-tag { font-size: 0.65rem; vertical-align: 4px; }
#modal .paper-title { margin: 0 0 4px; }
#modal .paper-help { margin: 0 0 8px; }
#modal .paper-ways { gap: 6px; margin: 4px 0 8px; }
#modal .paper-ways .paper-way { display: inline-flex; align-items: center; gap: 6px; min-height: 44px; padding: 6px 12px; margin: 0; font-size: 0.9rem; cursor: pointer; }
#modal .paper-ways .paper-way svg { width: 22px; height: 22px; }
#modal .paper-ways .paper-way.listening { border-color: var(--danger-fill); color: var(--danger-fill); animation: paper-pulse 1.2s ease-in-out infinite; }
@keyframes paper-pulse { 0%, 100% { box-shadow: 0 0 0 0 transparent; } 50% { box-shadow: 0 0 0 5px color-mix(in srgb, var(--danger-fill) 25%, transparent); } }
#modal #paper-status.is-error, #modal #paper-lines-sum.is-error { color: var(--danger-fill); }
#modal .paper-photo-row { display: flex; align-items: center; gap: 10px; margin: 0 0 8px; }
#modal .paper-photo-row .exp-thumb { width: 56px; height: 56px; }
#modal .paper-similar { margin: 0 0 10px; padding: 8px 10px; border: 1px dashed var(--border); border-radius: 12px; background: var(--surface-sunken); }
#modal .paper-similar p { margin: 0 0 6px; font-size: 0.9rem; }
#modal .paper-similar-list { display: flex; flex-wrap: wrap; gap: 6px; }
#modal .paper-similar-list .bubble { margin: 0; min-height: 40px; padding: 6px 12px; font-size: 0.9rem; }
#modal .paper-lines-fold { margin: 6px 0 0; }
#modal .paper-lines-fold > summary { cursor: pointer; font-weight: 600; padding: 6px 0; }
#modal .paper-lines { display: flex; flex-direction: column; gap: 6px; margin: 6px 0; }
#modal .paper-line { display: grid; grid-template-columns: minmax(0, 1fr) 64px 92px 36px; gap: 6px; align-items: center; }
#modal .paper-line input { width: 100%; min-width: 0; margin: 0; padding: 8px; min-height: 40px; box-sizing: border-box; }
#modal .paper-line .pl-x { padding: 0; width: 36px; height: 36px; }
#modal .paper-lines-foot { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
@media (prefers-reduced-motion: reduce) { #modal .paper-ways .paper-way.listening { animation: none; } }

/* --- Clients from the daily sheets, add to existing, merge (2026-10-14) --- */
.client-tag { display: inline-block; margin-left: 6px; padding: 1px 7px; border-radius: 999px; font-size: 0.72rem; font-weight: 600;
  vertical-align: middle; white-space: nowrap; background: var(--badge-bg); color: var(--badge-text); }
.client-tag-sheets { background: var(--badge-open-bg); color: var(--badge-open-text); }
.client-tag-conflict { background: var(--badge-void-bg); color: var(--warn-mid); }
.hist-plus { color: var(--text-muted); font-weight: 400; white-space: nowrap; }
.hist-analyze { border: 1px solid var(--border-soft); border-radius: 10px; padding: 10px 12px; margin: 10px 0 14px; }
.hist-analyze-row { gap: 10px; flex-wrap: wrap; align-items: center; }
.hist-upload-btn { display: inline-flex; align-items: center; padding: 10px 18px; border: 1px solid var(--border-soft); border-radius: 999px; cursor: pointer; font-size: inherit; }
.hist-lines table { width: 100%; }
.cm-modes { margin: 4px 0 8px; }
.cm-list { display: flex; flex-direction: column; gap: 4px; margin-top: 4px; max-height: 220px; overflow-y: auto; }
.cm-row { text-align: left; padding: 8px 10px; border: 1px solid var(--border-soft); border-radius: 8px; background: transparent; color: inherit; cursor: pointer; }
.cm-row small { color: var(--text-muted); }
.cm-chosen { margin-top: 4px; font-weight: 600; min-height: 1.2em; }
.cm-names { border: 1px solid var(--border-soft); border-radius: 10px; padding: 8px 10px; margin: 4px 0 10px; }
.cm-conflict { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; }

/* --- Tonight on Home, and the calendar (2026-10-14) ------------------------ */
.today-tonight { border: 1px solid var(--border-soft); border-radius: 14px; background: var(--surface); padding: 10px 14px; margin: 8px 0 14px; }
.today-tonight h2 { font-size: 1.05rem; margin: 0 0 6px; }
.tn-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 6px 14px; }
.tn-block h3 { font-size: 0.82rem; text-transform: uppercase; letter-spacing: 0.05em; color: var(--text-muted); margin: 4px 0 2px; }
.tn-block ul { list-style: none; margin: 0; padding: 0; }
.tn-block li { padding: 2px 0; font-size: 0.95rem; }
.tn-empty { color: var(--text-muted); font-size: 0.9rem; margin: 0; }
.cal-dot { display: inline-block; width: 9px; height: 9px; border-radius: 50%; margin-right: 6px; vertical-align: middle; background: var(--cal-google); }
.cal-k-reservation { background: var(--cal-reservation); }
.cal-k-payment { background: var(--cal-payment); }
.cal-k-task { background: var(--cal-task); }
.cal-k-notice { background: var(--cal-notice); }
.cal-k-google { background: var(--cal-google); }
.cal-head { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-bottom: 8px; }
.cal-head strong { flex: 1; text-align: center; min-width: 140px; }
.cal-grid { display: grid; grid-template-columns: repeat(7, minmax(0, 1fr)); gap: 3px; }
.cal-wd { font-size: 0.72rem; text-align: center; color: var(--text-muted); padding: 2px 0; }
.cal-cell { min-height: 54px; border: 1px solid var(--border-soft); border-radius: 8px; padding: 3px; background: var(--surface); text-align: left; cursor: pointer; color: var(--text); font: inherit; }
.cal-cell.out { opacity: 0.4; }
.cal-cell.today { border-color: var(--accent); border-width: 2px; }
.cal-cell.picked { background: var(--surface-hover); }
.cal-cell .cal-n { font-size: 0.8rem; font-weight: 700; display: block; }
.cal-cell .cal-dots { display: flex; flex-wrap: wrap; gap: 2px; margin-top: 2px; }
.cal-cell .cal-dot { width: 7px; height: 7px; margin: 0; }
.cal-legend { display: flex; flex-wrap: wrap; gap: 10px; font-size: 0.8rem; color: var(--text-muted); margin: 8px 0; }
.cal-day { margin-top: 10px; }
.cal-day h4 { margin: 4px 0 6px; }
.cal-item { display: flex; gap: 8px; align-items: baseline; padding: 6px 0; border-top: 1px solid var(--border-soft); }
.cal-item.done .cal-title { text-decoration: line-through; color: var(--text-muted); }
.cal-item .cal-time { font-variant-numeric: tabular-nums; color: var(--text-muted); min-width: 42px; }
.cal-item .cal-body { flex: 1; min-width: 0; }
.cal-item small { display: block; color: var(--text-muted); }
.cal-item .cal-actions button { min-height: 32px; padding: 2px 10px; font-size: 0.82rem; }
@media (max-width: 560px) { .cal-cell { min-height: 44px; } }

/* Incomings owed back (2026-10-14): the line under the row, the total
   on Outstanding, and the Pay back button on its own line. */
.in-owed, .in-pb { display: block; }
.in-owed-due { color: var(--danger); font-weight: 600; }
.ob-owed-total { margin: 4px 0 10px; }
.ob-in-actions { margin-top: 6px; }
.in-pb .link-btn { display: inline; min-height: 0; padding: 0 4px; margin: 0; font-size: 0.85em; vertical-align: baseline; }

/* A new version is ready (2026-10-14): a small bar at the bottom; a tap
   reloads. Hidden on the customer's screen like every staff bar. */
.update-bar {
  position: fixed; left: 50%; bottom: calc(14px + env(safe-area-inset-bottom)); transform: translateX(-50%);
  z-index: 2000; padding: 8px 14px; border-radius: 999px; border: none;
  white-space: nowrap; font-size: 0.82rem; min-height: 0;
  background: var(--text); color: var(--bg); font-weight: 600; box-shadow: 0 4px 16px rgba(0, 0, 0, 0.25);
}
body.client-view .update-bar { display: none !important; }

/* Close day (2026-10-14): a cream button with a gold rim, beside the Tabs
   title. The rim is a gradient border (padding-box cream, border-box gold). */
.tabs-title-row { display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap; }
.tabs-title-row h1 { margin-bottom: 0; }
.close-day-btn {
  border: 2px solid transparent; border-radius: 999px; padding: 8px 20px; min-height: 42px;
  background: linear-gradient(#fbf4e2, #f3e6c4) padding-box,
              linear-gradient(170deg, #f6e6a8 0%, #d4af37 38%, #a8791f 62%, #f3dd94 100%) border-box;
  color: #6b4e12; font-weight: 700; letter-spacing: 0.02em;
  box-shadow: 0 2px 8px rgba(168, 121, 31, 0.25);
}
.close-day-btn:active { transform: translateY(1px); }
.dc-head { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.dc-head input[type="date"] { width: auto; }
.dc-sheet h4 { margin: 18px 0 6px; }
.dc-row { display: flex; justify-content: space-between; gap: 12px; padding: 4px 0; border-bottom: 1px solid var(--border); }
.dc-row.dc-sub { padding-left: 14px; color: var(--text-muted); font-size: 0.92em; }
.dc-row.dc-big { font-weight: 700; font-size: 1.12em; border-bottom: 2px solid var(--border); }
.dc-row.dc-private span:first-child::after { content: " \1F512"; font-size: 0.8em; }
.dc-warn { background: var(--dc-warn-bg); color: var(--dc-warn-text); border-radius: 10px; padding: 8px 12px; margin: 8px 0; }
.dc-count { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; margin: 8px 0; }
.dc-count input { width: 10em; }

/* Tonight panel: which night, when it is the one ahead (2026-10-15). */
.today-tonight h2 .tn-night { font-size: 0.6em; font-weight: 600; color: var(--text-muted); margin-left: 6px; }

.today-tonight .tn-tomorrow { margin-top: 14px; padding-top: 10px; border-top: 1px solid var(--border); }
.today-tonight .tn-shift { color: var(--text-muted); font-size: 0.9em; }
.today-tonight .tn-legend { margin: 8px 0 0; color: var(--text-muted); font-size: 0.78rem; }

/* The drink search under the category bubbles on a bill. */
.bill-drink-search { display: block; width: 100%; box-sizing: border-box; margin: 0 0 8px; font-size: 16px; }
#menu-search-results:not(:empty) { margin-bottom: 12px; }

/* Guest menu look (2026-10-19): one row per picture slot. */
.gml-slot { display: flex; align-items: center; gap: 10px; padding: 8px 0; border-top: 1px solid var(--border); flex-wrap: wrap; }
.gml-thumb { width: 56px; height: 70px; border-radius: 10px; background: #0d0b08 center / cover no-repeat; color: #d4af37;
  display: inline-flex; align-items: center; justify-content: center; font-size: 0.65rem; text-align: center; flex: none; }
.gml-label { flex: 1 1 140px; font-weight: 600; }
.gml-pick, .gml-remove { margin: 0; }

/* Guest menu landing pages and staff return code (2026-10-19). */
.gmp-list { display: flex; flex-direction: column; gap: 8px; margin: 6px 0 4px; }
.gmp-row { border: 1px solid var(--border); border-radius: 12px; padding: 10px; background: var(--surface); }
.gmp-row.is-off .gmp-thumb, .gmp-row.is-off .gmp-name { opacity: 0.55; }
.gmp-main { display: flex; align-items: center; gap: 10px; }
.gmp-thumb { width: 52px; height: 72px; font-size: 0.85rem; font-weight: 700; letter-spacing: 0.08em; }
.gmp-info { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 3px; }
.gmp-name { font-weight: 600; overflow-wrap: anywhere; }
.gmp-meta { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; font-size: 0.82rem; color: var(--text-muted); }
.gmp-text { font-size: 0.82rem; color: var(--text-muted); display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
.gmp-chip { display: inline-block; padding: 1px 8px; border-radius: 999px; border: 1px solid currentColor; font-size: 0.75rem; font-weight: 600; line-height: 1.5; }
.gmp-chip.on { color: var(--positive); }
.gmp-chip.wait { color: var(--cpoll-warn); }
.gmp-chip.off { color: var(--text-muted); }
.gmp-move { display: flex; flex-direction: column; gap: 4px; flex: none; }
.gmp-move button { margin: 0; min-height: 36px; min-width: 44px; padding: 4px 10px; font-size: 1rem; line-height: 1; }
.gmp-move button:disabled { opacity: 0.3; cursor: default; }
.gmp-actions { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 8px; }
.gmp-actions button, .gmp-actions label, .gmp-add, .gmp-add-row button, .gmp-code-row button {
  margin: 0; min-height: 40px; padding: 8px 14px; font-size: 0.92rem; }
.gmp-actions label, .gmp-add { display: inline-flex; align-items: center; justify-content: center; box-sizing: border-box;
  border-radius: 999px; border: 1px solid var(--border); background: var(--surface); color: var(--text); font-weight: 500; cursor: pointer; }
.gmp-actions label:hover { background: var(--surface-hover); }
.gmp-add-row label.gmp-add { background: var(--accent); color: var(--accent-text); border-color: var(--accent); margin: 0; font-size: 0.92rem; }
.gmp-add-row label.gmp-add:hover { background: var(--accent-hover); }
.gmp-actions .gmp-delete { background: var(--surface); color: var(--danger); border-color: var(--danger); margin-left: auto; }
.gmp-actions .gmp-delete:hover { background: rgba(var(--danger-rgb), 0.08); }
.gmp-form { margin-top: 10px; padding-top: 8px; border-top: 1px dashed var(--border); }
#modal .gmp-form .field { margin: 0 0 8px; }
#modal .gmp-form .field label { margin-top: 0; }
#modal .gmp-form textarea { margin: 0; padding: 8px; border: 1px solid var(--border); border-radius: 6px;
  background: var(--surface); color: var(--text); font-family: inherit; resize: vertical; }
.gmp-form input[type="text"], .gmp-form textarea, .gmp-form input[type="date"] { width: 100%; box-sizing: border-box; font-size: 16px; }
.gmp-dates { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
.gmp-add-row { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 2px; }
.gmp-implicit { border-style: dashed; }
.gmp-code { margin: 4px 0 8px; }
.gmp-code-state { font-size: 0.9rem; color: var(--text-muted); margin-bottom: 6px; }
.gmp-code-state.is-set { color: var(--positive); font-weight: 600; }
.gmp-code-row { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
.gmp-code-row input { width: 5.5em; font-size: 20px; letter-spacing: 0.4em; text-align: center; padding: 8px; box-sizing: border-box; }
@media (max-width: 420px) {
  .gmp-actions button, .gmp-actions label { flex: 1 1 calc(50% - 6px); }
  .gmp-actions .gmp-delete { margin-left: 0; }
  .gmp-dates { grid-template-columns: 1fr; }
}
/* The picture slots' Upload / Replace is a <label> round a file input;
   drawn as the same pill as the Remove button next to it. */
.gml-slot label.gml-pick { display: inline-flex; align-items: center; justify-content: center; box-sizing: border-box;
  min-height: 40px; padding: 8px 14px; margin: 0; border-radius: 999px; border: 1px solid var(--border);
  background: var(--surface); color: var(--text); font-size: 0.92rem; font-weight: 500; cursor: pointer; }
.gml-slot label.gml-pick:hover { background: var(--surface-hover); }
.gml-slot .gml-remove { min-height: 40px; padding: 8px 14px; font-size: 0.92rem; }
/* RJ's Best Sellers (2026-10-19, pos/static/gm_bestsellers.js): settings,
   then one fold per category -- top sellers, the owner's picks, a drink
   search and what guests see. Existing tokens only. */
#modal .gmbs-settings { display: flex; flex-wrap: wrap; align-items: flex-end; gap: 8px 12px; margin: 10px 0 4px; }
#modal .gmbs-settings .field { flex: 1 1 150px; margin-bottom: 0; }
#modal .gmbs-settings .primary { flex: 0 0 auto; }
.gmbs-sets { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; margin: 12px 0 8px; }
.gmbs-sets-label { flex-basis: 100%; font-size: 0.85em; color: var(--text-muted); }
#modal .gmbs-sets .bubble { padding: 4px 14px; min-height: 36px; font-size: 0.95rem; margin: 0; }
details.gmbs-cat { border-top: 1px solid var(--border); }
details.gmbs-cat > summary { display: flex; align-items: baseline; justify-content: space-between; gap: 8px;
  padding: 12px 2px; cursor: pointer; list-style: none; }
details.gmbs-cat > summary::-webkit-details-marker { display: none; }
details.gmbs-cat > summary::before { content: "\25B8"; color: var(--text-muted); font-size: 0.8em; margin-right: 2px; }
details.gmbs-cat[open] > summary::before { content: "\25BE"; }
.gmbs-cat-name { flex: 1 1 auto; font-weight: 700; min-width: 0; overflow-wrap: anywhere; }
.gmbs-body { padding: 0 2px 12px; }
.gmbs-body h5 { margin: 12px 0 4px; font-size: 0.8em; text-transform: uppercase; letter-spacing: 0.04em; color: var(--text-muted); }
.gmbs-auto, .gmbs-picks, .gmbs-preview { list-style: none; margin: 0; padding: 0; }
.gmbs-auto li, .gmbs-preview li, .gmbs-line { display: flex; align-items: baseline; gap: 8px; }
.gmbs-auto li, .gmbs-preview li { padding: 5px 0; border-bottom: 1px solid var(--border-faint); }
.gmbs-name { flex: 1 1 auto; min-width: 0; overflow-wrap: anywhere; }
.gmbs-price { flex: 0 0 auto; font-variant-numeric: tabular-nums; white-space: nowrap; }
.gmbs-qty { flex: 0 0 auto; color: var(--text-muted); font-size: 0.85em; white-space: nowrap; }
.gmbs-pick { padding: 8px 0; border-bottom: 1px solid var(--border-faint); }
.gmbs-pick.gmbs-off .gmbs-name { color: var(--text-muted); text-decoration: line-through; }
.gmbs-tools { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; margin-top: 6px; }
/* #modal: .card label (block, muted, margin-top) would win otherwise. */
#modal label.gmbs-pin { display: inline-flex; align-items: center; gap: 8px; margin: 0 auto 0 0; font-size: 0.95em;
  color: var(--text); cursor: pointer; }
.gmbs-pin input { width: 20px; height: 20px; margin: 0; flex: none; }
.gmbs-pin span { line-height: 20px; }
#modal .gmbs-tools button { margin: 0; min-height: 36px; padding: 4px 12px; }
#modal .gmbs-tools .gmbs-icon { min-width: 40px; padding: 4px 8px; }
.gmbs-tag { display: inline-block; margin-left: 6px; padding: 0 6px; border-radius: 6px; font-size: 0.75em;
  background: var(--badge-db-bg); color: var(--badge-db-text); vertical-align: 1px; }
.gmbs-star { color: var(--cpoll-bar); }
#modal input.gmbs-search { display: block; width: 100%; box-sizing: border-box; margin: 10px 0 4px; font-size: 16px; }
.gmbs-hits { display: flex; flex-direction: column; gap: 4px; }
#modal .gmbs-hit { display: flex; align-items: baseline; gap: 8px; width: 100%; margin: 0; text-align: left; min-height: 40px; }
/* On Sale now (2026-10-19, pos/menu_sales.py): a small red SALE tag on the
   till's tiles, the bill's lines and the Best Sellers modal. */
.sale-tag { display: inline-block; vertical-align: middle; padding: 1px 6px; border-radius: 6px;
  font-size: 0.68rem; font-weight: 800; letter-spacing: 0.06em; line-height: 1.35;
  background: var(--danger-fill); color: var(--danger-text); }
.gmbs-rank { display: inline-block; min-width: 2.2em; text-align: center; padding: 0 5px; border-radius: 999px;
  font-size: 0.75em; font-weight: 800; vertical-align: 1px; }
.gmbs-gold { background: rgba(var(--tier-gold-rgb), 0.9); color: var(--tier-gold-ink); }
.gmbs-silver { background: rgba(var(--tier-silver-rgb), 0.9); color: var(--tier-silver-ink); }
.gmbs-rec { font-size: 0.72em; color: var(--text-muted); letter-spacing: 0.04em; text-transform: uppercase; }
.gmbs-was { color: var(--text-muted); font-weight: 400; }
.gmbs-sale-summary { margin: 10px 0 4px; font-weight: 600; }
.gmbs-sale-summary .field-help { display: block; font-weight: 400; }
.gmbs-sale { margin: 8px 0 4px; padding: 10px 12px 12px; border-radius: 12px; border: 1px solid rgba(var(--danger-rgb), 0.35);
  background: rgba(var(--danger-rgb), 0.04); }
.gmbs-sale h5 { margin-top: 0; }
.gmbs-sale-now { margin: 4px 0 8px; display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 8px; }
.gmbs-sale-when { flex-basis: 100%; font-size: 0.85em; color: var(--text-muted); }
.gmbs-sale-price { font-variant-numeric: tabular-nums; }
.gmbs-sale-label { display: block; font-size: 0.85em; font-weight: 600; margin-bottom: 4px; }
.gmbs-sale-chosen { display: flex; align-items: baseline; gap: 8px; padding: 6px 0; font-weight: 600; }
.gmbs-sale-chosen .gmbs-price { font-weight: 400; color: var(--text-muted); font-size: 0.9em; }
#modal input.gmbs-sale-search { display: block; width: 100%; box-sizing: border-box; margin: 2px 0 4px; font-size: 16px; }
.gmbs-sale-row { display: flex; flex-wrap: wrap; gap: 8px 12px; }
.gmbs-sale-row .field { flex: 1 1 140px; min-width: 0; }
#modal .gmbs-sale input[type="date"], #modal .gmbs-sale input[type="number"], #modal .gmbs-sale input[type="text"] {
  width: 100%; box-sizing: border-box; font-size: 16px; }
.gmbs-ro { list-style: none; margin: 8px 0 0; padding: 0; }
.gmbs-ro-row { display: flex; flex-direction: column; gap: 2px; padding: 8px 0; border-bottom: 1px solid var(--border-faint); }
/* Bluetooth printer fallback: a link that looks like the primary button,
   because a tap on a link is what lets the browser open the printer app. */
.modal-actions a.bt-print-link { display: inline-flex; align-items: center; justify-content: center; min-height: 44px;
  padding: 10px 20px; border-radius: 999px; background: var(--accent); color: var(--accent-text); font-weight: 600; text-decoration: none; }

/* ---- Night view: one business night analysed (day_report.js, 2026-10-19) --
   Existing tokens only (all three theme blocks define them): the chart
   series are --chart-1 (sales) and --chart-2 (tabs), the closed hours
   are --surface-sunken, the typical-weekday tick is --text. */
#screen-day .day-top { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 8px; margin-bottom: 6px; }
#screen-day .day-back { margin: 0; min-height: 44px; padding: 8px 16px; font-size: 0.95rem; }
#screen-day .day-nav { display: flex; align-items: center; gap: 6px; }
#screen-day .day-arrow { margin: 0; min-width: 44px; min-height: 44px; padding: 0; font-size: 1.6rem; line-height: 1; }
#screen-day .day-arrow:disabled { opacity: 0.35; cursor: default; }
#screen-day .day-date { min-height: 44px; padding: 6px 10px; border-radius: 12px; border: 1px solid var(--border-soft);
  background: var(--surface); color: var(--text); font-size: 1rem; max-width: 170px; }
.day-head { margin: 6px 0 10px; }
.day-title { margin: 0 0 2px; font-size: 1.35rem; }
.day-live { display: inline-block; margin-left: 6px; padding: 0 8px; border-radius: 999px; font-size: 0.75rem; font-weight: 600;
  background: var(--surface-sunken); color: var(--text); border: 1px solid var(--border-soft); }
.day-loading { color: var(--text-muted); }
.day-kpis { margin-bottom: 6px; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); }
.day-kpis .dash-kpi-sub { white-space: normal; }
.day-extra { margin: 0 0 12px; color: var(--text-muted); font-size: 0.85rem; }
.day-mix { margin: 0 0 14px; }
.day-mix-bar { display: flex; gap: 2px; height: 12px; border-radius: 6px; overflow: hidden; background: var(--surface-sunken); }
.day-mix-bar span { flex-basis: 0; min-width: 3px; }
.day-mix-card { background: var(--chart-5); }
.day-mix-cash { background: var(--chart-3); }
.day-mix-other { background: var(--chart-4); }
.day-mix-legend { display: flex; flex-wrap: wrap; gap: 4px 16px; margin-top: 6px; font-size: 0.88rem; color: var(--text-muted); }
.day-mix-item b { color: var(--text); font-weight: 600; }
.day-mix-item small { color: var(--text-muted); }
.day-mix-sw { display: inline-block; width: 10px; height: 10px; border-radius: 3px; margin-right: 6px; vertical-align: -1px; }
.day-card { margin-bottom: 14px; }
.day-chart { margin: 6px 0 2px; min-width: 0; }
.day-chart-head { display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between; gap: 2px 12px; font-size: 0.85rem; }
.day-chart-title { font-weight: 600; }
.day-legend { color: var(--text-muted); font-size: 0.8rem; }
.day-sw { display: inline-block; width: 10px; height: 10px; border-radius: 3px; margin: 0 4px 0 6px; vertical-align: -1px; }
.day-sw-sales { background: var(--chart-1); }
.day-sw-typical { height: 0; border-top: 2px dashed var(--text); border-radius: 0; width: 14px; vertical-align: 3px; }
.day-svg { display: block; max-width: 100%; overflow: visible; touch-action: pan-y; }
.day-svg text { font-size: 10px; font-family: inherit; }
.day-axis { fill: var(--text-muted); }
.day-grid { stroke: var(--chart-grid); stroke-width: 1; }
.day-base { stroke: var(--border-soft); stroke-width: 1; }
.day-closed-band { fill: var(--surface-sunken); }
.day-closed-text { fill: var(--text-muted); font-style: italic; }
.day-bar-sales { fill: var(--chart-1); }
.day-bar-tabs { fill: var(--chart-2); }
.day-typical { stroke: var(--text); stroke-width: 2; stroke-dasharray: 3 2; opacity: 0.75; }
.day-peak { fill: var(--text); font-weight: 700; }
.day-hit { fill: transparent; cursor: pointer; }
.day-col.sel .day-hit { fill: var(--chart-grid); }
.day-hour-detail { min-height: 1.4em; margin: 4px 0 6px; padding: 6px 10px; border-radius: 10px; background: var(--surface-sunken);
  font-size: 0.86rem; color: var(--text); }
.day-hour-detail:empty { display: none; }
.day-later { margin: 0 0 6px; }
.day-sub { margin: 12px 0 4px; font-size: 0.95rem; }
.day-facts { list-style: none; margin: 0; padding: 0; display: grid; gap: 6px; }
.day-fact { position: relative; padding: 7px 10px 7px 26px; border-radius: 10px; background: var(--surface-sunken); font-size: 0.9rem; line-height: 1.35; }
.day-fact::before { content: "\2022"; position: absolute; left: 11px; top: 6px; color: var(--text-muted); font-weight: 700; }
.day-fact.good::before { content: "\25B2"; color: var(--positive); font-size: 0.7rem; top: 9px; left: 9px; }
.day-fact.weak::before { content: "\25BC"; color: var(--danger); font-size: 0.7rem; top: 9px; left: 9px; }
.day-fold { margin-top: 12px; }
.day-fold > summary { cursor: pointer; font-weight: 600; font-size: 0.92rem; padding: 6px 0; min-height: 32px; }
.day-fold > summary:hover { color: var(--accent); }
.day-hour-table td small { color: var(--text-muted); }
.day-hour-table tr.day-closed-row td { color: var(--text-muted); }
.day-sales-head { display: flex; flex-wrap: wrap; gap: 8px 14px; align-items: center; justify-content: space-between; margin-bottom: 8px; }
.day-sales-head h4 { margin: 0; }
/* The Tabs history's table, in a box of its own that scrolls both ways so
   the header row and the first column hold (the sticky rules further up). */
.day-sales-scroll {
  overflow: auto; -webkit-overflow-scrolling: touch;
  max-height: calc(100vh - var(--topbar-h, 56px) - 16px);
  max-height: calc(100dvh - var(--topbar-h, 56px) - 16px);
  border: 1px solid var(--border-faint); border-radius: 10px;
}
.day-sales-table { font-size: 0.85rem; }
.day-sales-table th, .day-sales-table td { padding: 6px 8px; }
.day-sales-table th[data-dsort] { cursor: pointer; }
.day-sales-table td small { white-space: nowrap; }
.day-sales-table tbody tr:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.day-sales-table tfoot td { font-weight: 700; border-top: 2px solid var(--border-soft); background: var(--surface); }
.day-sales-table .day-neg { color: var(--danger); }
/* The history list hides three columns on a phone; this list keeps them
   all and scrolls sideways instead. */
@media (max-width: 560px) {
  .history-table.day-sales-table th:nth-child(n), .history-table.day-sales-table td:nth-child(n) { display: table-cell; }
  #screen-day .day-top { gap: 6px; }
  #screen-day .day-back { padding: 8px 12px; }
  #screen-day .day-date { max-width: 150px; font-size: 0.95rem; }
  .day-kpis { grid-template-columns: 1fr 1fr; gap: 8px; }
  .day-kpis .dash-kpi-value { font-size: 1.15rem; }
}
.day-drill-link { margin: 4px 0 10px; }
.day-drill-link button { margin: 0; }
/* ====================================================================   Screensaver (pos/static/screensaver.js, the owner 2026-10-19)
   ====================================================================   A minute idle (three with a task open) puts the RJ Members Lounge mark up
   full screen. Light: the logo as printed, black on white. Dark: the same
   mark in gold on black. A gold shimmer crosses it every 9 s.
   The overlay sits directly under <body>. Its own tokens, in all three
   theme blocks, are kept here so the block travels as one piece. */
:root {
  --saver-bg: #ffffff;
  --saver-halo: rgba(0, 0, 0, 0);
  --saver-g1: #000000;
  --saver-g2: #000000;
  --saver-g3: #000000;
  --saver-g4: #000000;
  --saver-g5: #000000;
  --saver-g6: #000000;
  --saver-g7: #000000;
  --saver-shine: rgba(236, 204, 128, 0.92);
  --saver-shine-edge: rgba(201, 160, 72, 0.38);
  --saver-glow: 0 22px 48px rgba(0, 0, 0, 0.16);
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --saver-bg: #000000;
    --saver-halo: rgba(201, 160, 72, 0.08);
    --saver-g1: #6b4d14;
    --saver-g2: #b48a33;
    --saver-g3: #f2d98f;
    --saver-g4: #cfa24a;
    --saver-g5: #8c6520;
    --saver-g6: #e8c977;
    --saver-g7: #9e762a;
    --saver-shine: rgba(255, 249, 228, 0.95);
    --saver-shine-edge: rgba(255, 228, 160, 0.35);
    --saver-glow: 0 0 64px rgba(201, 160, 72, 0.22);
  }
}
:root[data-theme="dark"] {
  --saver-bg: #000000;
  --saver-halo: rgba(201, 160, 72, 0.08);
  --saver-g1: #6b4d14;
  --saver-g2: #b48a33;
  --saver-g3: #f2d98f;
  --saver-g4: #cfa24a;
  --saver-g5: #8c6520;
  --saver-g6: #e8c977;
  --saver-g7: #9e762a;
  --saver-shine: rgba(255, 249, 228, 0.95);
  --saver-shine-edge: rgba(255, 228, 160, 0.35);
  --saver-glow: 0 0 64px rgba(201, 160, 72, 0.22);
}

#screensaver {
  position: fixed;
  inset: 0;
  z-index: 2147483647;
  display: flex;
  align-items: center;
  justify-content: center;
  background: radial-gradient(circle at 50% 50%, var(--saver-halo) 0, rgba(0, 0, 0, 0) 58%), var(--saver-bg);
  opacity: 0;
  transition: opacity 0.7s ease;
  touch-action: none;
  overscroll-behavior: none;
  user-select: none;
  -webkit-user-select: none;
  -webkit-touch-callout: none;
  -webkit-tap-highlight-color: transparent;
}
#screensaver.on { opacity: 1; }
.saver-logo {
  position: relative;
  /* 25% smaller than the first cut (the owner, 2026-10-20). */
  width: min(51vmin, 330px);
  aspect-ratio: 1 / 1;
  /* A box-shadow on the round box, not a drop-shadow of the mark: that
     would show through the lettering, which is cut out of the disc. */
  border-radius: 50%;
  box-shadow: var(--saver-glow);
  transform: scale(0.97);
  transition: transform 1.4s cubic-bezier(0.2, 0.7, 0.2, 1);
}
#screensaver.on .saver-logo { transform: scale(1); }
.saver-mark,
.saver-shine {
  position: absolute;
  inset: 0;
  -webkit-mask: url("/screensaver-logo.svg") center / contain no-repeat;
  mask: url("/screensaver-logo.svg") center / contain no-repeat;
}
.saver-mark {
  background: linear-gradient(135deg, var(--saver-g1) 0%, var(--saver-g2) 20%, var(--saver-g3) 36%,
    var(--saver-g4) 50%, var(--saver-g5) 68%, var(--saver-g6) 86%, var(--saver-g7) 100%);
}
/* The sweep: a narrow diagonal band, 1.4 s from left to right, then 7.6 s
   of rest. The band lives in a picture three times the logo's width, so
   at either end of its travel it is entirely off the mark. */
.saver-shine {
  background: linear-gradient(110deg, rgba(0, 0, 0, 0) 0%, rgba(0, 0, 0, 0) 41%, var(--saver-shine-edge) 46%,
    var(--saver-shine) 50%, var(--saver-shine-edge) 54%, rgba(0, 0, 0, 0) 59%, rgba(0, 0, 0, 0) 100%);
  background-size: 300% 100%;
  background-repeat: no-repeat;
  background-position: 100% 0;
  animation: saver-sweep 9s ease-in-out 0.8s infinite;
}
@keyframes saver-sweep {
  0% { background-position: 100% 0; }
  15.5% { background-position: 0% 0; }
  100% { background-position: 0% 0; }
}
@media (prefers-reduced-motion: reduce) {
  #screensaver, .saver-logo { transition: none; }
  .saver-logo, #screensaver.on .saver-logo { transform: none; }
  .saver-shine { animation: none; display: none; }
}
/* Print: English or 日本語, two big buttons; the last choice on this
   device is lit (2026-10-19). */
.print-lang-row { display: flex; gap: 12px; margin: 12px 0 4px; }
#modal .print-lang-btn { flex: 1 1 0; margin: 0; min-height: 72px; font-size: 1.25rem; font-weight: 700;
  border-radius: 14px; border: 2px solid var(--border); background: var(--surface); color: var(--text); }
#modal .print-lang-btn.selected { border-color: var(--accent); background: var(--accent); color: var(--accent-text); }
/* The browser's own print of a receipt or the day's sales (a Mac): the
   same 32-cell lines as the paper from the Bluetooth printer. */
#receipt-print-area .receipt-lines { width: max-content; max-width: 100%; margin: 0 auto; font-size: 13px; line-height: 1.35; }
#receipt-print-area .receipt-lines .rl { white-space: pre; font-family: ui-monospace, Menlo, Consolas, "Courier New", monospace; }
#receipt-print-area .receipt-lines .rl-center,
#receipt-print-area .receipt-lines .rl-center_bold { text-align: center; }
#receipt-print-area .receipt-lines .rl-bold,
#receipt-print-area .receipt-lines .rl-center_bold,
#receipt-print-area .receipt-lines .rl-total { font-weight: 700; }
/* Tall, not wide, like the paper: the line keeps its 32 cells. */
#receipt-print-area .receipt-lines .rl-total { transform: scaleY(1.6); margin: 0.3em 0; }
#receipt-print-area .receipt-lines .rl-small { font-size: 0.8em; white-space: pre-wrap; }
#receipt-print-area .receipt-logo { display: block; width: 120px; margin: 4px auto 8px; }
/* Guest menu offers (2026-10-19, pos/static/guest_offers.js): the chips in
   the guest menu popup, its custom offer form and "Push these tonight",
   and the Offers page. Existing tokens only. */
.gmo { margin: 14px 0 6px; padding-top: 12px; border-top: 1px solid var(--border); }
.gmo-head { display: flex; align-items: baseline; justify-content: space-between; gap: 8px; }
.gmo-head h4 { margin: 0; }
.gmo-chips { display: flex; flex-wrap: wrap; gap: 8px; margin: 8px 0; }
#modal .gmo-chip { display: inline-flex; flex-direction: column; align-items: flex-start; gap: 1px; margin: 0; max-width: 100%;
  min-height: 44px; padding: 7px 14px; text-align: left; border-radius: 16px; line-height: 1.25; }
.gmo-chip-title { font-weight: 600; overflow-wrap: anywhere; }
.gmo-chip small { font-size: 0.78em; opacity: 0.85; overflow-wrap: anywhere; }
#modal .gmo-custom { flex-direction: row; align-items: stretch; padding: 0; gap: 0; }
#modal .gmo-custom button { margin: 0; min-height: 44px; border: 0; background: none; color: inherit; border-radius: 0; }
#modal .gmo-custom .gmo-chip-body { display: flex; flex-direction: column; align-items: flex-start; gap: 1px; padding: 7px 6px 7px 14px; text-align: left; }
#modal .gmo-custom .gmo-chip-x { padding: 0 12px; font-size: 1.25rem; line-height: 1; opacity: 0.8; }
#modal .gmo-add { margin: 2px 0 4px; }
.gmo-form { margin: 10px 0; padding: 12px 14px; border: 1px solid var(--border); border-radius: 12px; background: var(--surface-sunken); }
.gmo-form h4, .gmo-form h5 { margin: 0 0 4px; }
.gmo-form h5 { font-size: 0.8em; text-transform: uppercase; letter-spacing: 0.04em; color: var(--text-muted); }
#modal .gmo-form .field { margin-bottom: 8px; }
#modal .gmo-form input[type="text"], #modal .gmo-form textarea, #modal .gmo-form select, #modal .gmo-form input[type="date"] {
  width: 100%; box-sizing: border-box; font-size: 16px; }
.gmo-form-row { display: grid; grid-template-columns: 1fr 1fr; gap: 0 10px; }
@media (max-width: 520px) { .gmo-form-row { grid-template-columns: 1fr; } }
#modal label.gmo-check { display: flex; align-items: center; gap: 8px; margin: 4px 0 8px; color: var(--text); font-size: 0.95em; cursor: pointer; }
.gmo-check input { width: 20px; height: 20px; margin: 0; flex: none; }
.gmo-form-actions { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 6px; }
#modal .gmo-form-actions button { margin: 0; }
.gmo-label { display: block; margin-top: 4px; font-size: 0.85em; color: var(--text-muted); }
#modal .gmo-kinds { margin: 6px 0 2px; gap: 6px; }
#modal .gmo-kinds .bubble { margin: 0; min-height: 38px; padding: 4px 14px; }
.gmo-price { font-weight: 600; }
.gmo-was { color: var(--text-muted); font-size: 0.9em; }
.gmo-approve { margin: 2px 0 8px; padding: 8px 10px; border-left: 3px solid var(--bell-high); border-radius: 6px; background: var(--surface); }
.gmo-ask-q { font-size: 1.1em; font-weight: 600; }
.gmo-push { margin-top: 12px; }
.gmo-push h5 { margin: 0; font-size: 0.8em; text-transform: uppercase; letter-spacing: 0.04em; color: var(--bell-high); }
.gmo-push ul, .gmo-slow, .gmo-list { list-style: none; margin: 6px 0 0; padding: 0; }
.gmo-push li, .gmo-slow li { display: flex; align-items: center; gap: 10px; padding: 8px 0; border-bottom: 1px solid var(--border-faint); }
.gmo-push-name { flex: 1 1 auto; min-width: 0; overflow-wrap: anywhere; }
.gmo-push-name small { display: block; color: var(--text-muted); font-size: 0.82em; margin-top: 2px; }
#modal .gmo-push li button, #modal .gmo-slow li button { flex: 0 0 auto; margin: 0; min-height: 38px; padding: 4px 12px; }
.gmo-note { margin: 6px 0 10px; padding: 8px 12px; border-left: 3px solid var(--bell-high); background: var(--surface-sunken);
  border-radius: 6px; font-size: 0.9em; }
.gmo-page-actions { margin: 8px 0 4px; }
.gmo-item { display: flex; align-items: flex-start; gap: 10px; padding: 10px 0; border-bottom: 1px solid var(--border-faint); }
.gmo-item-main { flex: 1 1 auto; min-width: 0; overflow-wrap: anywhere; }
.gmo-item-text { margin-top: 2px; }
.gmo-item-tools { flex: 0 0 auto; display: flex; flex-wrap: wrap; justify-content: flex-end; gap: 6px; }
#modal .gmo-item-tools button { margin: 0; min-height: 36px; padding: 4px 12px; }
@media (max-width: 520px) { .gmo-item { flex-direction: column; } .gmo-item-tools { justify-content: flex-start; } }
.gmo-tag, .gmo-status { display: inline-block; margin-left: 6px; padding: 0 7px; border-radius: 6px; font-size: 0.75em; vertical-align: 1px;
  background: var(--surface-sunken); color: var(--text-muted); border: 1px solid var(--border-soft); }
.gmo-status-on { background: transparent; color: var(--backup-ok); border-color: var(--backup-ok); font-weight: 600; }
.gmo-status-upcoming { background: var(--badge-db-bg); color: var(--badge-db-text); border-color: transparent; }
.gmo-st-off .gmo-item-title strong, .gmo-st-ended .gmo-item-title strong { color: var(--text-muted); }
.od-slow { color: var(--bell-high); font-weight: 600; }

/* Admin > Clients: the tier's name beside its badge, so a sorted column
   reads at a glance (the owner, 2026-10-20). */
.client-tier-cell { white-space: nowrap; }
.client-tier-name { font-size: 0.82em; color: var(--text-muted); margin-left: 4px; }

/* Reports > Membership money (2026-10-19). Colors are the existing
   tokens only, so all three theme blocks already cover it. */
.mm-help { color: var(--text-muted); margin-top: 0; }
.mm-tools { align-items: center; gap: 8px; margin-bottom: 6px; }
.mm-tools select { width: auto; min-width: 80px; }
.mm-scroll { overflow-x: auto; max-width: 100%; }
.mm-table th.num, .mm-table td.num { text-align: right; white-space: nowrap; font-variant-numeric: tabular-nums; }
.mm-table tr.mm-partial td { color: var(--text-muted); }
.mm-table td:first-child { white-space: nowrap; }
.mm-table tr.mm-total td { font-weight: 700; border-top: 2px solid var(--border-soft); }
.mm-comp { background: var(--surface); border: 1px solid var(--border-soft); border-radius: 12px; padding: 10px 12px; margin: 0 0 8px; }
.mm-comp-head { display: flex; justify-content: space-between; gap: 10px; font-weight: 600; }
.mm-comp-head span:last-child { white-space: nowrap; font-variant-numeric: tabular-nums; }
.mm-comp-sub { color: var(--text-muted); font-size: 0.9em; margin-top: 2px; overflow-wrap: anywhere; }
.mm-comp-hint { color: var(--positive); font-size: 0.9em; margin-top: 4px; overflow-wrap: anywhere; }
.mm-sim-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); gap: 10px 14px; margin: 6px 0 8px; }
.mm-sim-grid label { display: flex; flex-direction: column; gap: 4px; color: var(--text-muted); font-size: 0.92em; }
.mm-sim-grid input { width: 100%; box-sizing: border-box; }
.mm-sim-out { max-width: 560px; font-size: 1rem; }
.mm-sim-out .mm-ok { color: var(--positive); }
.mm-sim-out .mm-over { color: var(--danger); }
@media (max-width: 600px) {
  .mm-sim-grid { grid-template-columns: 1fr; }
  .mm-table th, .mm-table td { padding: 6px 6px; }
}

/* --- A guest's order from the guest menu (2026-10-20, pos/guest_order.py) -- */
.gorder-title { margin-bottom: 2px; }
.gorder-who { margin: 0; font-size: 1.25rem; font-weight: 700; color: var(--text); }
/* A drink Add to tab could not put on (2026-10-20): red, with why. */
.gorder-line.gorder-not-added .gorder-name, .gorder-line.gorder-not-added .gorder-qty { color: var(--danger); }
.gorder-line .gorder-why { display: block; color: var(--danger); font-weight: 600; }
.gorder-lines { list-style: none; margin: 12px 0 8px; padding: 0; border-top: 1px solid var(--border-soft); }
.gorder-line { display: flex; align-items: baseline; gap: 10px; padding: 10px 2px; border-bottom: 1px solid var(--border-soft); font-size: 1.05rem; }
.gorder-qty { flex: 0 0 auto; min-width: 2.6em; font-weight: 700; color: var(--accent); font-variant-numeric: tabular-nums; }
.gorder-name { flex: 1 1 auto; min-width: 0; overflow-wrap: anywhere; }
.gorder-name small { color: var(--text-muted); font-size: 0.82rem; }
.gorder-name small.gorder-alt { display: block; }
.gorder-amt { flex: 0 0 auto; font-variant-numeric: tabular-nums; color: var(--text-muted); }
.gorder-total { display: flex; justify-content: space-between; align-items: baseline; padding: 6px 2px 0; font-size: 1.05rem; }
.gorder-total strong { font-size: 1.3rem; font-variant-numeric: tabular-nums; }
.gorder-goes { margin: 10px 0 0; font-weight: 600; }
.gorder-state { margin: 12px 0 0; font-weight: 600; }
.gorder-done { color: var(--light-green); }
.gorder-declined { color: var(--text-muted); }
.gorder-pick { margin-top: 12px; }
/* Admin > Menu item: the guest menu sheet's fields. */
fieldset.gm-sheet-fields { border: 1px solid var(--border-soft); border-radius: 12px; padding: 10px 14px 12px; }
fieldset.gm-sheet-fields legend { font-weight: 700; padding: 0 6px; }
fieldset.gm-sheet-fields > label { display: block; margin-top: 10px; }
fieldset.gm-sheet-fields textarea, fieldset.gm-sheet-fields input[type="text"] { width: 100%; box-sizing: border-box; }
fieldset.gm-sheet-fields .gml-slot { border-top: 0; }

/* A saved login waiting for the server (boot): a card over the screen, so
   it never looks like the device signed the person out. */
#boot-wait {
  position: fixed; inset: 0; z-index: 60; display: flex; align-items: center; justify-content: center;
  padding: 16px; background: var(--bg);
}
#boot-wait .boot-wait-card {
  max-width: 360px; width: 100%; padding: 22px 20px; border-radius: 16px; text-align: center;
  background: var(--surface); color: var(--text); border: 1px solid var(--border-soft);
}
#boot-wait .boot-wait-title { margin: 0 0 6px; font-weight: 700; font-size: 1.1rem; }
#boot-wait .boot-wait-note { margin: 0 0 16px; color: var(--text-muted); }

/* A drink the guest sent from the guest menu's cart (the owner, 2026-10-20):
   a small gold tag under the name, on the bill the guest is shown. */
.guest-ordered-tag {
  display: inline-block; padding: 1px 8px; border-radius: 999px; font-size: 0.72rem; font-weight: 700; letter-spacing: 0.02em;
  color: var(--tier-gold-ink); background: rgba(var(--tier-gold-rgb), 0.9);
}

/* Fetch GMO now (2026-10-21): the button and what the Mac answered. */
.gmo-fetch-box { margin-top: 16px; display: flex; flex-direction: column; align-items: flex-start; gap: 6px; }
.gmo-fetch-box .field-help { margin: 0; }
.gmo-fetch-failed { color: var(--danger); }

/* People who joined a free flow later (2026-10-21): one row per clock in
   the banner, each with its own time, Extend and End. */
.ff-groups { flex-basis: 100%; display: flex; flex-direction: column; gap: 6px; margin: 4px 0; }
.ff-group-row { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 10px; padding: 6px 10px;
  border-radius: 10px; background: var(--surface); border: 1px solid var(--border-soft); }
.ff-group-row.ff-warning { border-color: var(--warn-mid); }
.ff-group-row.ff-warning .ff-group-clock { color: var(--warn-mid); }
.ff-group-row.ff-expired { border-color: var(--warn-high); }
.ff-group-clock { font-weight: 800; font-variant-numeric: tabular-nums; min-width: 5.5em; }
.ff-group-row.ff-expired .ff-group-clock { color: var(--warn-high); }
.ff-group-text { flex: 1 1 10em; min-width: 0; }
.ff-group-actions { display: flex; gap: 6px; margin-left: auto; }
.ff-group-actions button { margin: 0; padding: 4px 10px; min-height: 34px; font-size: 0.88rem; }

/* Cash log (2026-10-22): figures right-aligned, short in red, over in green. */
.cashlog-table td, .cashlog-table th { white-space: nowrap; }
.cashlog-table td:not(:first-child) { text-align: right; font-variant-numeric: tabular-nums; }
.cashlog-short { color: var(--danger); font-weight: 700; }
.cashlog-over { color: var(--success, #1e7f3a); font-weight: 700; }

/* Top bar: a tap shows at once (the owner, 2026-10-22: the buttons felt
   slow). ID selector, as #topbar button rules beat class rules. */
#topbar button, #brand { touch-action: manipulation; -webkit-tap-highlight-color: transparent; }
#topbar button:active, #brand:active { transform: scale(0.93); filter: brightness(1.25); transition: none; }
@media (prefers-reduced-motion: reduce) { #topbar button:active, #brand:active { transform: none; } }

/* Menu | Inventory tabs at the top of a drink's or a stock item's popup
   (the owner, 2026-10-22). They stay in view while the form scrolls;
   the negative margins reach the card's 20px padding. */
#modal .link-tabs {
  position: sticky; top: -20px; z-index: 5;
  display: flex; gap: 6px;
  margin: -20px -20px 12px; padding: 12px 20px 10px;
  background: var(--surface); border-bottom: 1px solid var(--border);
}
#modal .link-tab {
  flex: 1; margin: 0; padding: 10px 12px; border-radius: 8px; font-weight: 600;
  border: 1px solid var(--border); background: var(--surface); color: var(--text);
}
#modal .link-tab.selected { background: var(--accent); color: var(--accent-text); border-color: var(--accent); }
