/* Judgy — owner console.

   ONE stylesheet for the whole console, and also for /reset (reset.html loads
   it) and for the fallback cards boot-check.js hand-builds. The guest menu at
   /order/ and the marketing site have their own sheets and their own tokens;
   nothing here reaches them. */

/* The console declared `font-family: Inter` for years without ever bundling
   Inter, so it rendered in whatever the OS shipped — Segoe UI on Windows, SF
   on a Mac, Roboto on Android. The face is registered here under a PRIVATE
   alias: assets/fonts/menu-fonts.css registers these same files under
   "Menu …" names ONLY, deliberately, because registering the real family name
   would silently restyle the guest order page (which declares Inter for the
   same historical reason). Keep the alias.

   The axis really is 400–700 (see menu-fonts.css) — asking for more than 700
   gets a clamp or a synthetic bold, so nothing below should exceed it. */
@font-face {
  font-family: "Judgy Sans";
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url("/assets/fonts/inter-latin-v1.woff2") format("woff2");
}

:root {
  color-scheme: light;

  /* Surfaces. The ground is a warm off-white so a card can be pure white and
     read as raised without leaning on a shadow — structure over shadow. */
  --bg: #ffffff;
  --ground: #faf7f2;
  --paper: #fdfbf9;
  --sunk: #f4efe8;

  --ink: #14100c;
  --graphite: #2a2520;
  --muted: #6a6058;
  --line: #e8e1d9;
  --line-strong: #d9cfc4;

  /* Orange. #e05a00 is the pinned brand colour. It measures 3.73:1 on white,
     which clears WCAG 1.4.11 (3:1) for borders, rails, dots and stroke icons
     but NOT the 4.5:1 a small orange WORD needs. So the axis is split and each
     token has exactly one job. The rule that matters:
        --orange        never goes behind text, and always paints icons
        --orange-solid  is the only orange allowed UNDER white text
        --orange-strong is the only orange allowed AS text
     Getting this backwards turns every stroke icon in the console brown. */
  --orange: #e05a00;         /* 3.73:1 — fills, borders, rails, dots, ICONS */
  --orange-solid: #c94e00;   /* 4.59:1 — the fill under white text */
  --orange-strong: #b34a00;  /* 5.39:1 — orange AS text; also :active */
  --orange-soft: #fdf2eb;    /* tint ground */
  --orange-tint: #fbe8db;    /* deeper tint, for pills carrying small text */
  --orange-wash: #fdf7f2;    /* the faintest wash */
  --orange-line: #f6d1b8;    /* opaque border tint */
  --orange-rgb: 224, 90, 0;  /* single source for every rgba() in this file */

  --danger: #c4271b;
  --ok: #0c5132;
  --ok-soft: #e8f6ee;
  --ok-line: #b6e2c8;
  /* 7.63:1 — two clear steps darker than --orange-strong, so a warning still
     reads as a warning sitting next to brand orange. Was three near-identical
     ambers (#b54708, #b34a00, #b45309) that would now be indistinguishable. */
  --warn: #8f3900;

  /* Three shadows, not one under everything. */
  --shadow-sm: 0 1px 2px rgba(20, 16, 12, 0.06);
  --shadow: 0 2px 8px rgba(20, 16, 12, 0.06), 0 12px 32px rgba(20, 16, 12, 0.07);
  --shadow-lift: 0 4px 14px rgba(20, 16, 12, 0.09), 0 22px 52px rgba(20, 16, 12, 0.1);

  /* Three radii, so a control, a card and an illustration are not the same
     shape at three sizes. */
  --radius-sm: 6px;
  --radius: 10px;
  --radius-lg: 16px;

  --t-micro: 0.75rem;
  --t-small: 0.8125rem;
  --t-body: 0.9375rem;
  --t-lead: 1.0625rem;
  --t-h3: 1.125rem;
  --t-h2: 1.375rem;
  --t-h1: 2rem;

  --max: 1080px;
  --header-h: 72px;
  --sidebar-w: 240px;
  --sidebar-w-collapsed: 64px;
  font-family: "Judgy Sans", ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
}

* {
  box-sizing: border-box;
}

body {
  display: flex;
  flex-direction: column;
  margin: 0;
  min-height: 100svh;
  /* Squared paper, barely there. The warm --ground does the job the two
     diagonal gradient washes used to fake (one of which was a teal that
     belonged to no palette in this product). */
  background:
    linear-gradient(90deg, rgba(var(--orange-rgb), 0.035) 1px, transparent 1px),
    linear-gradient(180deg, rgba(var(--orange-rgb), 0.03) 1px, transparent 1px),
    var(--ground);
  background-size: 80px 80px;
  color: var(--ink);
  font-size: 16px;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

main {
  flex-grow: 1;
}

a {
  color: inherit;
  text-decoration: none;
}

h1,
h2,
h3,
p {
  margin-top: 0;
}

button {
  font-family: inherit;
}

.hidden {
  display: none !important;
}

/* Scroll lock while the mobile drawer is open (set on <html> and <body>). */
.no-scroll {
  overflow: hidden;
}

.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;
}

/* ---------- Header ---------- */

.app-header {
  position: sticky;
  top: 0;
  z-index: 70;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px 20px;
  min-height: var(--header-h);
  padding: 12px clamp(16px, 4vw, 48px);
  background: rgba(255, 255, 255, 0.86);
  border-bottom: 1px solid rgba(236, 232, 225, 0.9);
  backdrop-filter: blur(18px);
}

.brand {
  display: inline-flex;
  align-items: center;
}

.brand-logo {
  display: block;
  width: 104px;
  height: auto;
  overflow: visible;
}

.brand-logo .logo-orange {
  fill: var(--orange);
}

.brand-logo .logo-black {
  fill: var(--ink);
}

.header-user {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-left: auto;
}

/* Hamburger in the topbar: only shown on the mobile console (has-sidebar,
   ≤1024px) — .header-user is hidden there, so both can own margin-left:auto. */
.nav-toggle {
  display: none;
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 40px;
  margin-left: auto;
  padding: 0;
  color: var(--graphite);
  background: transparent;
  border: 1px solid transparent;
  border-radius: var(--radius);
  cursor: pointer;
  transition: background 180ms ease, border-color 180ms ease;
}

.nav-toggle:hover,
.nav-toggle:focus-visible {
  background: var(--orange-soft);
  border-color: rgba(var(--orange-rgb), 0.35);
  outline: none;
}

.nav-toggle svg {
  width: 24px;
  height: 24px;
}

/* ---------- App chrome: sidebar (desktop rail / mobile drawer) ----------
   The sidebar is a sibling of #view: every view rewrites #view.innerHTML, so
   persistent chrome cannot live inside it. Visibility is driven by
   body.has-sidebar, which app-shell.js mirrors from #app-nav's .hidden class
   (the router toggles it — main.js render()). */

.app-sidebar {
  display: none;
  flex-direction: column;
  background: rgba(255, 255, 255, 0.94);
  border-right: 1px solid var(--line);
  backdrop-filter: blur(18px);
}

.sidebar-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  padding: 20px 16px 14px 22px;
}

.sidebar-brand .brand-logo {
  width: 96px;
}

.brand-mark {
  display: none;
  width: 17px;
  height: auto;
  overflow: visible;
}

.brand-mark .logo-orange {
  fill: var(--orange);
}

.sidebar-collapse {
  display: flex;
  align-items: center;
  justify-content: center;
  flex: none;
  width: 30px;
  height: 30px;
  padding: 0;
  color: var(--muted);
  background: transparent;
  border: 1px solid var(--line);
  border-radius: 6px;
  cursor: pointer;
  transition: color 180ms ease, background 180ms ease, border-color 180ms ease;
}

.sidebar-collapse:hover,
.sidebar-collapse:focus-visible {
  /* An icon, not a word: --orange (3.73:1) clears WCAG 1.4.11 for a glyph. */
  color: var(--orange);
  background: var(--orange-soft);
  border-color: rgba(var(--orange-rgb), 0.35);
  outline: none;
}

.sidebar-collapse svg {
  width: 16px;
  height: 16px;
  transition: transform 260ms cubic-bezier(0.16, 1, 0.3, 1);
}

.app-sidebar.is-collapsed .sidebar-collapse svg {
  transform: rotate(180deg);
}

.app-nav {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: 10px 12px;
  overflow-y: auto;
}

.app-nav a {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 10px 12px;
  border-radius: var(--radius);
  color: var(--muted);
  font-size: 0.92rem;
  font-weight: 620;
  white-space: nowrap;
  position: relative;
  transition: color 180ms ease, background 180ms ease;
}

.app-nav a svg {
  width: 20px;
  height: 20px;
  flex: none;
}

.app-nav a:hover,
.app-nav a:focus-visible {
  color: var(--ink);
  background: var(--orange-soft);
  outline: none;
}

.app-nav a.is-active {
  color: var(--orange-strong);
  background: var(--orange-soft);
}

.app-nav a.is-active::before {
  content: "";
  position: absolute;
  left: -12px;
  top: 8px;
  bottom: 8px;
  width: 3px;
  border-radius: 2px;
  background: var(--orange);
}

.sidebar-foot {
  margin-top: auto;
  padding: 12px;
  border-top: 1px solid var(--line);
}

.sidebar-logout {
  display: flex;
  align-items: center;
  gap: 12px;
  width: 100%;
  padding: 10px 12px;
  border: none;
  border-radius: var(--radius);
  color: var(--muted);
  background: transparent;
  font-size: 0.92rem;
  font-weight: 620;
  white-space: nowrap;
  text-align: left;
  cursor: pointer;
  transition: color 180ms ease, background 180ms ease;
}

.sidebar-logout:hover,
.sidebar-logout:focus-visible {
  color: var(--ink);
  background: var(--orange-soft);
  outline: none;
}

.sidebar-logout svg {
  width: 20px;
  height: 20px;
  flex: none;
}

/* Drawer scrim: same enter/exit idiom as .qr-modal (opacity + delayed
   visibility). z-index 140 puts it above the install FAB (100) and both
   modals (120/130), so the drawer is always the topmost chrome. */
.app-scrim {
  position: fixed;
  inset: 0;
  z-index: 140;
  background: rgba(23, 23, 23, 0.32);
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transition: opacity 260ms cubic-bezier(0.16, 1, 0.3, 1), visibility 0s linear 260ms;
}

.app-scrim.is-open {
  opacity: 1;
  visibility: visible;
  pointer-events: auto;
  transition: opacity 260ms cubic-bezier(0.16, 1, 0.3, 1);
}

/* On the console the logout button lives in the sidebar/drawer; the topbar
   copy (#header-user) stays for the paywall and loading states, where the
   sidebar is hidden. */
body.has-sidebar .app-header .header-user {
  display: none;
}

/* Desktop console: fixed left rail, no topbar (the logo moves into the rail).
   --header-h: 0 keeps the sticky offsets and .auth-shell math honest. */
@media (min-width: 1025px) {
  body.has-sidebar {
    --header-h: 0px;
  }

  body.has-sidebar .app-header {
    display: none;
  }

  body.has-sidebar .app-sidebar {
    display: flex;
    position: fixed;
    top: 0;
    bottom: 0;
    left: 0;
    width: var(--sidebar-w);
    z-index: 60;
    transition: width 260ms cubic-bezier(0.16, 1, 0.3, 1);
  }

  body.has-sidebar main#view {
    margin-left: var(--sidebar-w);
    transition: margin-left 260ms cubic-bezier(0.16, 1, 0.3, 1);
  }

  body.sidebar-collapsed .app-sidebar {
    width: var(--sidebar-w-collapsed);
  }

  body.sidebar-collapsed main#view {
    margin-left: var(--sidebar-w-collapsed);
  }

  /* Collapsed rail: icons only. Labels shrink away but stay in the DOM so
     they can double as hover/focus tooltips. */
  .app-sidebar.is-collapsed .sidebar-head {
    flex-direction: column;
    align-items: center;
    padding: 16px 8px 10px;
  }

  .app-sidebar.is-collapsed .sidebar-logo-full {
    display: none;
  }

  .app-sidebar.is-collapsed .brand-mark {
    display: block;
  }

  .app-sidebar.is-collapsed .app-nav,
  .app-sidebar.is-collapsed .sidebar-foot {
    padding-left: 8px;
    padding-right: 8px;
  }

  /* The icons-only rail never needs to scroll, and any overflow value other
     than visible would clip the floating tooltips. */
  .app-sidebar.is-collapsed .app-nav {
    overflow: visible;
  }

  .app-sidebar.is-collapsed .app-nav a,
  .app-sidebar.is-collapsed .sidebar-logout {
    justify-content: center;
    gap: 0;
    padding: 10px;
  }

  .app-sidebar.is-collapsed .app-nav a span,
  .app-sidebar.is-collapsed .sidebar-logout span {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
  }

  .app-sidebar.is-collapsed .app-nav a.is-active::before {
    left: -8px;
  }

  /* CSS-only tooltip: un-clip the label and float it beside the rail. */
  .app-sidebar.is-collapsed .app-nav a:hover span,
  .app-sidebar.is-collapsed .app-nav a:focus-visible span,
  .app-sidebar.is-collapsed .sidebar-logout:hover span,
  .app-sidebar.is-collapsed .sidebar-logout:focus-visible span {
    width: auto;
    height: auto;
    margin: 0;
    clip: auto;
    overflow: visible;
    left: calc(100% + 12px);
    top: 50%;
    transform: translateY(-50%);
    background: var(--ink);
    color: #ffffff;
    padding: 6px 10px;
    border-radius: 6px;
    font-size: 0.8rem;
    box-shadow: var(--shadow);
    pointer-events: none;
    z-index: 5;
  }

}

/* Mobile console: slim topbar (logo + hamburger) and a slide-in drawer. */
@media (max-width: 1024px) {
  body.has-sidebar .nav-toggle {
    display: inline-flex;
  }

  body.has-sidebar .app-sidebar {
    display: flex;
    position: fixed;
    top: 0;
    bottom: 0;
    left: 0;
    width: min(84vw, 320px);
    z-index: 145;
    box-shadow: var(--shadow);
    transform: translateX(-103%);
    visibility: hidden;
    transition: transform 300ms cubic-bezier(0.16, 1, 0.3, 1), visibility 0s linear 300ms;
  }

  body.has-sidebar .app-sidebar.is-open {
    transform: translateX(0);
    visibility: visible;
    transition: transform 300ms cubic-bezier(0.16, 1, 0.3, 1);
  }

  /* Collapse is a desktop-only affordance. */
  .sidebar-collapse {
    display: none;
  }

  /* Compact type on small screens — deliberately no font inflation. */
  .app-nav a {
    font-size: 0.95rem;
  }
}

/* View entrance: app-shell.js adds .view-anim on hashchange only, so
   intra-view re-renders (billing snapshots, live orders) never replay it. */
#view.view-anim > * {
  animation: viewIn 240ms cubic-bezier(0.16, 1, 0.3, 1) both;
}

@keyframes viewIn {
  from {
    opacity: 0;
    transform: translateY(8px);
  }
}

/* ---------- Buttons ---------- */

.button {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  min-height: 42px;
  padding: 0 16px;
  border: 1px solid transparent;
  border-radius: var(--radius);
  font-size: 0.92rem;
  font-weight: 650;
  line-height: 1;
  white-space: nowrap;
  cursor: pointer;
  transition: transform 180ms ease, border-color 180ms ease, box-shadow 180ms ease, background 180ms ease;
}

.button:hover,
.button:focus-visible {
  transform: translateY(-1px);
  outline: none;
}

.button:disabled {
  opacity: 0.55;
  cursor: default;
  transform: none;
}

.button-ghost {
  color: var(--graphite);
  background: #ffffff;
  border-color: var(--line);
}

.button-ghost:hover,
.button-ghost:focus-visible {
  border-color: rgba(var(--orange-rgb), 0.5);
  box-shadow: 0 8px 24px rgba(var(--orange-rgb), 0.12);
}

/* The one orange allowed under white text: --orange-solid is 4.59:1, so the
   label clears AA. The old orange glow under an orange button read as a brown
   haze once the accent darkened, so the lift is neutral now. */
.button-solid {
  color: #ffffff;
  background: var(--orange-solid);
  box-shadow: 0 6px 18px rgba(43, 24, 10, 0.16), inset 0 1px 0 rgba(255, 255, 255, 0.22);
}

.button-soft {
  color: var(--orange-strong);
  background: var(--orange-wash);
  border-color: rgba(var(--orange-rgb), 0.22);
}

.button-danger {
  color: #ffffff;
  background: var(--danger);
  box-shadow: 0 10px 30px rgba(217, 45, 32, 0.24);
}

.button-danger-ghost {
  color: var(--danger);
  background: #ffffff;
  border-color: rgba(217, 45, 32, 0.35);
}

.button-large {
  min-height: 50px;
  padding: 0 20px;
  font-size: 0.98rem;
}

.button-small {
  min-height: 34px;
  padding: 0 12px;
  font-size: 0.84rem;
}

.button-block {
  width: 100%;
}

/* Inline stroke icons inside buttons (MENU_ICONS in main.js): sized here,
   colored by the button — plus a brand-orange pop inside ghost buttons,
   whose text stays graphite. */
.btn-ico {
  width: 18px;
  height: 18px;
  flex: none;
}

/* Every inline glyph inside a ghost button, the orders bell included. Icons
   are graphical objects (3:1), so they take the brand orange itself; pointing
   this at --orange-strong would age every icon in the console to brown. */
.button-ghost .btn-ico {
  color: var(--orange);
}

.button-small .btn-ico {
  width: 15px;
  height: 15px;
}

.icon-button .btn-ico {
  width: 16px;
  height: 16px;
}

/* ---------- Layout ---------- */

.shell {
  width: min(calc(100% - 32px), var(--max));
  margin: 0 auto;
  padding: clamp(28px, 5vw, 56px) 0 clamp(48px, 7vw, 88px);
}

.eyebrow {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  margin: 0 0 12px;
  color: var(--orange-strong);
  font-size: var(--t-small);
  font-weight: 650;
  letter-spacing: 0.005em;
}

.eyebrow::before {
  width: 20px;
  height: 2px;
  content: "";
  background: var(--orange);
}

.page-title {
  margin-bottom: 12px;
  font-size: clamp(1.75rem, 4.2vw, 2.6rem);
  font-weight: 700;
  line-height: 1.05;
  letter-spacing: -0.023em;
  overflow-wrap: anywhere;
}

/* Under 68 characters a line, which is where prose stops needing effort. */
.page-lede {
  max-width: 62ch;
  margin-bottom: 28px;
  color: var(--muted);
  font-size: var(--t-lead);
  line-height: 1.55;
}

/* Anywhere a figure is read, compared, or watched changing. Proportional
   digits make a price column jitter on every keystroke and an elapsed-time
   badge twitch every minute; tabular ones hold their place. */
.stat-value,
.receipt-total,
.receipt td,
.drill-count,
.order-total,
.order-qty,
.order-time,
.order-elapsed,
.order-group-count,
.menu-dish-price,
.menu-discount-input,
.menu-tables-input,
.qr-table-num,
.qr-table-of,
.hours-time,
.hours-colon,
.boot-pct {
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum" 1;
}

/* ---------- Cards ---------- */

.card {
  position: relative;
  padding: 24px;
  overflow: hidden;
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: var(--shadow-sm);
}

.card-accent::after {
  position: absolute;
  top: 0;
  left: 18px;
  width: 56px;
  height: 3px;
  content: "";
  background: linear-gradient(90deg, var(--orange), transparent);
}

/* ---------- Auth screens ---------- */

.auth-shell {
  display: grid;
  place-items: center;
  min-height: calc(100svh - var(--header-h));
  padding: clamp(24px, 5vw, 56px) 16px;
}

.auth-card {
  width: min(100%, 420px);
  padding: clamp(24px, 5vw, 36px);
}

.auth-card h1 {
  margin-bottom: 6px;
  font-size: 1.7rem;
  font-weight: 700;
  letter-spacing: -0.01em;
}

.auth-card .auth-sub {
  margin-bottom: 24px;
  color: var(--muted);
  font-size: 0.95rem;
}

.auth-links {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: 8px;
  margin-top: 18px;
  font-size: 0.92rem;
}

.auth-links a {
  color: var(--orange-strong);
  font-weight: 700;
}

.auth-links a:hover {
  text-decoration: underline;
}

.terms-check {
  display: flex;
  gap: 10px;
  align-items: flex-start;
  margin: 2px 0 4px;
  color: var(--muted);
  font-size: 0.92rem;
  line-height: 1.45;
  cursor: pointer;
}

.terms-check input {
  flex: none;
  width: 17px;
  height: 17px;
  margin-top: 2px;
  accent-color: var(--orange);
  cursor: pointer;
}

.terms-check a {
  color: var(--orange-strong);
  font-weight: 700;
}

.terms-check a:hover {
  text-decoration: underline;
}

.auth-ok {
  padding: 12px 14px;
  margin: 0 0 14px;
  color: var(--ok);
  background: var(--ok-soft);
  border: 1px solid var(--ok-line);
  border-radius: var(--radius);
  font-size: 0.92rem;
}

/* Standing reminder above every console screen: the agreed first instalment is
   coming and Stripe has no payment method to charge. Amber rather than red —
   nothing is broken yet, and it is the customer's own obligation under §5 of
   the Terms, not a failure. It removes itself the moment a card is on file. */
.console-banner {
  margin: 0 0 1.25rem;
  padding: 0.85rem 1.1rem;
  border: 1px solid rgba(181, 71, 8, 0.28);
  border-left: 4px solid var(--warn);
  border-radius: 10px;
  background: var(--orange-soft);
  color: var(--warn);
  font-size: 0.95rem;
  line-height: 1.5;
}

.console-banner a {
  color: inherit;
  font-weight: 600;
  text-decoration: underline;
}

/* ---------- Fields ---------- */

.field {
  display: block;
  margin-bottom: 14px;
}

.field-label {
  display: block;
  margin-bottom: 6px;
  color: var(--graphite);
  font-size: 0.82rem;
  font-weight: 650;
  text-transform: uppercase;
  letter-spacing: 0.05em;
}

.field input,
.field textarea {
  width: 100%;
  padding: 12px 14px;
  color: var(--ink);
  background: #ffffff;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  font-family: inherit;
  font-size: 0.98rem;
  transition: border-color 160ms ease, box-shadow 160ms ease;
}

.field textarea {
  min-height: 64px;
  resize: vertical;
}

.field input:focus,
.field textarea:focus {
  border-color: rgba(var(--orange-rgb), 0.55);
  box-shadow: 0 0 0 3px rgba(var(--orange-rgb), 0.2);
  outline: none;
}

.form-error {
  min-height: 20px;
  margin: 4px 0 12px;
  color: var(--danger);
  font-size: 0.9rem;
  font-weight: 620;
}

.password-req {
  display: grid;
  gap: 5px;
  margin: -6px 0 14px;
  padding: 0;
  list-style: none;
}

.password-req li {
  display: flex;
  align-items: center;
  gap: 8px;
  color: var(--muted);
  font-size: 0.82rem;
  transition: color 160ms ease;
}

.password-req-mark {
  flex: 0 0 auto;
  width: 14px;
  height: 14px;
  border: 1.5px solid var(--line);
  border-radius: 50%;
  transition: border-color 160ms ease, background 160ms ease;
}

.password-req li.is-met {
  color: var(--graphite);
}

.password-req li.is-met .password-req-mark {
  background: var(--orange);
  border-color: var(--orange);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12'%3E%3Cpath d='M2.5 6.2 5 8.7l4.5-5' fill='none' stroke='%23fff' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-size: 10px 10px;
  background-position: center;
  background-repeat: no-repeat;
}

/* ---------- Dashboard stats ---------- */

.stat-track {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 1px;
  margin-bottom: clamp(24px, 4vw, 40px);
  overflow: hidden;
  background: var(--line);
  border: 1px solid var(--line);
  border-radius: var(--radius);
}

.stat {
  position: relative;
  min-height: 112px;
  padding: 20px;
  background: rgba(255, 255, 255, 0.88);
}

.stat::after {
  position: absolute;
  right: 16px;
  bottom: 16px;
  width: 36px;
  height: 2px;
  content: "";
  background: var(--orange);
}

.stat-value {
  display: block;
  margin-bottom: 8px;
  font-size: clamp(1.7rem, 3.5vw, 2.3rem);
  font-weight: 700;
  line-height: 1.02;
  letter-spacing: -0.028em;
}

.stat-label {
  display: block;
  color: var(--muted);
  font-size: 0.9rem;
}

/* ---------- Archive drill-down ---------- */

.crumbs {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  margin-bottom: 16px;
  color: var(--muted);
  font-size: 0.9rem;
  font-weight: 620;
}

.crumbs a {
  color: var(--orange-strong);
  font-weight: 650;
}

.crumbs a:hover {
  text-decoration: underline;
}

.crumbs .crumb-sep {
  color: var(--line);
}

.crumbs .crumb-here {
  color: var(--ink);
}

.drill-list {
  display: grid;
  gap: 10px;
}

.drill-row {
  position: relative;
  display: flex;
  align-items: center;
  gap: 14px;
  width: 100%;
  padding: 16px 18px;
  overflow: hidden;
  color: inherit;
  text-align: left;
  background: rgba(255, 255, 255, 0.88);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: 0 10px 32px rgba(28, 25, 21, 0.05);
  cursor: pointer;
  transition: transform 160ms ease, border-color 160ms ease, box-shadow 160ms ease;
}

.drill-row::before {
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  width: 3px;
  content: "";
  background: linear-gradient(180deg, var(--orange), transparent);
  opacity: 0;
  transition: opacity 160ms ease;
}

.drill-row:hover,
.drill-row:focus-visible {
  transform: translateY(-1px);
  border-color: rgba(var(--orange-rgb), 0.45);
  box-shadow: 0 14px 40px rgba(var(--orange-rgb), 0.12);
  outline: none;
}

.drill-row:hover::before,
.drill-row:focus-visible::before {
  opacity: 1;
}

.drill-main {
  flex: 1;
  min-width: 0;
}

.drill-title {
  display: block;
  font-size: 1.02rem;
  font-weight: 650;
}

.drill-sub {
  display: block;
  margin-top: 2px;
  color: var(--muted);
  font-size: 0.88rem;
}

.drill-count {
  padding: 6px 10px;
  color: var(--orange-strong);
  background: var(--orange-tint);
  border-radius: 999px;
  font-size: 0.82rem;
  font-weight: 700;
  white-space: nowrap;
}

.drill-arrow {
  color: var(--orange);
  font-weight: 700;
}

/* ---------- Dashboard: one day (aggregates + receipts) ---------- */

.day-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
  gap: 16px;
  margin-bottom: 24px;
}

.day-card .section-label {
  margin-bottom: 10px;
}

.day-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.95rem;
}

.day-table th,
.day-table td {
  padding: 8px 6px;
  border-bottom: 1px solid var(--line);
  text-align: left;
  vertical-align: top;
}

.day-table th {
  color: var(--muted);
  font-size: 0.8rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

.day-table tr:last-child td {
  border-bottom: 0;
}

.day-table .num {
  text-align: right;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.receipt-list {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
  gap: 12px;
  align-items: start;
}

.receipt-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  margin-bottom: 10px;
}

.receipt-meta {
  color: var(--muted);
  font-size: 0.85rem;
  white-space: nowrap;
}

.receipt-total {
  margin-top: 10px;
  padding-top: 10px;
  border-top: 1px dashed var(--line);
  font-weight: 700;
  text-align: right;
  font-variant-numeric: tabular-nums;
}

.empty-state {
  padding: clamp(28px, 5vw, 44px);
  text-align: center;
}

.empty-state h3 {
  margin-bottom: 8px;
  font-size: 1.15rem;
  font-weight: 700;
}

.empty-state p {
  max-width: 460px;
  margin: 0 auto 18px;
  color: var(--muted);
  font-size: 0.95rem;
}

/* The kitchen display's live stream has died. This must not be readable as the
   calm "no open orders" card it replaces — that mistake costs a service — so it
   takes the danger colour on both the rule and the heading, and drops the muted
   grey the empty state uses for its body text. Seen from across a kitchen, the
   colour is the message. */
.empty-state.orders-broken {
  border-color: var(--danger);
  box-shadow: 0 10px 30px rgba(196, 39, 27, 0.18);
}

.empty-state.orders-broken strong {
  color: var(--danger);
}

.empty-state.orders-broken p {
  color: var(--ink);
}

.chip {
  padding: 4px 10px;
  color: var(--orange-strong);
  background: var(--orange-tint);
  border-radius: 999px;
  font-size: 0.85rem;
  font-weight: 700;
}

/* ---------- Table QR download card ----------
   Kept from the old owner-facing QR page: My Menu's "Table QR codes" card
   (.menu-qr-card) reuses this exact card language. */

.qr-dl-card {
  display: grid;
  gap: 10px;
  padding: 18px;
  background: #ffffff;
  border: 1px solid var(--line);
  border-radius: var(--radius);
}

.qr-dl-eyebrow {
  margin: 0;
  color: var(--orange-strong);
  font-size: 0.68rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.12em;
}

.qr-dl-title {
  margin: 0;
  font-size: 1.04rem;
  font-weight: 700;
  letter-spacing: -0.01em;
  color: var(--ink);
}

.qr-dl-lede {
  margin: 0;
  color: var(--muted);
  font-size: 0.88rem;
  line-height: 1.5;
}

.qr-dl-card .button {
  margin-top: 2px;
}

/* Reminds owners to print the poster PDF at 100% so sizes stay exact */
.qr-print-note {
  color: var(--muted);
  font-size: 0.78rem;
  line-height: 1.45;
  margin-top: -2px;
}

/* ---------- QR download preview modal ---------- */

/* Full-screen translucent-white overlay. It stays in the DOM (never
   display:none) so it can fade in and out; visibility + pointer-events gate
   interaction while hidden, and the delayed visibility transition keeps the
   card on screen through the fade-out. Only opacity/transform animate, so the
   motion stays on the compositor and reads smooth. */
.qr-modal {
  position: fixed;
  inset: 0;
  z-index: 120;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: clamp(16px, 4vw, 40px);
  background: rgba(255, 255, 255, 0.72);
  backdrop-filter: blur(12px) saturate(1.05);
  -webkit-backdrop-filter: blur(12px) saturate(1.05);
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transition: opacity 260ms cubic-bezier(0.16, 1, 0.3, 1), visibility 0s linear 260ms;
}

.qr-modal.is-open {
  opacity: 1;
  visibility: visible;
  pointer-events: auto;
  transition: opacity 260ms cubic-bezier(0.16, 1, 0.3, 1);
}

.qr-modal-card {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 14px;
  width: min(92vw, 420px);
  padding: 30px clamp(20px, 4vw, 30px) 24px;
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: 20px;
  box-shadow: var(--shadow);
  transform: translateY(16px) scale(0.965);
  opacity: 0;
  transition: transform 340ms cubic-bezier(0.16, 1, 0.3, 1), opacity 240ms ease;
  will-change: transform, opacity;
}

.qr-modal.is-open .qr-modal-card {
  transform: translateY(0) scale(1);
  opacity: 1;
}

.qr-modal-eyebrow {
  margin: 0;
  color: var(--orange-strong);
  font-size: 0.72rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.12em;
}

.qr-modal-title {
  margin: 0;
  font-size: 1.1rem;
  font-weight: 700;
  text-align: center;
  letter-spacing: -0.01em;
}

/* Segmented format chooser (PDF / PNG / SVG / .doc), styled like the print-size
   cards so the whole QR screen reads as one system. */
.qr-modal-formats {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 6px;
  width: 100%;
}

.qr-format {
  padding: 9px 4px;
  font-size: 0.82rem;
  font-weight: 650;
  color: var(--graphite);
  background: #ffffff;
  border: 1px solid var(--line);
  border-radius: 10px;
  cursor: pointer;
  transition: border-color 160ms ease, background 160ms ease, color 160ms ease,
    box-shadow 160ms ease;
}

.qr-format:hover {
  border-color: rgba(var(--orange-rgb), 0.5);
}

.qr-format.is-active {
  color: var(--orange-strong);
  background: var(--orange-tint);
  border-color: var(--orange);
  box-shadow: 0 0 0 1px var(--orange);
}

.qr-format:focus-visible {
  outline: 2px solid var(--orange);
  outline-offset: 2px;
}

/* One-line description of the selected format, under the preview. */
.qr-modal-hint {
  margin: -4px 0 0;
  min-height: 1em;
  color: var(--muted);
  font-size: 0.8rem;
  line-height: 1.4;
  text-align: center;
}

/* White "paper" that shows the code off, centred whatever its aspect ratio. */
.qr-modal-stage {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  padding: 16px;
  background: #ffffff;
  border: 1px solid var(--line);
  border-radius: 14px;
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.6);
}

.qr-modal-img {
  display: block;
  width: auto;
  height: auto;
  max-width: 100%;
  max-height: min(56vh, 440px);
  border-radius: 6px;
}

.qr-modal-dl {
  width: 100%;
}

/* Secondary "every table in one PDF" action under the per-table download. */
.qr-modal-dl-all {
  width: 100%;
  margin-top: -6px;
}

/* Table stepper (menu view only): ‹  Table [3] of 12  › */
.qr-modal-tables {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 12px;
  width: 100%;
}

.qr-table-step {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: none;
  width: 34px;
  height: 34px;
  padding: 0;
  color: var(--graphite);
  background: #ffffff;
  border: 1px solid var(--line);
  border-radius: 999px;
  cursor: pointer;
  transition: color 160ms ease, border-color 160ms ease, background 160ms ease;
}

.qr-table-step:hover:not(:disabled) {
  color: var(--orange);
  border-color: rgba(var(--orange-rgb), 0.5);
  background: var(--orange-soft);
}

.qr-table-step:focus-visible {
  outline: 2px solid var(--orange);
  outline-offset: 2px;
}

.qr-table-step:disabled {
  opacity: 0.35;
  cursor: default;
}

.qr-table-step svg {
  display: block;
  width: 18px;
  height: 18px;
}

.qr-table-num-wrap {
  display: inline-flex;
  align-items: center;
  gap: 8px;
}

.qr-table-word {
  color: var(--ink);
  font-size: 0.92rem;
  font-weight: 650;
}

.qr-table-num {
  width: 64px;
  padding: 8px 6px;
  font: inherit;
  font-weight: 700;
  text-align: center;
  color: var(--ink);
  background: #ffffff;
  border: 1.5px solid var(--line);
  border-radius: 10px;
}

.qr-table-num:focus {
  outline: none;
  border-color: rgba(var(--orange-rgb), 0.55);
  box-shadow: 0 0 0 3px rgba(var(--orange-rgb), 0.2);
}

.qr-table-of {
  color: var(--muted);
  font-size: 0.86rem;
  font-weight: 620;
}

.qr-modal-err {
  margin: -2px 0 0;
  min-height: 1em;
  color: var(--danger);
  font-size: 0.82rem;
  text-align: center;
}

/* Round orange close button in the card's top-right corner. */
.qr-modal-close {
  position: absolute;
  top: 12px;
  right: 12px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 36px;
  height: 36px;
  padding: 0;
  color: #ffffff;
  background: var(--orange);
  border: none;
  border-radius: 999px;
  box-shadow: 0 4px 12px rgba(43, 24, 10, 0.18);
  cursor: pointer;
  transition: transform 160ms ease, box-shadow 160ms ease;
}

.qr-modal-close:hover,
.qr-modal-close:focus-visible {
  transform: scale(1.06);
  outline: none;
  box-shadow: 0 8px 22px rgba(var(--orange-rgb), 0.42);
}

.qr-modal-close:active {
  transform: scale(0.95);
}

.qr-modal-close svg {
  display: block;
  width: 18px;
  height: 18px;
}

/* ---------- Working hours (anti-spam window) ---------- */

/* Set apart from the download buttons above by a divider + extra space, so the
   orange toggle reads as its own feature rather than another download. */
.qr-hours {
  margin-top: 26px;
  padding-top: 22px;
  border-top: 1px solid var(--line);
  /* Land clear of the sticky header when the editor scrolls itself into view. */
  scroll-margin-top: calc(var(--header-h) + 16px);
}

.qr-hours-toggle {
  width: 100%;
}

.qr-hours-ico {
  display: inline-flex;
  align-items: center;
  line-height: 1;
}

.qr-hours-ico svg {
  display: block;
}

.qr-hours-panel {
  margin-top: 16px;
}

.hours-card {
  display: grid;
  gap: 18px;
  animation: hoursReveal 220ms ease-out;
}

@keyframes hoursReveal {
  from { opacity: 0; transform: translateY(-6px); }
}

/* Enable/disable switch */
.hours-switch {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  cursor: pointer;
}

.hours-switch input {
  position: absolute;
  opacity: 0;
  pointer-events: none;
}

.hours-switch-track {
  position: relative;
  flex: none;
  width: 44px;
  height: 26px;
  margin-top: 2px;
  background: var(--line);
  border-radius: 999px;
  transition: background 180ms ease;
}

.hours-switch-thumb {
  position: absolute;
  top: 3px;
  left: 3px;
  width: 20px;
  height: 20px;
  background: #ffffff;
  border-radius: 50%;
  box-shadow: 0 2px 5px rgba(28, 25, 21, 0.28);
  transition: transform 180ms ease;
}

.hours-switch input:checked + .hours-switch-track {
  background: var(--orange);
}

.hours-switch input:checked + .hours-switch-track .hours-switch-thumb {
  transform: translateX(18px);
}

.hours-switch input:focus-visible + .hours-switch-track {
  outline: 2px solid var(--orange);
  outline-offset: 2px;
}

.hours-switch-text strong {
  display: block;
  font-size: 0.96rem;
  font-weight: 650;
  color: var(--ink);
}

.hours-switch-text small {
  display: block;
  margin-top: 2px;
  color: var(--muted);
  font-size: 0.82rem;
  line-height: 1.45;
}

/* Days list — dimmed (but still editable) while the window is off */
.hours-days {
  display: grid;
  gap: 10px;
  transition: opacity 180ms ease;
}

.hours-card.is-off .hours-days {
  opacity: 0.5;
}

.hours-day {
  padding: 14px;
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: var(--radius);
}

.hours-day-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
}

.hours-day-head h3 {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 0;
  font-size: 0.98rem;
  font-weight: 650;
}

/* Brand tick beside the day name — orange while the day has hours, gray when closed */
.hours-day-head h3::before {
  content: "";
  flex: none;
  width: 8px;
  height: 8px;
  background: var(--orange);
  border-radius: 3px;
}

.hours-day.is-closed .hours-day-head h3::before {
  background: var(--line-strong);
}

.hours-closed {
  margin: 8px 0 0;
  color: var(--muted);
  font-size: 0.85rem;
}

.hours-slots {
  display: grid;
  gap: 8px;
  margin-top: 10px;
}

/* A slot card: meal chip + Remove on top, the From → To groups below. Two
   fixed rows instead of one long flex-wrap line, so nothing wraps mid-time. */
.hours-slot {
  display: grid;
  gap: 10px;
  padding: 12px;
  background: #ffffff;
  border: 1px solid var(--line);
  border-radius: var(--radius);
}

.hours-slot-top {
  display: flex;
  align-items: center;
  gap: 10px;
}

/* Meal picker as a brand chip; appearance:none drops the native arrow, so a
   chevron in the brand orange is drawn back in (CSP allows data: images) */
.hours-meal {
  appearance: none;
  -webkit-appearance: none;
  padding: 7px 30px 7px 13px;
  color: var(--orange-strong);
  background:
    var(--orange-soft)
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12'%3E%3Cpath d='M2.5 4.5 6 8l3.5-3.5' fill='none' stroke='%23e05a00' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E")
    no-repeat right 11px center / 12px;
  border: 1px solid rgba(var(--orange-rgb), 0.32);
  border-radius: 999px;
  font-family: inherit;
  font-size: 0.85rem;
  font-weight: 700;
  cursor: pointer;
  transition: border-color 160ms ease, box-shadow 160ms ease;
}

.hours-meal:hover {
  border-color: var(--orange);
}

.hours-meal:focus {
  border-color: var(--orange);
  box-shadow: 0 0 0 3px rgba(var(--orange-rgb), 0.2);
  outline: none;
}

.hours-remove {
  margin-left: auto;
  padding: 5px 10px;
  color: var(--danger);
  background: none;
  border: 1px solid transparent;
  border-radius: 6px;
  font-family: inherit;
  font-size: 0.8rem;
  font-weight: 700;
  cursor: pointer;
  transition: background 160ms ease, border-color 160ms ease;
}

.hours-remove:hover {
  background: rgba(217, 45, 32, 0.07);
  border-color: rgba(217, 45, 32, 0.28);
}

/* From/To sit side by side and wrap as whole units on narrow screens */
.hours-slot-times {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: 10px 18px;
}

.hours-field {
  display: grid;
  gap: 5px;
  justify-items: start;
}

.hours-field-cap {
  color: var(--muted);
  font-size: 0.66rem;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}

.hours-time {
  display: inline-flex;
  align-items: center;
  gap: 5px;
}

.hours-colon {
  color: var(--muted);
  font-weight: 700;
}

/* Hour / minute / AM-PM as small centred boxes: reads as 7 : 30 PM */
.hours-time select {
  appearance: none;
  -webkit-appearance: none;
  min-width: 48px;
  padding: 8px 6px;
  color: var(--ink);
  background: #ffffff;
  border: 1px solid var(--line);
  border-radius: 7px;
  font-family: inherit;
  font-size: 0.92rem;
  font-weight: 650;
  font-variant-numeric: tabular-nums;
  text-align: center;
  text-align-last: center;
  cursor: pointer;
  transition: border-color 160ms ease, box-shadow 160ms ease;
}

.hours-time select:hover {
  border-color: rgba(var(--orange-rgb), 0.5);
}

.hours-time select:focus {
  border-color: rgba(var(--orange-rgb), 0.75);
  box-shadow: 0 0 0 3px rgba(var(--orange-rgb), 0.2);
  outline: none;
}

.hours-time .hours-ampm {
  font-size: 0.8rem;
  letter-spacing: 0.04em;
  color: var(--graphite);
}

/* Chip on a slot whose "to" lands past midnight ("ends Tuesday" / "until
   midnight"), and the read-only continuation line the next day shows for it. */
.hours-next-day {
  /* Sits in the times row: line up with the select boxes, not their captions */
  align-self: flex-end;
  margin-bottom: 6px;
  padding: 3px 9px;
  color: var(--warn);
  background: rgba(var(--orange-rgb), 0.1);
  border: 1px solid rgba(var(--orange-rgb), 0.28);
  border-radius: 999px;
  font-size: 0.78rem;
  font-weight: 700;
  white-space: nowrap;
}

.hours-carry {
  display: flex;
  align-items: baseline;
  gap: 8px;
  margin: 10px 0 0;
  padding: 9px 12px;
  color: var(--muted);
  font-size: 0.85rem;
  background: var(--orange-wash);
  border: 1px dashed rgba(var(--orange-rgb), 0.38);
  border-radius: var(--radius);
}

.hours-carry strong {
  color: var(--ink);
  font-weight: 650;
}

.hours-carry-moon {
  flex: none;
  color: var(--warn);
}

/* ---------- Shared small controls (icon buttons, section labels/hints) ---------- */

.icon-button {
  display: grid;
  place-items: center;
  width: 34px;
  height: 34px;
  padding: 0;
  color: var(--orange);
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  font-size: 0.95rem;
  font-weight: 700;
  cursor: pointer;
  transition: border-color 160ms ease, background 160ms ease;
}

.icon-button:hover:not(:disabled),
.icon-button:focus-visible {
  background: var(--orange-soft);
  border-color: rgba(var(--orange-rgb), 0.4);
  outline: none;
}

.icon-button:disabled {
  color: var(--line);
  cursor: default;
}

.type-hint {
  margin: 8px 0 0;
  color: var(--muted);
  font-size: 0.85rem;
}

.section-label {
  margin: 26px 0 6px;
  font-size: var(--t-h3);
  font-weight: 650;
  letter-spacing: -0.012em;
}

.section-hint {
  max-width: 62ch;
  margin-bottom: 14px;
  color: var(--muted);
  font-size: var(--t-small);
  line-height: 1.5;
}

/* ---------- Account ---------- */

.account-grid {
  display: grid;
  gap: 16px;
  max-width: 560px;
}

.kv-label {
  display: block;
  margin-top: 12px;
  color: var(--muted);
  font-size: var(--t-small);
  font-weight: 650;
}

.kv-label:first-child {
  margin-top: 0;
}

.kv-value {
  display: block;
  margin-top: 2px;
  font-size: 1rem;
  font-weight: 620;
  overflow-wrap: anywhere;
}

.badge-ok {
  color: var(--ok);
  font-weight: 650;
}

.badge-warn {
  color: var(--warn);
  font-weight: 650;
}

.danger-card {
  border-color: rgba(217, 45, 32, 0.3);
}

.danger-card::after {
  background: linear-gradient(90deg, var(--danger), transparent);
  box-shadow: 0 0 16px rgba(217, 45, 32, 0.4);
}

.danger-card h3 {
  color: var(--danger);
}

.stack {
  display: grid;
  gap: 10px;
}

/* ---------- Loading splash ---------- */

.splash {
  display: grid;
  place-items: center;
  gap: 14px;
  min-height: 60svh;
  color: var(--muted);
}

.splash-pulse {
  width: 42px;
  height: 3px;
  background: var(--orange);
  animation: lineBlink 1.6s ease-in-out infinite;
}

/* ---------- Animations ---------- */

@keyframes lineBlink {
  0%,
  100% {
    opacity: 0.35;
    transform: scaleX(0.68);
  }

  50% {
    opacity: 1;
    transform: scaleX(1);
  }
}

/* ---------- My Menu (table-side ordering) ---------- */

.menu-master {
  display: grid;
  gap: 14px;
  margin-bottom: 20px;
}

/* The Opening hours card: same .card surface as the master switch above it,
   just its own bottom margin so it doesn't sit flush against the Table QR
   codes card below (the qr-hours-* classes it reuses are plain, tag-agnostic
   rules from the QR page and need no ".qr-hours" ancestor). */
.menu-hours {
  margin-bottom: 20px;
}

/* The Table QR codes card: qr-dl-card look (same language as the QR page's
   download cards) dressed with the .card surface so it sits naturally next to
   the master card above it. */
.menu-qr-card {
  margin-bottom: 20px;
  padding: 24px;
  background: rgba(255, 255, 255, 0.88);
  box-shadow: 0 14px 45px rgba(28, 25, 21, 0.06);
}

/* Count field + download button on one ordered line (stacked on phones). */
.menu-qr-row {
  display: flex;
  align-items: flex-end;
  gap: 14px;
  flex-wrap: wrap;
  margin-top: 4px;
}

.menu-tables-input {
  width: 120px;
  padding: 10px 12px;
  font: inherit;
  color: var(--ink);
  background: var(--paper);
  border: 1.5px solid var(--line);
  border-radius: var(--radius);
}

.menu-tables-input:focus {
  outline: none;
  border-color: rgba(var(--orange-rgb), 0.55);
  box-shadow: 0 0 0 3px rgba(var(--orange-rgb), 0.2);
  background: #fff;
}

.menu-toolbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  flex-wrap: wrap;
  margin-bottom: 14px;
}

.menu-toolbar-actions {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
}

/* Category overview: one compact tile per category — clicking it opens that
   category's dishes alone (menu-group below), so the page never lists the
   whole menu at once. */
.menu-cats {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(230px, 1fr));
  gap: 10px;
}

.menu-cat-tile {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 13px 15px;
  border: 1.5px solid var(--line);
  border-radius: var(--radius);
  background: #ffffff;
  font-family: inherit;
  text-align: left;
  cursor: pointer;
  transition: border-color 160ms ease, transform 160ms ease, box-shadow 160ms ease;
}

.menu-cat-tile:hover,
.menu-cat-tile:focus-visible {
  border-color: rgba(var(--orange-rgb), 0.5);
  transform: translateY(-1px);
  box-shadow: 0 8px 24px rgba(var(--orange-rgb), 0.12);
  outline: none;
}

.menu-cat-tile .btn-ico {
  margin-left: auto;
  color: var(--orange);
}

.menu-cat-tile-body {
  display: grid;
  gap: 2px;
  min-width: 0;
}

.menu-cat-tile-name {
  font-weight: 650;
  font-size: 0.95rem;
  color: var(--ink);
  overflow-wrap: anywhere;
}

.menu-cat-tile-count {
  color: var(--muted);
  font-size: 0.8rem;
  font-weight: 620;
}

.menu-group-back {
  margin-bottom: 12px;
}

/* The one open category (or the whole list when there are no categories),
   the category name editable right in the heading row. */
.menu-group {
  margin-bottom: 16px;
}

.menu-group-head {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 10px;
}

.menu-group-title {
  font-size: 0.85rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--muted);
}

.menu-group-tools {
  display: flex;
  gap: 6px;
  flex: none;
}

.menu-cat-name {
  flex: 1;
  min-width: 0;
  font-weight: 650;
}

/* Shared input look for the editor (mirrors the lock/hours fields). */
.menu-input {
  padding: 8px 10px;
  font: inherit;
  color: var(--ink);
  background: var(--paper);
  border: 1.5px solid var(--line);
  border-radius: var(--radius);
}

.menu-input:focus {
  outline: none;
  border-color: rgba(var(--orange-rgb), 0.55);
  box-shadow: 0 0 0 3px rgba(var(--orange-rgb), 0.2);
  background: #fff;
}

.menu-dish {
  padding: 10px 12px;
  margin-bottom: 8px;
}

.menu-dish.is-off {
  opacity: 0.6;
}

.menu-dish-grid {
  display: grid;
  grid-template-columns: 88px 1fr;
  gap: 10px;
  align-items: start;
}

.menu-photo-cell {
  display: flex;
  flex-direction: column;
  gap: 6px;
  align-items: stretch;
  text-align: center;
}

.menu-photo-thumb {
  width: 88px;
  height: 62px;
  object-fit: cover;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--paper);
}

.menu-photo-empty {
  display: grid;
  place-items: center;
  width: 88px;
  height: 62px;
  border: 1.5px dashed var(--line);
  border-radius: var(--radius);
  color: var(--muted);
  font-size: 0.78rem;
}

/* A <label> styled as a small ghost button so the file input stays native. */
.menu-photo-btn {
  display: inline-block;
  padding: 6px 8px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: #fff;
  color: var(--ink);
  font-size: 0.78rem;
  font-weight: 700;
  cursor: pointer;
  text-align: center;
}

.menu-photo-btn:hover {
  border-color: rgba(var(--orange-rgb), 0.5);
}

.menu-photo-btn.is-disabled {
  opacity: 0.5;
  pointer-events: none;
}

.menu-photo-remove {
  border: none;
  background: none;
  color: var(--danger);
  font-family: inherit;
  font-size: 0.76rem;
  font-weight: 700;
  cursor: pointer;
  padding: 2px;
}

.menu-dish-fields {
  display: grid;
  gap: 6px;
  min-width: 0;
}

.menu-dish-row1 {
  display: flex;
  gap: 8px;
}

.menu-dish-name {
  flex: 1;
  min-width: 0;
  font-weight: 700;
}

.menu-price-wrap {
  display: flex;
  align-items: center;
  gap: 6px;
  flex: none;
  color: var(--muted);
  font-weight: 700;
}

.menu-dish-price {
  width: 92px;
  text-align: right;
}

.menu-dish-desc {
  resize: vertical;
  min-height: 36px;
}

.menu-dish-row2 {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
}

.menu-dish-cat {
  max-width: 240px;
}

.menu-avail {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  color: var(--graphite);
  font-size: 0.86rem;
  font-weight: 700;
  cursor: pointer;
  -webkit-user-select: none;
  user-select: none;
}

.menu-avail input {
  width: 16px;
  height: 16px;
  accent-color: var(--orange);
}

/* The move/delete tools sit at the end of the last field row (they used to be
   a third grid column, which cost every card a button of height). */
.menu-dish-tools {
  display: flex;
  gap: 6px;
  margin-left: auto;
}

.menu-dish-tools .icon-button,
.menu-group-tools .icon-button {
  width: 30px;
  height: 30px;
}

.menu-dish-del {
  color: var(--danger);
}

/* Notes & discount: a collapsed strip under the dish fields. Closed it costs
   one line, so the compact editor stays compact; the summary carries the
   state ("20% off · Most Popular") in the brand orange. */
.menu-dish-extras {
  border-top: 1px dashed var(--line);
  margin-top: 2px;
  padding-top: 6px;
}

.menu-extras-summary {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
  list-style: none;
  cursor: pointer;
  color: var(--muted);
  font-size: 0.82rem;
  font-weight: 700;
  -webkit-user-select: none;
  user-select: none;
}

.menu-extras-summary::-webkit-details-marker {
  display: none;
}

/* A caret drawn in CSS rather than an icon: it has to rotate with the state
   and there is one per dish row. */
.menu-extras-title::before {
  content: "";
  display: inline-block;
  width: 6px;
  height: 6px;
  margin-right: 7px;
  border-right: 1.6px solid currentColor;
  border-bottom: 1.6px solid currentColor;
  transform: translateY(-1px) rotate(-45deg);
  transition: transform 140ms ease;
}

.menu-dish-extras[open] .menu-extras-title::before {
  transform: translateY(-3px) rotate(45deg);
}

.menu-extras-state {
  color: var(--orange-strong);
  overflow-wrap: anywhere;
}

.menu-extras-body {
  display: grid;
  gap: 10px;
  padding: 10px 0 4px;
}

.menu-discount-row {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
}

.menu-discount-field {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  color: var(--graphite);
  font-size: 0.86rem;
  font-weight: 700;
}

.menu-discount-input {
  width: 62px;
  text-align: right;
}

.menu-discount-hint {
  color: var(--muted);
  font-size: 0.82rem;
}

.menu-badge-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  align-items: center;
}

.menu-badge-chip {
  padding: 5px 11px;
  border: 1.5px solid var(--line);
  border-radius: 999px;
  background: var(--surface);
  color: var(--graphite);
  font-family: inherit;
  font-size: 0.79rem;
  font-weight: 700;
  cursor: pointer;
  transition: border-color 140ms ease, background 140ms ease, color 140ms ease;
}

.menu-badge-chip:hover:not(:disabled) {
  border-color: var(--orange);
  color: var(--orange-strong);
}

.menu-badge-chip.is-on {
  /* The smallest white-on-orange text in the app (12.6px). */
  border-color: var(--orange-solid);
  background: var(--orange-solid);
  color: #ffffff;
}

/* Disabled means "the two-note limit is reached", not "broken" — it has to
   read as temporarily unavailable, hence a fade rather than a grey box. */
.menu-badge-chip:disabled {
  opacity: 0.45;
  cursor: not-allowed;
}

.menu-badge-custom {
  display: inline-flex;
  align-items: center;
  gap: 4px;
}

.menu-badge-custom .menu-input {
  width: 150px;
  padding-block: 5px;
  border-radius: 999px;
  font-size: 0.79rem;
}

.menu-badge-custom .icon-button {
  width: 26px;
  height: 26px;
  color: var(--danger);
}

.menu-badge-hint {
  color: var(--muted);
  font-size: 0.78rem;
}

/* Allergens (Reg. UE 1169/2011): fourteen chips in the same language as the
   note chips, numbered the way printed menus number them. */
.menu-allergens {
  display: grid;
  gap: 6px;
}

.menu-allergens-title {
  color: var(--graphite);
  font-size: 0.86rem;
  font-weight: 700;
}

/* A category's own text (what comes with every dish, supplements), under its
   name and as wide as the dishes it introduces. */
.menu-cat-desc {
  width: 100%;
  margin-bottom: 10px;
  resize: vertical;
}

/* The menu-wide notes, under the categories and above the save bar. */
.menu-notes-card {
  display: grid;
  gap: 8px;
  margin-top: 16px;
}

.menu-notes-label {
  font-weight: 700;
}

.menu-notes {
  width: 100%;
  resize: vertical;
}

/* An armed delete: the first press turns the trash icon into a labelled
   confirmation, the second deletes. Declared after .menu-dish-tools
   .icon-button so its width wins at equal specificity. */
.menu-dish-del.is-armed {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  width: auto;
  padding: 0 10px;
  border-color: var(--danger);
  font-size: 0.8rem;
  white-space: nowrap;
}

.menu-add-dish {
  width: 100%;
  border-style: dashed;
}

.menu-savebar {
  margin-top: 20px;
  display: grid;
  gap: 8px;
  justify-items: start;
}

/* ---------- Incoming Orders (live kitchen display) ---------- */

.orders-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 16px;
  margin-bottom: 18px;
}

.orders-side {
  display: grid;
  justify-items: end;
  gap: 6px;
  flex: none;
  padding-top: 6px;
}

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

.orders-zoom {
  display: inline-flex;
  gap: 4px;
}

/* The one thing the browser cannot fix by itself: the device volume. Shown
   only while the sound is on, so it reads as a tip and not as noise. */
.orders-hint {
  max-width: 270px;
  margin: 0;
  color: var(--muted);
  font-size: 0.78rem;
  line-height: 1.35;
  text-align: right;
}

/* The "updating live" beacon: a small pulsing orange dot. */
.orders-live {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  color: var(--muted);
  font-size: 0.82rem;
  font-weight: 650;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}

.orders-live-dot {
  width: 9px;
  height: 9px;
  border-radius: 999px;
  background: var(--orange);
  box-shadow: 0 0 0 3px rgba(var(--orange-rgb), 0.16);
  animation: ordersPulse 1.8s ease-in-out infinite;
}

/* Not live. The dot stops pulsing and turns red: on a kitchen screen across the
   room, motion and colour read before any word does. */
.orders-live.is-stale {
  color: var(--danger);
}

.orders-live.is-stale .orders-live-dot {
  background: var(--danger);
  box-shadow: 0 0 0 3px rgba(196, 39, 27, 0.18);
  animation: none;
}

/* A completion the server has not acknowledged yet. Held on screen on purpose:
   it is not done until the server says so, and offline it may be waiting a long
   time. Dimmed rather than hidden, so the ticket is still readable and cookable. */
.order-card.is-pending {
  opacity: 0.55;
}

@keyframes ordersPulse {
  0%,
  100% {
    opacity: 0.6;
    transform: scale(0.8);
  }

  50% {
    opacity: 1;
    transform: scale(1);
  }
}

.orders-list {
  display: grid;
  gap: 12px;
}

/* A new release is out: the screen offers the reload, it never takes it. */
.orders-update {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 14px;
  padding: 10px 14px;
}

/* Older ready tickets of the service, one page at a time. */
.orders-more {
  justify-self: center;
  margin: 16px auto 0;
  display: block;
}

/* One panel per table: the kitchen reads "table 7" as a single job, with its
   rounds stacked inside in the order they were sent. */
.order-group {
  display: grid;
  gap: calc(14px * var(--kds-scale, 1));
  animation: orderArrive 260ms ease-out backwards;
}

.order-group-head {
  display: flex;
  align-items: center;
  gap: 10px;
}

.order-group-count {
  padding: 3px 10px;
  border-radius: 999px;
  background: var(--orange-tint);
  color: var(--orange-strong);
  font-size: 0.8rem;
  font-weight: 700;
}

/* A single open ticket needs no count. */
.order-group-count:empty {
  display: none;
}

.order-group-body {
  display: grid;
  gap: calc(14px * var(--kds-scale, 1));
}

.order-card {
  display: grid;
  gap: 12px;
  animation: orderArrive 260ms ease-out backwards;
}

/* A second round from the same table: kept visibly apart from the first, so
   nobody cooks the two as one ticket. */
.order-card + .order-card {
  border-top: 1px dashed var(--line);
  padding-top: calc(14px * var(--kds-scale, 1));
}

@keyframes orderArrive {
  from {
    opacity: 0;
    transform: translateY(-8px);
  }
}

.order-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
}

/* The table number is what the kitchen reads first: big and unmissable. */
.order-table {
  display: inline-block;
  padding: 6px 14px;
  border-radius: 999px;
  /* White bold on orange needs --orange-solid, not --orange. In fullscreen
     --kds-scale takes this to ~39px where either would pass, but the console
     reads it at 16.8px. */
  background: var(--orange-solid);
  color: #fff;
  font-size: 1.05rem;
  font-weight: 700;
  letter-spacing: 0.02em;
  box-shadow: 0 4px 12px rgba(43, 24, 10, 0.14);
}

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

.order-time {
  color: var(--muted);
  font-size: 0.86rem;
  font-weight: 700;
}

.order-items {
  list-style: none;
  display: grid;
  gap: 6px;
}

.order-items li {
  font-size: 1rem;
  font-weight: 620;
  color: var(--graphite);
}

.order-qty {
  display: inline-block;
  min-width: 34px;
  color: var(--orange-strong);
  font-weight: 700;
}

/* The guest's note for the kitchen: set apart so it is never skimmed over. */
.order-note {
  padding: 10px 12px;
  border-left: 3px solid var(--orange);
  border-radius: 0 var(--radius) var(--radius) 0;
  background: var(--orange-soft);
  color: var(--graphite);
  font-size: 0.94rem;
  font-style: italic;
}

.order-foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  border-top: 1px solid var(--line);
  padding-top: 12px;
}

.order-total {
  font-size: 1.15rem;
  font-weight: 700;
}

.order-err {
  margin: 0;
}

.order-err:empty {
  display: none;
}

/* Kitchen states, two since 2026-09-14: «Da preparare» and «Pronte». Moved here
   from staff/styles.css, which now styles only the waiters' screens. */
.kitchen-tabs {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin: 4px 0 20px;
}

.kitchen-tabs a {
  padding: 8px 14px;
  border: 1px solid var(--line-strong);
  border-radius: 999px;
  background: var(--bg);
  color: var(--graphite);
  font-size: var(--t-small);
  font-weight: 650;
}

.kitchen-tabs a[aria-current="page"] {
  border-color: var(--ink);
  background: var(--ink);
  color: #fff;
}

.order-sender {
  margin: 0;
  color: var(--muted);
  font-size: 0.84rem;
}

/* A note on one dish, under that dish: never merged into the order's note. */
.order-line-note {
  margin: 4px 0 0 34px;
  padding: 4px 8px;
  border-left: 3px solid var(--orange);
  background: var(--orange-soft);
  color: var(--graphite);
  font-size: 0.9rem;
  font-weight: 500;
  overflow-wrap: anywhere;
  white-space: pre-wrap;
}

.order-card .order-note {
  white-space: pre-wrap;
}

.order-card .order-cancel {
  justify-self: start;
}

/* .button sets display:inline-flex, which would beat the hidden attribute. */
.order-done[hidden] {
  display: none !important;
}

/* Out of the kitchen's hands (since 2026-09-26): marked «Pronto», carried to the
   table or cancelled. The card stays three minutes in its new state, then leaves
   the board by itself (staff/board.js), so nothing vanishes under a cook's eyes. */
.order-card.is-replaced {
  animation: none;
}

.order-state {
  font-weight: 700;
}

.order-card.is-closed .order-items li {
  color: var(--muted);
}

.order-card.is-ready .order-state,
.order-card.is-delivered .order-state,
.order-card.is-done .order-state {
  background: var(--ok-soft);
  color: var(--ok);
}

.order-card.is-cancelled {
  padding: 12px 14px;
  border: 2px solid var(--danger);
  border-radius: var(--radius);
  background: rgba(196, 39, 27, 0.06);
}

.order-card.is-cancelled .order-state {
  background: var(--danger);
  color: #fff;
}

.order-card.is-cancelled .order-items li {
  text-decoration: line-through;
}

.order-cancel-reason {
  margin: 0;
  color: var(--danger);
  font-weight: 650;
  overflow-wrap: anywhere;
  white-space: pre-wrap;
}

/* Fullscreen kitchen mode: white wall-display background, larger type so the
   pass can read tables and dishes from across the kitchen. Everything that
   grows is driven by --kds-scale, so the A− / A+ buttons only ever set one
   number: bigger tickets and, because the column floor grows with them, fewer
   per row. The variable is scoped to fullscreen — the console next to the
   sidebar keeps its normal size. */
.orders-shell.is-fullscreen {
  --kds-scale: 1;
  background: var(--bg);
  overflow-y: auto;
  padding: 24px clamp(16px, 4vw, 48px) 48px;
  width: 100%;
  max-width: none;
}

.orders-shell.is-fullscreen.zoom-2 {
  --kds-scale: 1.35;
}

.orders-shell.is-fullscreen.zoom-3 {
  --kds-scale: 1.8;
}

/* On the wall display the eyebrow / restaurant name / lede are noise: only
   the tickets and the tool row (live dot, sound, A− A+, exit) stay. */
.orders-shell.is-fullscreen .orders-heading {
  display: none;
}

.orders-shell.is-fullscreen .orders-head {
  justify-content: flex-end;
}

.orders-shell.is-fullscreen .orders-list {
  grid-template-columns: repeat(auto-fill, minmax(calc(340px * var(--kds-scale)), 1fr));
  gap: calc(16px * var(--kds-scale));
  /* Panels keep their own height: a table with one dish must not be inflated
     to match the busiest one in its row. */
  align-items: start;
}

.orders-shell.is-fullscreen .order-group {
  padding: calc(24px * var(--kds-scale));
}

.orders-shell.is-fullscreen .order-table {
  font-size: calc(1.35rem * var(--kds-scale));
  padding: calc(6px * var(--kds-scale)) calc(14px * var(--kds-scale));
}

.orders-shell.is-fullscreen .order-group-count {
  font-size: calc(0.85rem * var(--kds-scale));
}

.orders-shell.is-fullscreen .order-items li {
  font-size: calc(1.15rem * var(--kds-scale));
}

.orders-shell.is-fullscreen .order-qty {
  min-width: calc(34px * var(--kds-scale));
}

.orders-shell.is-fullscreen .order-time,
.orders-shell.is-fullscreen .order-elapsed,
.orders-shell.is-fullscreen .order-state {
  font-size: calc(0.9rem * var(--kds-scale));
}

.orders-shell.is-fullscreen .order-note {
  font-size: calc(1rem * var(--kds-scale));
}

.orders-shell.is-fullscreen .order-total {
  font-size: calc(1.15rem * var(--kds-scale));
}

.orders-shell.is-fullscreen .order-done {
  min-height: calc(40px * var(--kds-scale));
  padding: 0 calc(18px * var(--kds-scale));
  font-size: calc(0.95rem * var(--kds-scale));
}

/* ---------- Responsive ---------- */

@media (max-width: 720px) {
  :root {
    --header-h: 64px;
  }

  .app-header {
    padding: 10px 12px;
  }

  .brand-logo {
    width: 88px;
  }

  .stat-track {
    grid-template-columns: 1fr;
  }

  .stat {
    min-height: 92px;
  }

  .orders-head {
    flex-direction: column;
  }

  .orders-tools {
    padding-top: 0;
  }

  .menu-qr-row {
    flex-direction: column;
    align-items: stretch;
  }

  .menu-dish-grid {
    grid-template-columns: 1fr;
  }

  .menu-photo-cell {
    flex-direction: row;
    align-items: center;
  }

  .menu-dish-tools {
    flex-direction: row;
  }
}

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

/* ---------- legal footer — used only by reset.html now ----------
   The SPA shell dropped its persistent footer: legal links live in the
   Account view (.legal-links) and under the paywall card (.auth-legal). */

.app-footer {
  width: min(calc(100% - 32px), var(--max));
  margin: 40px auto 0;
  padding: 18px 0 26px;
  border-top: 1px solid var(--line);
}

.app-footer nav {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 8px 20px;
}

.app-footer a {
  color: var(--muted);
  font-size: 0.82rem;
}

.app-footer a:hover,
.app-footer a:focus-visible {
  color: var(--orange-strong);
  text-decoration: underline;
}

/* Legal links inside the Account view's Legal card. */
.legal-links {
  display: flex;
  flex-wrap: wrap;
  gap: 8px 20px;
  margin-top: 6px;
}

.legal-links a,
.auth-legal a {
  color: var(--muted);
  font-size: 0.82rem;
}

.legal-links a:hover,
.legal-links a:focus-visible,
.auth-legal a:hover,
.auth-legal a:focus-visible {
  color: var(--orange-strong);
  text-decoration: underline;
}

/* Legal links under the paywall card (the one state where Account — and its
   Legal card — is unreachable). */
.auth-legal {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 8px 20px;
  margin-top: 16px;
}

/* ---------- "Add to desktop" install button + instructions modal ---------- */

/* Floating pill at the bottom-right of the Dashboard: white fill, orange
   border, the Judgy mark + "Add to desktop". Lives outside <main>, so the
   frequent view re-renders never touch it. The wrapper owns the fixed corner
   position so the pill and its dismiss badge move and hide as one. */
.install-fab-wrap {
  position: fixed;
  right: 20px;
  bottom: calc(20px + env(safe-area-inset-bottom, 0px));
  z-index: 100;
  /* Shrink-wrap the pill so the badge can hang off its top-right corner. */
  width: max-content;
}

.install-fab-wrap[hidden] {
  display: none;
}

.install-fab {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  padding: 11px 18px;
  color: var(--orange-strong);
  background: #ffffff;
  border: 1.5px solid var(--orange);
  border-radius: 999px;
  box-shadow: 0 10px 30px rgba(var(--orange-rgb), 0.18), 0 2px 6px rgba(28, 25, 21, 0.08);
  font-family: inherit;
  font-size: 0.92rem;
  font-weight: 650;
  letter-spacing: -0.01em;
  cursor: pointer;
  transition: transform 160ms ease, box-shadow 160ms ease, border-color 160ms ease;
}

.install-fab:hover,
.install-fab:focus-visible {
  transform: translateY(-1px);
  border-color: var(--orange);
  box-shadow: 0 14px 36px rgba(var(--orange-rgb), 0.26), 0 2px 6px rgba(28, 25, 21, 0.1);
  outline: none;
}

.install-fab:active {
  transform: translateY(0);
}

.install-fab-icon {
  flex: none;
  width: 20px;
  height: 20px;
  display: block;
}

/* Small circled orange "×" hanging off the pill's top-right corner: an orange
   disc with a white glyph and a white ring so it reads as a badge, not a hole. */
.install-fab-close {
  position: absolute;
  top: -7px;
  right: -7px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 22px;
  height: 22px;
  padding: 0;
  color: #ffffff;
  background: var(--orange);
  border: 2px solid #ffffff;
  border-radius: 999px;
  box-shadow: 0 3px 8px rgba(var(--orange-rgb), 0.35);
  cursor: pointer;
  transition: transform 140ms ease, box-shadow 140ms ease;
}

.install-fab-close:hover,
.install-fab-close:focus-visible {
  transform: scale(1.1);
  outline: none;
  box-shadow: 0 5px 12px rgba(var(--orange-rgb), 0.45);
}

.install-fab-close:active {
  transform: scale(0.92);
}

.install-fab-close svg {
  display: block;
}

/* Narrow screens: keep the label but tuck the button tighter to the corner. */
@media (max-width: 480px) {
  .install-fab-wrap {
    right: 12px;
    bottom: calc(12px + env(safe-area-inset-bottom, 0px));
  }

  .install-fab {
    padding: 10px 15px;
    font-size: 0.88rem;
  }
}

/* Instructions modal — shown where the browser has no native install prompt
   (iOS Safari, Firefox, desktop Safari). Mirrors the .qr-modal look. */
.install-modal {
  position: fixed;
  inset: 0;
  z-index: 130;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: clamp(16px, 4vw, 40px);
  background: rgba(255, 255, 255, 0.72);
  backdrop-filter: blur(12px) saturate(1.05);
  -webkit-backdrop-filter: blur(12px) saturate(1.05);
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transition: opacity 260ms cubic-bezier(0.16, 1, 0.3, 1), visibility 0s linear 260ms;
}

.install-modal.is-open {
  opacity: 1;
  visibility: visible;
  pointer-events: auto;
  transition: opacity 260ms cubic-bezier(0.16, 1, 0.3, 1);
}

.install-modal[hidden] {
  display: none;
}

.install-modal-card {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 14px;
  width: min(92vw, 420px);
  padding: 32px clamp(20px, 4vw, 30px) 26px;
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: 20px;
  box-shadow: var(--shadow);
  transform: translateY(16px) scale(0.965);
  opacity: 0;
  transition: transform 340ms cubic-bezier(0.16, 1, 0.3, 1), opacity 240ms ease;
  will-change: transform, opacity;
}

.install-modal.is-open .install-modal-card {
  transform: translateY(0) scale(1);
  opacity: 1;
}

.install-modal-icon {
  width: 56px;
  height: 56px;
  display: block;
}

.install-modal-title {
  margin: 0;
  font-size: 1.15rem;
  font-weight: 700;
  text-align: center;
  letter-spacing: -0.01em;
}

.install-modal-steps {
  margin: 4px 0 0;
  padding-left: 22px;
  align-self: stretch;
  display: grid;
  gap: 10px;
  color: var(--graphite);
  font-size: 0.95rem;
  line-height: 1.5;
}

.install-modal-steps li {
  padding-left: 4px;
}

.install-modal-steps li::marker {
  color: var(--orange-strong);
  font-weight: 700;
}

/* Round orange close button, matching .qr-modal-close. */
.install-modal-close {
  position: absolute;
  top: 12px;
  right: 12px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 36px;
  height: 36px;
  padding: 0;
  color: #ffffff;
  background: var(--orange);
  border: none;
  border-radius: 999px;
  box-shadow: 0 4px 12px rgba(43, 24, 10, 0.18);
  cursor: pointer;
  transition: transform 160ms ease, box-shadow 160ms ease;
}

.install-modal-close:hover,
.install-modal-close:focus-visible {
  transform: scale(1.06);
  outline: none;
  box-shadow: 0 8px 22px rgba(var(--orange-rgb), 0.42);
}

.install-modal-close:active {
  transform: scale(0.95);
}

.install-modal-close svg {
  display: block;
  width: 18px;
  height: 18px;
}

/* ---------- Boot splash ----------
   The markup is static in index.html: script-src forbids inline scripts, so
   nothing injected can paint before the external scripts run. It is a SIBLING
   of #view (every view rewrites view.innerHTML) and a fixed overlay, so
   app-shell.js can repaint the chrome and main.js can paint its first screen
   underneath it, unseen. boot-check.js owns it: it advances the bar from real
   milestones and removes the node the moment the app is ready, or before any
   fallback card. z-index clears the drawer (145), scrim (140), modals (120/130)
   and the install FAB (100). */

.boot {
  position: fixed;
  inset: 0;
  z-index: 200;
  display: grid;
  place-items: center;
  padding: 24px;
  background: var(--ground);
  transition: opacity 240ms ease;
}

.boot.is-done {
  opacity: 0;
  pointer-events: none;
}

.boot-inner {
  display: grid;
  justify-items: center;
  gap: 20px;
  width: min(260px, 74vw);
}

/* Beats .brand-logo's own width and its ≤720px override. */
.boot .brand-logo {
  width: 132px;
  height: auto;
}

.boot-track {
  width: 100%;
  height: 6px;
  overflow: hidden;
  /* --line on the warm ground was too faint to read as a container at all;
     the tint gives the empty half a visible boundary without shouting. */
  background: var(--orange-tint);
  border-radius: 999px;
}

.boot-bar {
  display: block;
  width: 0;
  /* Two percent of 240px is 5px, which a pill radius would round away into a
     dot. The floor keeps the fill legible as a bar from the very first step. */
  min-width: 14px;
  height: 100%;
  background: var(--orange);
  border-radius: 999px;
  /* Linear, not eased: the JS already eases toward each ceiling, and a second
     easing on top makes the bar visibly lag the number beside it. */
  transition: width 180ms linear;
}

.boot-pct {
  margin: 0;
  color: var(--graphite);
  font-size: var(--t-body);
  font-weight: 650;
  letter-spacing: 0.01em;
}

/* ---------- Incoming orders: the sound toggle ----------
   Two channels of state, so it reads at a glance in bad light: SHAPE (a bell,
   or a bell struck through) and COLOUR (brand orange, or grey). aria-pressed is
   already in the DOM for assistive tech, so the state needs no extra class.
   Specificity 0,3,0 beats .button-ghost .btn-ico at 0,2,0. */

.orders-sound[aria-pressed="false"] .btn-ico {
  color: var(--muted);
}

/* The bell rocks once when an order lands — the only arrival cue an owner with
   the volume down ever gets. Origin is the stem, not the centre of the box. */
.orders-sound .btn-ico {
  transform-origin: 50% 18%;
}

.orders-sound.is-ringing .btn-ico {
  animation: bellRing 620ms cubic-bezier(0.36, 0.07, 0.19, 0.97) 1;
  /* Declared after the aria-pressed rule on purpose: a MUTED bell must flash
     on arrival too, which is the whole point of decoupling it from the sound. */
  color: var(--orange-strong);
}

@keyframes bellRing {
  0% {
    transform: rotate(0deg);
  }

  15% {
    transform: rotate(11deg);
  }

  32% {
    transform: rotate(-9deg);
  }

  49% {
    transform: rotate(6deg);
  }

  66% {
    transform: rotate(-4deg);
  }

  83% {
    transform: rotate(2deg);
  }

  100% {
    transform: rotate(0deg);
  }
}

/* ---------- My Menu: the hub ----------
   #/menu is a landing grid; every job on it lives at its own #/menu/* route.
   Two kinds of tile, and the difference is structural rather than decorative,
   because it is the difference between a door and a dial:

     .hub-card         a DESTINATION. White, hairline, illustrated vignette,
                       a chevron that surfaces on hover. Navigates.
     .hub-card-live    an INSTRUMENT. Sits on the orange wash with an orange
                       border, holds its control inline, and goes nowhere.

   Column count is set by the view, not guessed by auto-fit: the BASE plan has
   four tiles and the PRO plan six, and one auto-fit rule cannot make both look
   deliberate (4 across a 3-track grid leaves a hole). */

.hub-grid {
  display: grid;
  gap: 18px;
  margin: 4px 0 34px;
}

.hub-grid.is-two {
  grid-template-columns: repeat(2, minmax(0, 1fr));
  max-width: 760px;
}

.hub-card {
  display: grid;
  grid-template-rows: auto 1fr;
  gap: 14px;
  padding: 14px 14px 18px;
  color: inherit;
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: var(--shadow-sm);
  transition: transform 160ms ease, box-shadow 160ms ease, border-color 160ms ease;
}

/* One element moves per gesture: the card lifts, and the drawing breathes
   inside its own frame. Nothing else animates. */
a.hub-card:hover,
a.hub-card:focus-visible {
  transform: translateY(-2px);
  border-color: var(--orange-line);
  box-shadow: var(--shadow-lift);
  outline: none;
}

a.hub-card:focus-visible {
  border-color: var(--orange);
  box-shadow: var(--shadow-lift), 0 0 0 3px rgba(var(--orange-rgb), 0.2);
}

.hub-art {
  display: block;
  overflow: hidden;
  aspect-ratio: 10 / 7;
  background: var(--orange-soft);
  border-radius: var(--radius-lg);
}

.hub-art svg {
  display: block;
  width: 100%;
  height: 100%;
  transition: transform 260ms cubic-bezier(0.16, 1, 0.3, 1);
}

a.hub-card:hover .hub-art svg,
a.hub-card:focus-visible .hub-art svg {
  transform: scale(1.035);
}

.hub-body {
  display: grid;
  align-content: start;
  gap: 5px;
  padding: 0 4px;
}

.hub-title {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: var(--t-h3);
  font-weight: 650;
  letter-spacing: -0.012em;
  line-height: 1.25;
}

/* The chevron is a hover affordance, not a glyph glued onto the label — no
   arrow lives inside the words. */
.hub-title .btn-ico {
  width: 16px;
  height: 16px;
  margin-left: auto;
  color: var(--orange);
  opacity: 0;
  transform: translateX(-3px);
  transition: opacity 160ms ease, transform 160ms ease;
}

a.hub-card:hover .hub-title .btn-ico,
a.hub-card:focus-visible .hub-title .btn-ico {
  opacity: 1;
  transform: none;
}

/* Live state, read off the real menu: how many dishes, whether the venue is
   open right now, how many table codes exist. It is what makes the hub a
   panel you read rather than a menu you click through. */
.hub-note {
  color: var(--muted);
  font-size: var(--t-small);
  line-height: 1.45;
}

.hub-note strong {
  color: var(--graphite);
  font-weight: 650;
}

/* The instrument. */
.hub-card-live {
  background: var(--orange-wash);
  border-color: var(--orange-line);
}

.hub-card-live .hub-art {
  background: var(--bg);
}

/* Wears .hours-switch too, so the track, the thumb, the checked state and the
   focus ring all come from the one switch in this stylesheet. This block is
   only the hub's layout, and it must stay AFTER .hours-switch to win. */
.hub-switch {
  align-items: center;
  gap: 10px;
  margin-top: 4px;
}

.hub-switch .hours-switch-track {
  margin-top: 0;
}

.hub-switch-state {
  color: var(--graphite);
  font-size: var(--t-small);
  font-weight: 650;
}

.hub-card-live.is-blocked {
  background: var(--bg);
  border-color: var(--line);
}

.hub-card-live.is-blocked .hub-switch {
  cursor: not-allowed;
  opacity: 0.55;
}

@media (max-width: 900px) {
  .hub-grid.is-two {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    max-width: none;
  }
}

/* A phone in a dining room: one column, and the drawing shrinks to a tile
   beside the words instead of six tall posters to scroll past. */
@media (max-width: 620px) {
  .hub-grid.is-two {
    grid-template-columns: 1fr;
    gap: 12px;
  }

  .hub-card {
    grid-template-rows: none;
    grid-template-columns: 76px 1fr;
    align-items: center;
    gap: 14px;
    padding: 14px;
  }

  .hub-art {
    aspect-ratio: 1;
    border-radius: var(--radius);
  }

  .hub-art svg {
    transform: scale(1.28);
  }

  a.hub-card:hover .hub-art svg,
  a.hub-card:focus-visible .hub-art svg {
    transform: scale(1.28);
  }

  .hub-title .btn-ico {
    opacity: 1;
    transform: none;
  }
}

/* ---------- My Menu: sub-page chrome ----------
   Every #/menu/* page opens with the way back. It is a real link to a real
   route, so the browser's own Back and this agree with each other. */

.subnav {
  display: flex;
  align-items: center;
  gap: 6px;
  margin: 0 0 18px;
  padding: 6px 12px 6px 8px;
  color: var(--muted);
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: 999px;
  font-size: var(--t-small);
  font-weight: 650;
  justify-self: start;
  width: max-content;
  transition: color 160ms ease, border-color 160ms ease, background 160ms ease;
}

.subnav:hover,
.subnav:focus-visible {
  color: var(--orange-strong);
  border-color: var(--orange-line);
  background: var(--orange-wash);
  outline: none;
}

.subnav .btn-ico {
  width: 16px;
  height: 16px;
  color: var(--orange);
}

/* Unsaved dish edits survive a hop to another #/menu page, because the draft
   lives at module scope rather than inside the view. This is how the owner is
   told so — on the hub tile, and again above Save. */
.hub-flag {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  margin-top: 2px;
  padding: 2px 9px;
  color: var(--warn);
  background: var(--orange-tint);
  border-radius: 999px;
  font-size: var(--t-micro);
  font-weight: 650;
  width: max-content;
}

/* ---------- Reduced motion ----------
   Declared LAST on purpose. The global block earlier in this file zeroes every
   duration with !important, but sections declared after it can override that,
   so any new motion rule needs its counterpart down here to be sure of
   winning. The boot bar is the interesting case: it is INFORMATION, not
   decoration, so it must keep showing progress — it just stops sliding and
   steps between milestone values instead. */
@media (prefers-reduced-motion: reduce) {
  .boot,
  .boot-bar {
    transition: none !important;
  }

  .orders-sound.is-ringing .btn-ico {
    animation: none !important;
  }

  a.hub-card:hover,
  a.hub-card:focus-visible {
    transform: none;
  }

  .hub-art svg,
  a.hub-card:hover .hub-art svg,
  a.hub-card:focus-visible .hub-art svg {
    transform: none;
  }

  .hub-title .btn-ico {
    opacity: 1;
    transform: none;
  }
}
