/* ==========================================================================
   Zatila theme: base elements and the ui.js component classes, for the
   kitchen tablet (body.tablet) and the admin (body.admin), light and dark.

   - Built on /css/tokens.css, imported below, so a page links only this file
     (linking tokens.css as well does no harm). Read docs/design/RESEARCH.md
     §3 for the rules behind every value.
   - The Zatila frame never takes the restaurant's colour: buttons, focus,
     tabs and status come from the tokens (RESEARCH §3.7).
   - Kitchen: the primary button is the strongest inverse of its surface,
     never Saffron, because Saffron means NEW there. Admin: Saffron with Ink.
   - Section 1 keeps the old variable names (--ink, --muted, --ok …) as
     aliases, so screens not yet restyled still render (ARCH §11.1). New code
     reads the tokens, never the aliases. The holds left from before the
     restyle (2 Nov 2026 audit): --brand*, --line-2 and the Cream-200
     --surface-3 had no reader left and are gone; --th-action and the
     reduced-motion guard (now for transitions only) say below what still
     needs them.
   ========================================================================== */

@import url("tokens.css");


/* --------------------------------------------------------------------------
   1. Legacy aliases
   -------------------------------------------------------------------------- */

/* Saffron and Ink as the frame uses them. Kept (2 Nov 2026): ui.js Card and
   PageHeader still accept a section colour that they set as --accent inline,
   and seven stylesheets (admin-pages, auth, billing, control, recipes,
   reports, setup) draw their focus rings with var(--th-action). Captured at
   the root, so a button or ring inside a coloured block stays Saffron. */
:root {
  --th-action: var(--accent);         /* Saffron */
  --th-on-action: var(--on-accent);   /* Ink on Saffron: 9.7 */
}

/* Old names that follow the theme and the paper surface, so they are
   declared again on every scope the tokens change. */
:root,
[data-theme],
[data-surface="paper"] {
  /* text and lines */
  --ink: var(--text);
  --ink-2: var(--text-2);
  --muted: var(--text-3);           /* Ink-500: 5.7 on Cream; Ink-300: 7.9 on Ink */
  --faint: var(--text-3);           /* was 2.9:1; text keeps 4.5:1 now */
  --overlay: var(--scrim);

  /* status, always with a word: Basil = ok, Tomato = danger, Saffron = warn
     (--warn is Ink on light, Saffron on dark). Zatila has no blue: info is neutral. */
  --ok: var(--ready-text); --ok-bg: var(--ready-tint); --ok-line: color-mix(in srgb, var(--ready) 40%, var(--ready-tint));
  --danger: var(--late-text); --danger-bg: var(--late-tint); --danger-line: color-mix(in srgb, var(--late) 40%, var(--late-tint));
  --warn: var(--new-text); --warn-bg: var(--new-tint); --warn-line: color-mix(in srgb, var(--new) 60%, var(--new-tint));
  --mustard: var(--new); --mustard-50: var(--new-tint);
  --info: var(--text-2); --info-bg: var(--fill-secondary); --info-line: var(--line);

  /* shape, depth, type, motion */
  --radius-xs: var(--radius-sm); --radius: var(--radius-md); --radius-xl: var(--radius-lg);
  --shadow-1: var(--e1); --shadow-2: var(--e2); --shadow-3: var(--e3);
  --font: var(--font-sans); --mono: var(--font-mono); --ease: var(--ease-out);
}

/* Sizes (rem, so the kitchen text-size control scales them) */
:root {
  --tap: var(--a-target);           /* 44: Apple's minimum */
  --gap: var(--s-16);
  --page-pad: var(--s-20);
  --topbar-h: 4rem;
}
body.tablet {
  --tap: var(--k-target-min);       /* 56: the kitchen floor */
  --page-pad: var(--k-margin);
  --topbar-h: var(--k-topbar);
}


/* --------------------------------------------------------------------------
   2. Values that differ by theme beyond the tokens. Same three blocks as
      tokens.css: light, forced dark, then OS dark when <html> has no theme.
      - --surface-3 is the tokens' dialog surface (White on light); the
        Cream-200 hold is gone since tablet.css paints the kitchen's syncing
        and offline pill with --fill-secondary (2 Nov 2026).
      - --th-dialog / --th-toast: the true dialog and toast surfaces.
      - --th-tint-2: how much of a content colour goes into a tint
        (editors.css category tiles).
   -------------------------------------------------------------------------- */
:root,
[data-theme="light"] {
  --th-dialog: var(--z-white); --th-toast: var(--z-ink);
  --th-tint-2: 14%;
}
[data-theme="dark"] {
  --th-dialog: var(--z-dark-3); --th-toast: var(--z-dark-3);
  --th-tint-2: 32%;
}
@media (prefers-color-scheme: dark) {
  :root:where(:not([data-theme="light"])) {
    --th-dialog: var(--z-dark-3); --th-toast: var(--z-dark-3);
    --th-tint-2: 32%;
  }
}


/* --------------------------------------------------------------------------
   3. Base
   -------------------------------------------------------------------------- */
*, *::before, *::after { box-sizing: border-box; }

/* No px font-size here: the kitchen text-size control sets 100–150 %. */
html { -webkit-text-size-adjust: 100%; text-size-adjust: 100%; }

body {
  margin: 0; font-family: var(--font-sans); font-size: 1rem; font-weight: 400; line-height: 1.5;   /* admin body 16/24 */
  color: var(--text); background: var(--bg);
  -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; -webkit-tap-highlight-color: transparent;
  min-height: 100vh; min-height: 100dvh;
}
body.tablet { font-size: 1.125rem; user-select: none; -webkit-user-select: none; }   /* 18: the kitchen minimum */
body.tablet input, body.tablet textarea { user-select: text; -webkit-user-select: text; }

h1, h2, h3, h4 { margin: 0; line-height: 1.2; letter-spacing: var(--ls-title); text-wrap: balance; }
h1 { font-size: 1.75rem; font-weight: 700; }     /* 28 */
h2 { font-size: 1.375rem; font-weight: 600; }    /* 22, title-3 */
h3 { font-size: 1.125rem; font-weight: 600; }    /* 18, headline */
p { margin: 0; text-wrap: pretty; }
a { color: var(--accent-text); text-decoration-thickness: 1px; text-underline-offset: 0.2em; }
img, svg { max-width: 100%; }
button, input, select, textarea { font: inherit; color: inherit; }
button { cursor: pointer; }
strong, b { font-weight: 700; }
hr { border: 0; border-top: 1px solid var(--line); margin: var(--s-16) 0; }
::selection { background: color-mix(in srgb, var(--th-action) 40%, transparent); }

/* Focus: always visible on keyboard focus. 2 px Ink, then a 3 px Saffron
   ring (4 px with Increase Contrast). An outline, so a component's own
   box-shadow never hides the Saffron part. */
:focus-visible,
.input:focus,
.textarea:focus,
.select:focus { outline: var(--focus-w) solid var(--th-action); outline-offset: 2px; box-shadow: 0 0 0 2px var(--z-ink); }

.sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }

/* Lucide icons from /icons/lucide.svg: <svg class="icon"><use href="/icons/lucide.svg#i-name"/></svg>.
   20 px in admin, 28 px in the kitchen; set --icon-size for another size. */
.icon {
  display: inline-block; flex: none; vertical-align: middle;
  width: var(--icon-size, var(--a-icon)); height: var(--icon-size, var(--a-icon));
  fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round;
}
body.tablet .icon { width: var(--icon-size, var(--k-icon)); height: var(--icon-size, var(--k-icon)); }


/* --------------------------------------------------------------------------
   4. Layout helpers and text utilities
   -------------------------------------------------------------------------- */
.page { padding: var(--page-pad); padding-bottom: calc(var(--page-pad) + 6rem); max-width: 90rem; margin: 0 auto; }
.stack { display: flex; flex-direction: column; gap: var(--gap); }
.stack-sm { display: flex; flex-direction: column; gap: var(--s-8); }
.stack-lg { display: flex; flex-direction: column; gap: 1.75rem; }
.row { display: flex; align-items: center; gap: var(--s-12); }
.row-top { display: flex; align-items: flex-start; gap: var(--s-12); }
.row-wrap { display: flex; align-items: center; gap: 0.625rem; flex-wrap: wrap; }
.between { justify-content: space-between; }
.grow { flex: 1 1 auto; min-width: 0; }
.spacer { flex: 1; }
.grid { display: grid; gap: var(--gap); grid-template-columns: repeat(auto-fill, minmax(var(--min, 15rem), 1fr)); }
.grid-2 { display: grid; gap: var(--gap); grid-template-columns: repeat(2, minmax(0, 1fr)); }
.grid-3 { display: grid; gap: var(--gap); grid-template-columns: repeat(3, minmax(0, 1fr)); }
.center { display: grid; place-items: center; }
.muted { color: var(--text-3); }
.faint { color: var(--text-3); }
.small { font-size: 0.875rem; }
.tiny { font-size: 0.75rem; }
.big { font-size: 1.25rem; }
/* Numbers: JetBrains Mono inside a sentence (0.95 em, so cap heights match),
   tabular, ligatures off (it ships coding `calt`). Outfit digits in columns
   use .num (tnum). */
.mono {
  font-family: var(--font-mono); font-size: var(--num-inline); font-weight: 500;
  font-variant-numeric: tabular-nums; font-variant-ligatures: none; font-feature-settings: "tnum" 1, "calt" 0, "liga" 0;
}
.num { font-variant-numeric: tabular-nums; }
.bold { font-weight: 700; }
.upper { font: var(--t-eyebrow); letter-spacing: var(--ls-eyebrow); text-transform: uppercase; }
.nowrap { white-space: nowrap; }
.ellipsis { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.section-title { display: flex; align-items: baseline; gap: 0.625rem; margin: var(--s-8) 0 var(--s-12); }
.section-title h2 { font-size: 1.125rem; }
.section-title .muted { font-size: 0.9375rem; }

@media (max-width: 720px) {
  .grid-2, .grid-3 { grid-template-columns: 1fr; }
  :root, body.tablet { --page-pad: var(--s-16); }
  .hide-sm { display: none !important; }
}
@media (min-width: 721px) { .show-sm { display: none !important; } }


/* --------------------------------------------------------------------------
   5. Buttons. One Saffron action per admin view; the kitchen primary is the
      inverse of its surface. Pressed: scale 0.97 and 6 % darker.
   -------------------------------------------------------------------------- */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--s-8);
  min-height: var(--tap); padding: 0 var(--s-16); border: 1.5px solid transparent; border-radius: var(--radius-md);
  font: var(--t-button); text-decoration: none; white-space: nowrap; touch-action: manipulation;
  transition: transform var(--dur-press) var(--ease-out), background-color var(--dur-quick) ease,
    color var(--dur-quick) ease, border-color var(--dur-quick) ease;
}
body.tablet .btn { font-size: 1.125rem; line-height: 1.375rem; }
.btn:active:not(:disabled) {
  transform: scale(var(--press-scale)); transition-duration: var(--dur-instant);
  background-image: linear-gradient(var(--press-tint), var(--press-tint));
}
.btn:disabled { opacity: 0.45; cursor: not-allowed; }
.btn-icon { font-size: 1.15em; line-height: 1; display: inline-flex; }
.btn-block { display: flex; width: 100%; }

.btn-sm { min-height: 2.25rem; padding: 0 var(--s-12); font-size: 0.875rem; border-radius: var(--radius-sm); }
body.tablet .btn-sm { font-size: 1rem; }
.btn-lg { min-height: calc(var(--tap) + 0.5rem); padding: 0 var(--s-24); font-size: 1.0625rem; }
body.tablet .btn-lg { font-size: 1.25rem; line-height: 1.625rem; }
.btn-xl { min-height: calc(var(--tap) + 1.5rem); padding: 0 var(--s-32); font-size: 1.25rem; line-height: 1.625rem; }
body.tablet .btn-xl { font: var(--k-button); }   /* 22/28: 80 px Done, Start, Ready */

.btn-primary { background: var(--th-action); color: var(--th-on-action); }
body.tablet .btn-primary { background: var(--inverse); color: var(--on-inverse); }
.btn-secondary { background: var(--fill-secondary); color: var(--text); }
.btn-ghost { background: transparent; color: var(--text-2); }
.btn-danger { background: var(--late); color: var(--on-late); }        /* White on Tomato 5.17 */
.btn-success { background: var(--ready); color: var(--on-ready); }     /* White on Basil 4.99 */
.btn-warn { background: var(--new); color: var(--on-new); }            /* Ink on Saffron 9.7 */

@media (hover: hover) and (pointer: fine) {
  .btn:hover:not(:disabled) { background-image: linear-gradient(color-mix(in srgb, var(--press-tint), transparent 50%), color-mix(in srgb, var(--press-tint), transparent 50%)); }
  .btn-ghost:hover:not(:disabled) { background-color: var(--fill-secondary); color: var(--text); }
}

.icon-btn {
  position: relative; display: inline-grid; place-items: center; flex: 0 0 auto; width: var(--tap); height: var(--tap);
  border: 1.5px solid transparent; border-radius: var(--radius-md); background: transparent;
  font-size: 1.35rem; color: var(--text-2); touch-action: manipulation;
  transition: transform var(--dur-press) var(--ease-out), background-color var(--dur-quick) ease, color var(--dur-quick) ease;
}
.icon-btn:active:not(:disabled) {
  transform: scale(var(--press-scale)); transition-duration: var(--dur-instant);
  background-image: linear-gradient(var(--press-tint), var(--press-tint));
}
.icon-btn:disabled { opacity: 0.45; cursor: not-allowed; }
.icon-btn.secondary { background: var(--fill-secondary); color: var(--text); }
.icon-btn.primary { background: var(--th-action); color: var(--th-on-action); }
body.tablet .icon-btn.primary { background: var(--inverse); color: var(--on-inverse); }
.icon-btn.sm { width: 2.25rem; height: 2.25rem; font-size: 1.05rem; border-radius: var(--radius-sm); }
@media (hover: hover) and (pointer: fine) {
  .icon-btn:hover:not(:disabled) { background-color: var(--fill-secondary); color: var(--text); }
  .icon-btn.primary:hover:not(:disabled) { background-color: var(--th-action); color: var(--th-on-action); }
}
/* Admin also runs on phones and tablets: small controls keep a 40 px target.
   On the tablet the small icon buttons are the dialog close and the search
   clear, so wet hands get 48 px. */
body.admin .btn-sm { min-height: 2.5rem; }
body.admin .icon-btn.sm { width: 2.5rem; height: 2.5rem; }
body.tablet .icon-btn.sm { width: 3rem; height: 3rem; }
body.tablet .icon-btn.primary:hover:not(:disabled) { background-color: var(--inverse); color: var(--on-inverse); }

/* Count badge on an icon button (alerts): Tomato with White, 5.17 */
.icon-badge {
  position: absolute; top: 0.25rem; right: 0.25rem; display: grid; place-items: center;
  min-width: 1.25rem; height: 1.25rem; padding: 0 0.3125rem; border-radius: var(--radius-full);
  background: var(--late); color: var(--on-late); font: 700 0.75rem/1 var(--font-mono); font-variant-numeric: tabular-nums;
  box-shadow: 0 0 0 2px var(--surface);
}


/* --------------------------------------------------------------------------
   6. Cards and tiles
   -------------------------------------------------------------------------- */
.card {
  position: relative; background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius-md); box-shadow: var(--e1); padding: var(--a-card-pad);
}
.card.flat { box-shadow: none; }
.card.tight { padding: var(--s-12); }
.card.nopad { padding: 0; overflow: hidden; }
/* A section colour set by the restaurant (content, not the frame) */
.card[style*='--accent'] { border-top: 5px solid var(--accent); }
.card-click { cursor: pointer; transition: transform var(--dur-press) var(--ease-out), box-shadow var(--dur-quick) ease; }
.card-click:active { transform: scale(0.985); transition-duration: var(--dur-instant); }
@media (hover: hover) and (pointer: fine) {
  .card-click:hover { box-shadow: var(--e2); }
}
.card-head { display: flex; align-items: center; gap: 0.625rem; margin-bottom: 0.625rem; }
.card-head h3 { flex: 1; min-width: 0; }

/* Big coloured tile (home sections, tools). The screen sets --accent and,
   for coloured tiles, the text colour that reads on it. */
.tile {
  position: relative; display: flex; flex-direction: column; justify-content: space-between; gap: 0.875rem;
  min-height: 9.375rem; padding: var(--s-20); border-radius: var(--radius-lg);
  background: var(--accent); color: var(--z-white); border: 0; text-align: left; box-shadow: var(--e1);
  overflow: hidden; cursor: pointer; transition: transform var(--dur-press) var(--ease-out);
}
.tile::after {
  content: ''; position: absolute; right: -2.5rem; top: -2.5rem; width: 10rem; height: 10rem; border-radius: 50%;
  background: rgb(255 255 255 / 0.10);
}
.tile:active { transform: scale(0.98); transition-duration: var(--dur-instant); }
.tile-icon { font-size: 2.6rem; line-height: 1; }
.tile-title { font-size: 1.375rem; font-weight: 700; line-height: 1.15; }
.tile-sub { font-size: 0.9375rem; opacity: 0.92; }
.tile-count {
  position: absolute; top: 1rem; right: 1.125rem; z-index: 1; padding: 0.1875rem 0.625rem;
  border-radius: var(--radius-sm); background: rgb(0 0 0 / 0.2); font-weight: 600; font-size: 0.875rem;
}
.tile.light { background: var(--surface); color: var(--text); border: 1px solid var(--line); box-shadow: var(--e1); }
.tile.light::after { background: color-mix(in srgb, var(--accent) 9%, transparent); }
.tile.light .tile-count { background: var(--fill-secondary); color: var(--text-2); }


/* --------------------------------------------------------------------------
   7. Chips, badges, dots
   -------------------------------------------------------------------------- */
.chip {
  display: inline-flex; align-items: center; gap: 0.375rem; max-width: 100%;
  padding: 0.3125rem 0.75rem; border: 1px solid transparent; border-radius: var(--radius-sm);
  font-size: 0.875rem; font-weight: 600; line-height: 1.2; background: var(--fill-secondary); color: var(--text-2);
}
.chip.lg { padding: 0.5rem 1rem; font-size: 1rem; }
.chip.sm { padding: 0.1875rem 0.5625rem; font-size: 0.75rem; }
.chip.outline { background: transparent; border-color: var(--line-strong); }
.chip button { border: 0; background: transparent; padding: 0; margin-left: 2px; font-size: 1rem; color: inherit; opacity: 0.75; }
/* Filter chips: selected is monochrome, the inverse of the surface */
.chip-select {
  cursor: pointer; min-height: calc(var(--tap) - 0.5rem); padding: 0 var(--s-16);
  border: 1.5px solid transparent; background: var(--fill-secondary); color: var(--text); font-size: 0.9375rem;
  transition: background-color var(--dur-quick) ease, color var(--dur-quick) ease;
}
.chip-select[aria-pressed='true'] { background: var(--inverse); border-color: var(--inverse); color: var(--on-inverse); }

.badge {
  display: inline-flex; align-items: center; gap: 0.3125rem; padding: 0.1875rem 0.5625rem;
  border-radius: var(--radius-sm); white-space: nowrap; font-size: 0.75rem; font-weight: 600; line-height: 1.3;
  background: var(--fill-secondary); color: var(--text-2);
}
.badge-ok { background: var(--ready-tint); color: var(--on-ready-tint); }
.badge-warn { background: var(--new-tint); color: var(--on-new-tint); }
.badge-danger { background: var(--late-tint); color: var(--on-late-tint); }
.badge-info { background: var(--fill-secondary); color: var(--text-2); }
.badge-brand { background: var(--new); color: var(--on-new); }        /* "New", role: Ink on Saffron */
.badge-dark { background: var(--inverse); color: var(--on-inverse); }

.dot { width: 0.625rem; height: 0.625rem; border-radius: 50%; display: inline-block; flex: 0 0 auto; background: var(--text-3); }
.dot.ok, .dot.live { background: var(--ready); }
.dot.warn { background: var(--new); }
.dot.danger { background: var(--late); }


/* --------------------------------------------------------------------------
   8. Notes and callouts (house rules, safety)
   -------------------------------------------------------------------------- */
.note {
  display: flex; gap: 0.875rem; align-items: flex-start; padding: var(--s-16) 1.125rem;
  border: 1.5px solid var(--line); border-radius: var(--radius-md); background: var(--surface); line-height: 1.5;
}
.note-icon { font-size: 1.6rem; line-height: 1; flex: 0 0 auto; }
.note-title { font-weight: 700; margin-bottom: 2px; }
.note-body { color: var(--text-2); }
.note-info { background: var(--fill-secondary); border-color: transparent; }
.note-warn { background: var(--new-tint); border-color: color-mix(in srgb, var(--new) 60%, var(--new-tint)); }
.note-danger { background: var(--late-tint); border-color: color-mix(in srgb, var(--late) 40%, var(--late-tint)); }
.note-warn .note-body, .note-danger .note-body { color: var(--text); }
.note-rule { background: var(--surface); border-color: var(--line); }
.note-items { display: flex; flex-wrap: wrap; gap: var(--s-8); margin-top: var(--s-8); }
.rule-chip {
  display: inline-flex; align-items: center; gap: 0.375rem; padding: var(--s-8) 0.875rem;
  border: 1.5px solid var(--line); border-radius: var(--radius-md); background: var(--surface); font-size: 1rem; font-weight: 500;
}
.rule-chip strong { color: var(--text); }


/* --------------------------------------------------------------------------
   9. Forms. Label above (14/500), hint below (13), error in --late-text.
      Fields 44 px (56 in the kitchen), radius 14, --line-strong border.
   -------------------------------------------------------------------------- */
.field { display: flex; flex-direction: column; gap: 0.375rem; min-width: 0; }
.field-label { font: var(--t-label); color: var(--text-2); }
.field-hint { font: var(--t-footnote); color: var(--text-3); }
.field-error { font-size: 0.8125rem; line-height: 1.125rem; font-weight: 600; color: var(--late-text); }
.input, .textarea, .select {
  width: 100%; min-height: var(--tap); padding: 0.625rem 0.875rem;
  border: 1.5px solid var(--line-strong); border-radius: var(--radius-md); background: var(--surface); color: var(--text);
  transition: border-color var(--dur-quick) ease;
}
.input:focus, .textarea:focus, .select:focus { border-color: var(--text); }
.input::placeholder, .textarea::placeholder { color: var(--text-3); opacity: 1; }
.textarea { min-height: 6rem; resize: vertical; line-height: 1.45; }
.select {
  appearance: none;
  background-image: linear-gradient(45deg, transparent 50%, var(--text-3) 50%), linear-gradient(135deg, var(--text-3) 50%, transparent 50%);
  background-position: calc(100% - 20px) 50%, calc(100% - 14px) 50%; background-size: 6px 6px; background-repeat: no-repeat;
  padding-right: 2.25rem;
}
.input.sm, .select.sm { min-height: 2.25rem; padding: 0.375rem 0.625rem; border-radius: var(--radius-sm); font-size: 0.9375rem; }
.select.sm { padding-right: 2.125rem; }
.input.big { min-height: calc(var(--tap) + 0.75rem); font-size: 1.3rem; font-weight: 600; }
.input-row { display: flex; gap: var(--s-8); align-items: center; }

.searchbox { position: relative; display: flex; align-items: center; }
.searchbox .input { padding-left: 2.875rem; padding-right: 2.75rem; }
.searchbox-icon { position: absolute; left: 1rem; font-size: 1.1rem; color: var(--text-3); pointer-events: none; }
.searchbox-clear { position: absolute; right: 0.375rem; }
body.tablet .searchbox .input { padding-right: 3.625rem; }

.stepper { display: inline-flex; align-items: center; gap: var(--s-8); }
.stepper .icon-btn { background: var(--fill-secondary); color: var(--text); font-weight: 700; font-size: 1.6rem; }
.stepper input {
  width: 4.2em; min-height: var(--tap); text-align: center;
  border: 1.5px solid var(--line-strong); border-radius: var(--radius-md); background: var(--surface);
  font-family: var(--font-mono); font-weight: 700; font-size: 1.25rem; font-variant-numeric: tabular-nums;
}
.stepper.lg .icon-btn { width: calc(var(--tap) + 0.75rem); height: calc(var(--tap) + 0.75rem); }
.stepper.lg input { min-height: calc(var(--tap) + 0.75rem); font-size: 1.5rem; }

/* Toggle: monochrome. On is the inverse of the surface; status colours stay
   for status. The off track's border reaches 3:1. */
.toggle { display: inline-flex; align-items: center; gap: var(--s-12); cursor: pointer; min-height: 2.5rem; }
.toggle input { position: absolute; opacity: 0; pointer-events: none; }
.toggle-track {
  position: relative; flex: 0 0 auto; width: 3.25rem; height: 1.875rem; border-radius: var(--radius-full);
  background: var(--line-strong); transition: background-color var(--dur-quick) ease;
}
.toggle-thumb {
  position: absolute; top: 3px; left: 3px; width: 1.5rem; height: 1.5rem; border-radius: 50%;
  background: var(--z-white); box-shadow: 0 1px 3px rgb(21 23 28 / 0.25); transition: transform var(--dur-base) var(--ease-out);
}
.toggle input:checked + .toggle-track { background: var(--inverse); }
.toggle input:checked + .toggle-track .toggle-thumb { transform: translateX(1.375rem); background: var(--on-inverse); }
.toggle input:focus-visible + .toggle-track { outline: var(--focus-w) solid var(--th-action); outline-offset: 2px; box-shadow: 0 0 0 2px var(--z-ink); }
.toggle input:disabled + .toggle-track { opacity: 0.45; }

/* Big tappable checklist row: done is Basil with White, plus the line-through */
.check-row {
  display: flex; align-items: center; gap: 0.875rem; width: 100%; text-align: left; min-height: var(--tap); padding: 0.625rem 0.875rem;
  border: 1.5px solid var(--line); border-radius: var(--radius-md); background: var(--surface);
  transition: background-color var(--dur-quick) ease, border-color var(--dur-quick) ease;
}
.check-row .box {
  display: grid; place-items: center; flex: 0 0 auto; width: 1.875rem; height: 1.875rem;
  border: 2.5px solid var(--line-strong); border-radius: var(--radius-sm); font-weight: 700; color: var(--on-ready);
}
.check-row[aria-checked='true'] { background: var(--ready-tint); border-color: color-mix(in srgb, var(--ready) 40%, var(--ready-tint)); }
.check-row[aria-checked='true'] .box { background: var(--ready); border-color: var(--ready); }
.check-row[aria-checked='true'] .check-text { text-decoration: line-through; color: var(--text-3); }


/* --------------------------------------------------------------------------
   10. Tabs and segmented controls: the selected one is monochrome
   -------------------------------------------------------------------------- */
.tabs { display: flex; gap: var(--s-4); border-bottom: 1.5px solid var(--line); overflow-x: auto; scrollbar-width: none; }
.tabs::-webkit-scrollbar { display: none; }
.tab {
  display: inline-flex; align-items: center; gap: var(--s-8); white-space: nowrap;
  min-height: var(--tap); padding: var(--s-12) var(--s-16); margin-bottom: -1.5px;
  border: 0; border-bottom: 3px solid transparent; background: transparent; font-weight: 600; color: var(--text-3);
  transition: color var(--dur-quick) ease, border-color var(--dur-quick) ease;
}
.tab[aria-selected='true'] { color: var(--text); border-bottom-color: var(--text); }
.tab .count {
  padding: 1px var(--s-8); border-radius: var(--radius-sm); background: var(--fill-secondary);
  font: 500 0.75rem/1.25rem var(--font-mono); font-variant-numeric: tabular-nums;
}

.segmented {
  display: inline-flex; flex-wrap: wrap; gap: var(--s-4); padding: var(--s-4);
  border-radius: calc(var(--radius-md) + 4px); background: var(--bg-well);
}
.segmented button {
  min-height: calc(var(--tap) - 0.5rem); padding: 0 var(--s-16); border: 0; border-radius: var(--radius-md);
  background: transparent; font-weight: 600; color: var(--text-2);
  transition: background-color var(--dur-quick) ease, color var(--dur-quick) ease;
}
.segmented button[aria-pressed='true'] { background: var(--surface); color: var(--text); box-shadow: var(--e1); }


/* --------------------------------------------------------------------------
   11. Page header
   -------------------------------------------------------------------------- */
.page-header { display: flex; align-items: center; gap: 0.875rem; margin-bottom: 1.125rem; }
.page-icon {
  display: grid; place-items: center; flex: 0 0 auto; width: 3.5rem; height: 3.5rem; border-radius: var(--radius-md);
  font-size: 1.9rem; background: color-mix(in srgb, var(--accent) 14%, var(--surface));
}
.page-titles { flex: 1; min-width: 0; }
.page-titles h1 { overflow-wrap: anywhere; }
.page-sub { color: var(--text-3); margin-top: 3px; }
.page-actions { display: flex; gap: var(--s-8); flex-wrap: wrap; justify-content: flex-end; }
@media (max-width: 720px) {
  .page-header { flex-wrap: wrap; }
  .page-actions { width: 100%; justify-content: flex-start; }
  h1 { font-size: 1.5rem; }
}


/* --------------------------------------------------------------------------
   12. Dialogs and toasts (MOTION-AND-POLISH §1: 250 ms, --ease-out;
       on phones the dialog is a bottom sheet)
   -------------------------------------------------------------------------- */
.modal-backdrop {
  position: fixed; inset: 0; z-index: 1000; display: grid; place-items: center; padding: var(--s-20);
  background: var(--scrim); animation: z-fade-in var(--dur-base) var(--ease-out);
}
.modal {
  display: flex; flex-direction: column; width: min(35rem, 100%); max-height: min(88vh, 56.25rem);
  background: var(--th-dialog); color: var(--text); border-radius: var(--radius-lg); box-shadow: var(--e3); overflow: hidden;
  animation: z-modal-in var(--dur-base) var(--ease-out);
}
/* Secondary text in a dialog: on dark, Ink-300 on a fill-secondary control over
   the dialog surface is 4.36:1, so dialogs read --text-3-dialog (4.7:1; the
   same Ink-500 on light). --muted and --faint are re-pointed too, because
   they resolve at the root. */
.modal { --text-3: var(--text-3-dialog); --muted: var(--text-3-dialog); --faint: var(--text-3-dialog); }
.modal.sm { width: min(26.25rem, 100%); }
.modal.lg { width: min(53.75rem, 100%); }
.modal.full { width: min(75rem, 100%); height: min(92vh, 62.5rem); max-height: none; }
.modal-head { display: flex; align-items: center; gap: 0.625rem; padding: 1.125rem var(--s-20) var(--s-12); }
.modal-head h2 { flex: 1; font-size: 1.375rem; font-weight: 600; }
.modal-body { flex: 1; overflow: auto; padding: var(--s-4) var(--s-20) var(--s-20); }
.modal-foot { display: flex; flex-wrap: wrap; justify-content: flex-end; gap: 0.625rem; padding: 0.875rem var(--s-20); border-top: 1px solid var(--line); }
@media (max-width: 720px) {
  .modal-backdrop { padding: 0; align-items: end; }
  .modal, .modal.sm, .modal.lg, .modal.full {
    width: 100%; max-height: 92vh; border-radius: var(--radius-lg) var(--radius-lg) 0 0;
    animation: z-sheet-in var(--dur-base) var(--ease-drawer);
  }
}
@keyframes z-fade-in { from { opacity: 0; } }
@keyframes z-modal-in { from { opacity: 0; transform: scale(0.95); } }
@keyframes z-sheet-in { from { transform: translateY(100%); } }

/* Toasts: Ink (light) or surface-3 (dark) with Cream text; admin bottom-right
   356 px, kitchen bottom-centre 480 px with 20 px text. Status toasts use
   their fill. */
.toast-host {
  position: fixed; z-index: 1100; bottom: var(--s-24); left: 50%; transform: translateX(-50%);
  display: flex; flex-direction: column; align-items: center; gap: 0.875rem; width: min(35rem, calc(100% - 1.5rem)); pointer-events: none;
}
body.admin .toast-host { left: auto; right: var(--s-24); transform: none; align-items: flex-end; width: min(22.25rem, calc(100% - 2rem)); }
body.tablet .toast-host { width: min(30rem, calc(100% - 1.5rem)); }
.toast {
  pointer-events: auto; display: flex; align-items: center; gap: var(--s-12); max-width: 100%;
  padding: 0.875rem 1.125rem; border-radius: var(--radius-md); background: var(--th-toast); color: var(--z-cream); box-shadow: var(--e3);
  font-weight: 600; animation: z-toast-in var(--dur-base) var(--ease-out);
}
body.tablet .toast { font-size: 1.25rem; line-height: 1.75rem; }
.toast-ok { background: var(--ready); color: var(--on-ready); }
.toast-danger { background: var(--late); color: var(--on-late); }
.toast-warn { background: var(--new); color: var(--on-new); }
@keyframes z-toast-in { from { opacity: 0; transform: translateY(100%); } }
/* Reduce Motion: dialogs, bottom sheets and toasts fade in place (no scale, no rise). */
@media (prefers-reduced-motion: reduce) {
  .modal, .modal.sm, .modal.lg, .modal.full, .toast { animation-name: z-fade-in; }
}


/* --------------------------------------------------------------------------
   13. Empty states, spinner, stat tiles
   -------------------------------------------------------------------------- */
.empty {
  display: flex; flex-direction: column; align-items: center; gap: 0.625rem; max-width: 26.25rem; margin: 0 auto;
  padding: var(--s-48) var(--s-20); text-align: center; color: var(--text-3);
}
.empty-icon { font-size: 3rem; line-height: 1; }
.empty-title { font: var(--t-title-3); color: var(--text); }
.spinner {
  display: inline-block; width: 1.75rem; height: 1.75rem; border-radius: 50%;
  border: 3.5px solid var(--fill-secondary); border-top-color: var(--text); animation: spin 0.8s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }
/* Reduce Motion: the ring breathes in place instead of turning. */
@media (prefers-reduced-motion: reduce) { .spinner { animation: z-breathe 1.2s ease-in-out infinite alternate; } }
@keyframes z-breathe { from { opacity: 1; } to { opacity: 0.4; } }
.boot-splash {
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: var(--s-16);
  min-height: 100vh; min-height: 100dvh; color: var(--text-3); font-weight: 600;
}

/* KPI tile: label 14/500, figure in JetBrains Mono 32/700 */
.stat {
  display: flex; flex-direction: column; gap: var(--s-4); min-width: 0; padding: var(--s-16) 1.125rem;
  border: 1px solid var(--line); border-radius: var(--radius-md); background: var(--surface); box-shadow: var(--e1);
}
.stat-label { font: var(--t-label); color: var(--text-2); }
.stat-value { font: var(--t-num-kpi); font-variant-numeric: tabular-nums; font-variant-ligatures: none; font-feature-settings: "tnum" 1, "calt" 0; }
.stat-sub { font: var(--t-footnote); color: var(--text-3); }
.stat.ok .stat-value { color: var(--ready-text); }
.stat.warn .stat-value { color: var(--new-text); }
.stat.danger .stat-value { color: var(--late-text); }
.stat.click { cursor: pointer; transition: box-shadow var(--dur-quick) ease; }
@media (hover: hover) and (pointer: fine) {
  .stat.click:hover { box-shadow: var(--e2); }
}


/* --------------------------------------------------------------------------
   14. Tables (admin): hairlines, sticky 13/600 header, Cream-200 hover
   -------------------------------------------------------------------------- */
.table-wrap { overflow: auto; border: 1px solid var(--line); border-radius: var(--radius-md); background: var(--surface); }
.table { width: 100%; border-collapse: separate; border-spacing: 0; font-size: 0.9375rem; }
.table thead th {
  position: sticky; top: 0; z-index: 1; padding: 0.625rem var(--s-12); white-space: nowrap;
  background: var(--surface); border-bottom: 1px solid var(--line); text-align: left;
  font-size: 0.8125rem; line-height: 1.125rem; font-weight: 600; color: var(--text-3);
}
.table td { padding: 0.6875rem var(--s-12); border-bottom: 1px solid var(--line); vertical-align: top; }
.table tr:last-child td { border-bottom: 0; }
.table tr.click { cursor: pointer; }
.table tr.click:hover td { background: var(--fill-hover); }


/* --------------------------------------------------------------------------
   15. Sortable list, colour and emoji fields, name picker
   -------------------------------------------------------------------------- */
.sortable { display: flex; flex-direction: column; gap: var(--s-8); }
.sortable-item {
  display: flex; align-items: center; gap: var(--s-8); padding: 0.375rem var(--s-8);
  border: 1px solid var(--line); border-radius: var(--radius-md); background: var(--surface);
}
.sortable-item.dragging { opacity: 0.5; }
.sortable-item.drop-target { border-color: var(--text); box-shadow: 0 0 0 3px var(--new-tint); }
.drag-handle { cursor: grab; color: var(--text-3); font-size: 1.1rem; padding: var(--s-4); user-select: none; }
.sortable-moves { display: flex; flex-direction: column; }
.sortable-moves button { border: 0; background: transparent; color: var(--text-3); font-size: 0.75rem; line-height: 1; padding: 2px 6px; }
.sortable-moves button:disabled { opacity: 0.3; }
.sortable-content { flex: 1; min-width: 0; }
/* Touch screens can't drag the handle, so the arrows are the only way to reorder: real targets. */
@media (pointer: coarse) {
  .sortable-moves { gap: var(--s-4); }
  .sortable-moves button { min-width: 2.5rem; min-height: 2.5rem; font-size: 1rem; border-radius: var(--radius-sm); background: var(--fill-secondary); }
}

.color-field { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-8); }
.color-field input[type='color'] {
  width: 3rem; height: 2.75rem; padding: 2px;
  border: 1.5px solid var(--line-strong); border-radius: var(--radius-sm); background: var(--surface);
}
.color-field .input { width: 7.5rem; font-family: var(--font-mono); }
.swatch { width: 1.75rem; height: 1.75rem; border-radius: var(--radius-sm); border: 2px solid var(--surface); box-shadow: 0 0 0 1px var(--line-strong); }
.swatch[aria-pressed='true'] { box-shadow: 0 0 0 2px var(--text); }
.emoji-field { display: flex; flex-direction: column; gap: var(--s-8); }
.emoji-palette { display: flex; flex-wrap: wrap; gap: var(--s-4); }
.emoji-palette button {
  width: 2.375rem; height: 2.375rem; font-size: 1.25rem;
  border: 1px solid var(--line); border-radius: var(--radius-sm); background: var(--surface);
}
.emoji-palette button[aria-pressed='true'] { border-color: var(--text); box-shadow: 0 0 0 2px var(--new-tint); }

.name-grid { display: grid; gap: 0.625rem; grid-template-columns: repeat(auto-fill, minmax(8.75rem, 1fr)); }
.name-btn {
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 2px; min-height: 4rem; padding: var(--s-8);
  border: 1.5px solid transparent; border-radius: var(--radius-md); background: var(--fill-secondary); color: var(--text);
  font-weight: 600; font-size: 1.0625rem; transition: transform var(--dur-press) var(--ease-out);
}
.name-btn small { font-weight: 500; color: var(--text-3); font-size: 0.8125rem; }
.name-btn:active { transform: scale(var(--press-scale)); transition-duration: var(--dur-instant); }


/* --------------------------------------------------------------------------
   15b. Target sizes (RESEARCH §3.6, WCAG 2.5.8)
        Never under 24 px anywhere. On touch screens the admin and the
        control panel (body.admin) reach 44 px; the mouse keeps the denser
        36–40 px. The kitchen (body.tablet) has its own larger sizes and is
        not touched here. :where() keeps these rules weaker than a screen's
        own sizing (e.g. a whole .adm-switch row as the target).
   -------------------------------------------------------------------------- */
.chip button { display: inline-grid; place-items: center; min-width: 1.5rem; min-height: 1.5rem; margin-block: -0.25rem; }
.sortable-moves button { min-width: 1.5rem; min-height: 1.5rem; }

@media (pointer: coarse) {
  body.admin :where(.btn, .chip-select, .toggle, .segmented button, .sortable-moves button) { min-height: var(--a-target); }
  body.admin .btn-sm,
  body.admin .input.sm,
  body.admin .select.sm { min-height: var(--a-target); }
  body.admin .icon-btn.sm { width: var(--a-target); height: var(--a-target); }
  body.admin :where(.chip button, .sortable-moves button) { min-width: var(--a-target); min-height: var(--a-target); }
  body.admin :where(.swatch, .emoji-palette button) { width: var(--a-target); height: var(--a-target); }
}


/* --------------------------------------------------------------------------
   16. Print and reduced motion
   -------------------------------------------------------------------------- */
#print-root { display: none; }
@media print {
  body > :not(#print-root) { display: none !important; }
  #print-root { display: block !important; }
  body { background: #fff; }
}

/* The tokens already shorten motion to 150 ms fades with Reduce Motion, and
   every animation now has its own reduced-motion fade (this file's dialog,
   sheet, toast and spinner; tablet.css .k-spin and the code-box caret; the
   control.css spinner; the others in their own files), so the guard no longer
   touches animations (2 Nov 2026). Transitions still need it: some move
   without a reduced-motion rule of their own (the control panel's menu
   drawer, the toggle knob, the chevrons, the skip link, the training bar
   and card flip, setup.css), so they still jump to their end state here. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { transition-duration: 0.01ms !important; scroll-behavior: auto !important; }
}
