/* Golden Dish — styles.css. SOURCE OF TRUTH for the design tokens (DESIGN.md mirrors this). Vanilla CSS,
   no build step. OKLCH role-based tokens; self-hosted faces with a system fallback so the site renders
   before the woff2 files are vendored. Elevated British-Chinese takeaway: lacquer red + gold on paper. */

/* ---- Self-hosted faces (fall back to system stack until the woff2 files land — see fonts/README.md) -- */
@font-face {
  font-family: "Fraunces"; font-style: normal; font-weight: 400 700;
  font-display: swap; src: url("/assets/fonts/fraunces-var.woff2") format("woff2");
}
@font-face {
  font-family: "Public Sans"; font-style: normal; font-weight: 300 700;
  font-display: swap; src: url("/assets/fonts/public-sans-var.woff2") format("woff2");
}

/* ---- Tokens ---------------------------------------------------------------------------------------- */
:root {
  --brand:       oklch(50% 0.19 25);
  --brand-deep:  oklch(37% 0.14 25);
  --accent:      oklch(78% 0.12 82);
  --accent-deep: oklch(62% 0.11 75);
  --bg:          oklch(97.5% 0.006 85);
  --surface:     oklch(100% 0 0);
  --surface-2:   oklch(96% 0.008 80);
  --border:      oklch(90% 0.010 80);
  --text-strong: oklch(24% 0.020 40);
  --text:        oklch(34% 0.018 40);
  --text-muted:  oklch(53% 0.018 50);
  --on-brand:    oklch(96% 0.020 85);
  --success:     oklch(56% 0.13 150);
  --warning:     oklch(72% 0.14 70);
  --danger:      oklch(55% 0.20 15);

  --font-head: "Fraunces", Georgia, "Times New Roman", serif;
  --font-body: "Public Sans", system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;

  --space-1: 4px; --space-2: 8px; --space-3: 12px; --space-4: 16px;
  --space-5: 24px; --space-6: 32px; --space-7: 48px; --space-8: 64px;

  --radius: 14px; --radius-sm: 8px; --radius-pill: 999px;
  --shadow-sm: 0 1px 2px oklch(24% 0.02 40 / 0.06), 0 2px 8px oklch(24% 0.02 40 / 0.05);
  --shadow-md: 0 8px 30px oklch(24% 0.02 40 / 0.16);
  --shadow-glow: 0 0 0 3px oklch(78% 0.12 82 / 0.4);

  --dur: 180ms; --dur-panel: 260ms; --ease: cubic-bezier(0.2, 0.8, 0.2, 1);
  --wrap: 1080px;
}

/* ---- Base ------------------------------------------------------------------------------------------ */
*, *::before, *::after { box-sizing: border-box; }
/* The `hidden` attribute must always win — a class selector's `display:` (e.g. .opts-modal{display:grid})
   otherwise beats the UA `[hidden]{display:none}` and leaks the element open on load. Keep this global. */
[hidden] { display: none !important; }
html { -webkit-text-size-adjust: 100%; }
/* Column layout pins the footer to the bottom on short pages (empty checkout, policies, /soon,
   an unmatched AI query) — otherwise it floats mid-viewport with dead ground beneath it. */
body {
  margin: 0; background: var(--bg); color: var(--text);
  font-family: var(--font-body); font-size: 16px; line-height: 1.55;
  -webkit-font-smoothing: antialiased;
  min-height: 100vh; display: flex; flex-direction: column;
}
#main { flex: 1 0 auto; }
.site-foot { flex-shrink: 0; }
body.modal-open { overflow: hidden; }
h1, h2, h3, h4 { font-family: var(--font-head); color: var(--text-strong); line-height: 1.1; letter-spacing: -0.01em; margin: 0 0 var(--space-3); }
h1 { font-size: clamp(1.9rem, 4vw, 2.8rem); font-weight: 600; }
h2 { font-size: clamp(1.4rem, 2.6vw, 1.9rem); font-weight: 600; }
h3 { font-size: 1.1rem; font-weight: 600; }
p { margin: 0 0 var(--space-3); }
a { color: var(--brand); text-decoration: none; }
a:hover { text-decoration: underline; }
img { max-width: 100%; height: auto; }
.wrap { max-width: var(--wrap); margin: 0 auto; padding-inline: var(--space-4); width: 100%; }
.narrow { max-width: 760px; }
.eyebrow { font-size: 0.78rem; font-weight: 600; text-transform: uppercase; letter-spacing: 0.14em; color: var(--accent-deep); margin-bottom: var(--space-2); }
.skip { position: absolute; left: -999px; top: 0; background: var(--brand); color: var(--on-brand); padding: var(--space-2) var(--space-4); z-index: 200; }
.skip:focus { left: var(--space-2); top: var(--space-2); border-radius: var(--radius-sm); }

/* ---- Buttons --------------------------------------------------------------------------------------- */
.btn-primary, .add-btn {
  font-family: var(--font-body); font-weight: 600; cursor: pointer; border: 0;
  border-radius: var(--radius-pill); transition: transform var(--dur) var(--ease), box-shadow var(--dur) var(--ease), background var(--dur) var(--ease);
}
.btn-primary {
  display: inline-block; text-align: center; background: var(--brand); color: var(--on-brand);
  padding: var(--space-3) var(--space-6); font-size: 1rem; text-decoration: none;
}
.btn-primary:hover { background: var(--brand-deep); box-shadow: var(--shadow-glow); text-decoration: none; }
.btn-primary:disabled, .btn-primary.is-disabled { opacity: 0.5; pointer-events: none; }
.add-btn {
  background: var(--brand); color: var(--on-brand); padding: var(--space-2) var(--space-4); font-size: 0.9rem;
}
.add-btn:hover { background: var(--brand-deep); }
.add-btn.added { background: var(--success); transform: scale(1.06); }
.add-badge { display: inline-grid; place-items: center; min-width: 1.25em; height: 1.25em; margin-left: 0.35em;
  padding: 0 0.3em; border-radius: 999px; background: var(--on-brand); color: var(--brand);
  font-size: 0.72rem; font-weight: 700; line-height: 1; }

/* Inline quantity stepper — the Add button morphs into this once the (no-options) item is in the basket. */
.qstep { display: inline-flex; align-items: center; gap: 0; border: 1px solid var(--brand);
  border-radius: 999px; overflow: hidden; background: var(--surface); }
.qstep button { background: var(--brand); color: var(--on-brand); border: 0; width: 2rem; height: 2rem;
  font-size: 1.1rem; line-height: 1; cursor: pointer; padding: 0; }
.qstep button:hover { background: var(--brand-deep); }
.qstep .q-n { min-width: 2ch; text-align: center; font-weight: 700; color: var(--text-strong);
  font-variant-numeric: tabular-nums; }

/* Floating basket bar — persistent, non-intrusive running total (never auto-opens the panel). */
.basket-bar { position: fixed; left: 50%; bottom: var(--space-5); transform: translateX(-50%); z-index: 150;
  display: inline-flex; align-items: center; gap: var(--space-3); padding: var(--space-3) var(--space-5);
  border: 0; border-radius: 999px; background: var(--brand); color: var(--on-brand);
  box-shadow: var(--shadow-md); cursor: pointer; font-size: 0.95rem; font-weight: 600;
  animation: bb-in 0.18s ease-out; }
.basket-bar:hover { background: var(--brand-deep); }
/* The bar and the panel share z-index 150, and the bar comes later in the DOM — so with the panel open
   it painted directly OVER "Go to checkout" and obscured the primary action (measured overlap on a
   390x844 phone). The bar's whole job is to open the panel, so once the panel is open it is redundant. */
body:has(.basket-panel.open) .basket-bar { display: none; }
.basket-bar .bb-count { display: inline-grid; place-items: center; min-width: 1.5em; height: 1.5em;
  padding: 0 0.4em; border-radius: 999px; background: var(--on-brand); color: var(--brand);
  font-weight: 700; font-variant-numeric: tabular-nums; }
.basket-bar .bb-total { font-variant-numeric: tabular-nums; }
@keyframes bb-in { from { transform: translate(-50%, 120%); } to { transform: translate(-50%, 0); } }

/* ---- Header ---------------------------------------------------------------------------------------- */
.site-head { position: sticky; top: 0; z-index: 100; background: var(--brand-deep); color: var(--on-brand); box-shadow: var(--shadow-sm); }
.site-head .wrap { display: flex; align-items: center; gap: var(--space-4); min-height: 62px; }
.brand { display: inline-flex; align-items: center; gap: var(--space-3); color: var(--on-brand); text-decoration: none; }
.brand:hover { text-decoration: none; }
.brand-mark {
  display: grid; place-items: center; width: 34px; height: 34px; border-radius: var(--radius-sm);
  background: var(--brand); color: var(--accent); font-family: var(--font-head); font-weight: 700; font-size: 1.15rem;
  border: 1px solid oklch(78% 0.12 82 / 0.4);
}
.brand-name { font-family: var(--font-head); font-weight: 600; font-size: 1.25rem; letter-spacing: -0.01em; }
.site-nav { margin-left: auto; display: flex; align-items: center; gap: var(--space-5); }
.site-nav a { color: var(--on-brand); font-weight: 500; opacity: 0.92; }
.site-nav a:hover { opacity: 1; text-decoration: none; color: var(--accent); }
.basket-btn {
  position: relative; display: inline-grid; place-items: center; width: 42px; height: 42px;
  background: var(--brand); color: var(--on-brand); border: 0; border-radius: var(--radius-pill); cursor: pointer;
  transition: background var(--dur) var(--ease);
}
.basket-btn:hover { background: oklch(55% 0.20 25); }
.basket-count {
  position: absolute; top: -4px; right: -4px; min-width: 20px; height: 20px; padding: 0 5px;
  display: grid; place-items: center; background: var(--accent); color: var(--brand-deep);
  font-size: 0.72rem; font-weight: 700; border-radius: var(--radius-pill);
}

/* ---- Menu hero ------------------------------------------------------------------------------------- */
.menu-hero { background: var(--brand-deep); color: var(--on-brand); padding: var(--space-7) 0 var(--space-5); position: relative; overflow: hidden; }
.menu-hero::after { content: ""; display: block; height: 6px; background: linear-gradient(90deg, var(--accent), transparent); opacity: 0.5; position: absolute; left: 0; right: 0; bottom: 0; z-index: 2; }
/* Real shopfront photo behind the hero text — darkened for legibility (props: client's Google photo). */
.menu-hero.has-media .hero-bg { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; opacity: 0.55; z-index: 0; }
.menu-hero.has-media::before { content: ""; position: absolute; inset: 0; z-index: 1; background: linear-gradient(180deg, oklch(20% 0.06 30 / 0.4), oklch(37% 0.14 25 / 0.82) 92%); }
.menu-hero .wrap { position: relative; z-index: 2; }
.menu-hero h1 { color: var(--on-brand); margin-bottom: var(--space-2); }
.menu-hero .eyebrow { color: var(--accent); }
.menu-hero .lede { font-size: 1.1rem; opacity: 0.9; max-width: 46ch; }


/* Hero two-column: content left, opening-hours panel right (over the transparent shopfront). */
.hero-grid { display: flex; align-items: flex-start; justify-content: space-between; gap: var(--space-6); }
/* Physical units (15mm/6mm) have no business in a screen layout and sat on no scale — they resolved to
   56.7px and 22.7px. Back on the token scale; the shopfront photo behind this is decorative, so the
   panel no longer needs to dodge the printed phone number pixel-for-pixel. */
.hero-content { flex: 1 1 auto; margin-top: var(--space-7); }
.hero-hours { flex: 0 0 auto; min-width: 224px; align-self: center; margin-top: var(--space-5);
  background: oklch(20% 0.03 30 / 0.4); border: 1px solid oklch(100% 0 0 / 0.16);
  border-radius: var(--radius); padding: var(--space-4) var(--space-5); backdrop-filter: blur(3px); }
.hero-hours h2 { margin: 0 0 var(--space-3); font-size: 0.72rem; font-weight: 700;
  text-transform: uppercase; letter-spacing: 0.12em; color: var(--accent); }
.hero-hours dl { margin: 0; display: grid; gap: var(--space-1); }
.hero-hours .hh-row { display: flex; justify-content: space-between; gap: var(--space-5);
  font-size: 0.92rem; color: var(--on-brand); }
.hero-hours dt { margin: 0; opacity: 0.82; }
.hero-hours dd { margin: 0; font-variant-numeric: tabular-nums; }
.hero-hours .hh-row.is-today { font-weight: 700; }
.hero-hours-all { display: none; color: var(--accent); font-size: 0.88rem; font-weight: 600; }  /* mobile only */
.hero-hours .hh-row.is-today dt, .hero-hours .hh-row.is-today dd { color: var(--accent); opacity: 1; }

/* ---- Menu layout ----------------------------------------------------------------------------------- */
.menu-layout { display: grid; grid-template-columns: 220px 1fr; gap: var(--space-6); align-items: start; padding-top: var(--space-6); padding-bottom: var(--space-8); }
.cat-nav { position: sticky; top: 78px; display: flex; flex-direction: column; gap: 2px; max-height: calc(100vh - 96px); overflow-y: auto; }
.cat-link { padding: var(--space-2) var(--space-3); border-radius: var(--radius-sm); color: var(--text-muted); font-weight: 500; font-size: 0.95rem; border-left: 3px solid transparent; }
.cat-link:hover { background: var(--surface-2); text-decoration: none; color: var(--text-strong); }
.cat-link.active { color: var(--brand); border-left-color: var(--accent); background: var(--surface); font-weight: 600; }

/* Returning-customer bar: "Order again". Gold-tinted so it reads as a personal offer distinct from the
   menu itself, and it only ever appears for a device that has ordered before. */
.order-again-bar { display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap;
  gap: var(--space-3); margin: var(--space-4) 0 0; padding: var(--space-4) var(--space-5);
  background: oklch(78% 0.12 82 / 0.14); border: 1px solid oklch(78% 0.12 82 / 0.5);
  border-radius: var(--radius); }
.order-again-bar .oa-text { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.order-again-bar strong { font-family: var(--font-head); font-size: 1.05rem; color: var(--text-strong); }
.order-again-bar .oa-summary { color: var(--text); font-size: 0.94rem; }
.order-again-bar .oa-note:empty { display: none; }
.order-again-bar .oa-note { color: var(--text-muted); font-size: 0.82rem; }
.order-again-bar .oa-btn { flex: 0 0 auto; }

/* "Welcome back" on checkout — same idea, quieter, because the fields below are the real payoff. */
.welcome-back { display: flex; align-items: baseline; flex-wrap: wrap; gap: var(--space-2);
  margin: 0 0 var(--space-4); padding: var(--space-3) var(--space-4); font-size: 0.94rem;
  background: var(--surface-2); border-left: 3px solid var(--accent-deep);
  border-radius: var(--radius-sm); color: var(--text); }
.welcome-back .wb-reset { font-weight: 600; }

@media (max-width: 520px) {
  .order-again-bar { align-items: stretch; }
  .order-again-bar .oa-btn { width: 100%; }
}

/* Menu search — sits above the category blocks, sticky under the header so it stays reachable
   while scrolling 89 items. Visually a peer of the item cards, not a hero element. */
.menu-search { position: sticky; top: 70px; z-index: 80; background: var(--bg);
  padding: var(--space-3) 0 var(--space-3); margin-bottom: var(--space-2); }
.menu-search input {
  width: 100%; font: inherit; font-size: 1rem;      /* 16px — smaller zooms on iOS focus */
  padding: var(--space-3) var(--space-4); color: var(--text-strong);
  background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-pill);
  box-shadow: var(--shadow-sm);
}
.menu-search input::placeholder { color: var(--text-muted); }
.menu-search input:focus { outline: 2px solid var(--accent); outline-offset: 1px; border-color: var(--accent); }
.menu-search-count:empty { display: none; }
.menu-search-count { margin: var(--space-2) 0 0; font-size: 0.88rem; color: var(--text-muted); }
.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; }

.cat-block { scroll-margin-top: 84px; margin-bottom: var(--space-7); }
.cat-block h2 { border-bottom: 2px solid var(--accent); padding-bottom: var(--space-2); display: inline-block; }
.cat-blurb { color: var(--text-muted); margin-top: calc(-1 * var(--space-1)); }
.item-list { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--space-3); }
.item-card {
  display: flex; gap: var(--space-4); justify-content: space-between; align-items: flex-start;
  background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius);
  padding: var(--space-4); box-shadow: var(--shadow-sm); transition: border-color var(--dur) var(--ease), transform var(--dur) var(--ease);
}
/* The name/description column must GROW, or flex sizes it to its content and space-between centres it —
   leaving every item name at a different x (a 93px ragged edge across the list). A hard left edge is the
   whole point of a scannable menu; it is what Deliveroo/Just Eat/Toast/Flipdish all do. min-width:0 lets
   long descriptions wrap instead of forcing the row wider. */
.item-main { flex: 1 1 auto; min-width: 0; }
.item-card:hover { border-color: oklch(78% 0.12 82 / 0.6); }
.item-card.is-sold-out { opacity: 0.6; }
.item-name { margin-bottom: var(--space-1); font-family: var(--font-head); }
.item-popular { display: inline-block; margin-bottom: var(--space-1); padding: 2px var(--space-2);
  font-size: 0.68rem; font-weight: 700; letter-spacing: 0.04em; text-transform: uppercase;
  color: var(--brand-deep); background: oklch(78% 0.12 82 / 0.92); border-radius: var(--radius-pill); }
.item-desc { color: var(--text-muted); font-size: 0.94rem; margin-bottom: var(--space-1); max-width: 52ch; }
.item-allergens { color: var(--text-muted); font-size: 0.78rem; text-transform: uppercase; letter-spacing: 0.04em; margin: 0; }
.item-side { display: flex; flex-direction: column; align-items: flex-end; gap: var(--space-2); flex-shrink: 0; min-width: 84px; }
.item-price { font-family: var(--font-head); font-weight: 600; font-size: 1.05rem; color: var(--text-strong); }
.sold-out-tag { font-size: 0.78rem; font-weight: 600; color: var(--danger); }
/* Optional dish photo (only a subset of items have one — cards without stay a clean text row). */
.item-thumb { flex-shrink: 0; width: 84px; height: 84px; object-fit: cover; border-radius: var(--radius-sm); background: var(--surface-2); align-self: center; }
.item-card.is-sold-out .item-thumb { filter: grayscale(0.7); }

/* ---- Basket panel ---------------------------------------------------------------------------------- */
.basket-scrim { position: fixed; inset: 0; background: oklch(20% 0.02 40 / 0.5); z-index: 140; }
.basket-panel {
  position: fixed; top: 0; right: 0; bottom: 0; width: min(400px, 92vw); z-index: 150;
  background: var(--surface); box-shadow: var(--shadow-md); display: flex; flex-direction: column;
  transform: translateX(100%); transition: transform var(--dur-panel) var(--ease); visibility: hidden;
}
.basket-panel.open { transform: translateX(0); visibility: visible; }
.basket-head { display: flex; align-items: center; justify-content: space-between; padding: var(--space-4) var(--space-5); border-bottom: 1px solid var(--border); }
.basket-head h2 { margin: 0; font-size: 1.3rem; }
/* 44px minimum — these were 29x31, the only way out of the dialog on touch now that Escape exists
   for keyboards. Grid-centred so the glyph stays optically centred in the larger hit area. */
.basket-close, .opts-close { display: grid; place-items: center; width: 44px; height: 44px;
  background: none; border: 0; font-size: 1.8rem; line-height: 1; color: var(--text-muted); cursor: pointer;
  border-radius: var(--radius-pill); }
.basket-close:hover, .opts-close:hover { background: var(--surface-2); color: var(--text-strong); }
.basket-body { flex: 1; overflow-y: auto; padding: var(--space-4) var(--space-5); }
/* Inside the slide-in panel the empty state sits on the panel's own ground — no card border, and
   less vertical padding than the full-page version, or it floats in a tall void. */
.empty-state-panel { border: 0; box-shadow: none; background: none; padding: var(--space-6) 0; margin-top: var(--space-5); }
.basket-line { display: flex; align-items: center; justify-content: space-between; gap: var(--space-3); padding: var(--space-3) 0; border-bottom: 1px solid var(--border); }
.bl-info { display: flex; flex-direction: column; gap: 2px; }
.bl-name { font-weight: 600; color: var(--text-strong); }
.bl-opts { font-size: 0.82rem; color: var(--text-muted); }
.bl-price { font-size: 0.88rem; color: var(--text-muted); }
.bl-qty { display: flex; align-items: center; gap: var(--space-2); }
.bl-qty button { width: 28px; height: 28px; border-radius: var(--radius-pill); border: 1px solid var(--border); background: var(--surface); color: var(--brand); font-size: 1.1rem; line-height: 1; cursor: pointer; }
.bl-qty button:hover { background: var(--surface-2); }
.basket-foot { padding: var(--space-4) var(--space-5); border-top: 1px solid var(--border); background: var(--surface-2); }
.basket-subtotal { display: flex; justify-content: space-between; font-size: 1.05rem; }
.basket-subtotal strong { font-family: var(--font-head); }
.basket-note { font-size: 0.82rem; color: var(--text-muted); margin: var(--space-2) 0 var(--space-3); }
.basket-checkout { width: 100%; }

/* ---- Options modal --------------------------------------------------------------------------------- */
.opts-modal { position: fixed; inset: 0; z-index: 160; display: grid; place-items: center; background: oklch(20% 0.02 40 / 0.5); padding: var(--space-4); }
.opts-dialog { position: relative; background: var(--surface); border-radius: var(--radius); box-shadow: var(--shadow-md); width: min(460px, 96vw); max-height: 86vh; overflow-y: auto; padding: var(--space-6); }
.opts-close { position: absolute; top: var(--space-3); right: var(--space-4); }
.opts-dialog h2 { padding-right: var(--space-6); }
.opt-group { border: 0; border-top: 1px solid var(--border); margin: 0; padding: var(--space-4) 0 var(--space-2); }
.opt-group legend { font-weight: 600; color: var(--text-strong); padding: 0; }
.opt-rule { display: inline-block; margin-left: var(--space-2); padding: 2px var(--space-2);
  border-radius: var(--radius-sm); background: var(--surface-2); color: var(--text-muted);
  font-size: 0.7rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.04em; vertical-align: 1px; }
.opt-rule.is-required { background: oklch(78% 0.12 82 / 0.28); color: var(--brand-deep); }
.opt-choice { display: flex; align-items: center; gap: var(--space-3); padding: var(--space-2) 0; cursor: pointer; }
.opt-choice input { accent-color: var(--brand); width: 18px; height: 18px; }
.opts-foot { display: flex; align-items: center; justify-content: space-between; gap: var(--space-4); margin-top: var(--space-4); padding-top: var(--space-4); border-top: 1px solid var(--border); }
.opts-price { font-family: var(--font-head); font-weight: 600; font-size: 1.2rem; }

/* ---- AI Mode (/ai) --------------------------------------------------------------------------------- */
/* A search surface, so the input IS the hero — full-width, pill, generous target, on the brand ground so
   it reads as a distinct mode rather than another menu page. Example prompts are tappable chips (the
   affordance every conversational-search UI uses); a bulleted link list reads as body copy, not controls. */
.ai-hero { background: var(--brand-deep); color: var(--on-brand); padding: var(--space-7) 0 var(--space-6); position: relative; }
.ai-hero::after { content: ""; position: absolute; left: 0; right: 0; bottom: 0; height: 6px;
  background: linear-gradient(90deg, var(--accent), transparent); opacity: 0.5; }
.ai-hero .wrap { max-width: 780px; }
.ai-hero h1 { color: var(--on-brand); margin-bottom: var(--space-2); }
.ai-hero .eyebrow { color: var(--accent); }
.ai-hero .lede { font-size: 1.05rem; opacity: 0.9; max-width: 56ch; margin-bottom: var(--space-5); }

.ai-form { display: flex; gap: var(--space-3); align-items: stretch; }
.ai-form input {
  flex: 1 1 auto; min-width: 0; font: inherit; font-size: 1rem;   /* 16px min — smaller zooms on iOS focus */
  padding: var(--space-3) var(--space-5); color: var(--text-strong);
  background: var(--surface); border: 1px solid oklch(100% 0 0 / 0.25); border-radius: var(--radius-pill);
}
.ai-form input::placeholder { color: var(--text-muted); }
.ai-form input:focus { outline: 2px solid var(--accent); outline-offset: 1px; border-color: var(--accent); }
.ai-form .btn-primary { flex: 0 0 auto; background: var(--accent); color: var(--brand-deep); }
.ai-form .btn-primary:hover { background: var(--on-brand); color: var(--brand-deep); }

.ai-chips { list-style: none; display: flex; flex-wrap: wrap; gap: var(--space-2); margin: var(--space-4) 0 0; padding: 0; }
.ai-chips a { display: inline-block; padding: var(--space-2) var(--space-4); font-size: 0.9rem;
  color: var(--on-brand); background: oklch(100% 0 0 / 0.10); border: 1px solid oklch(100% 0 0 / 0.22);
  border-radius: var(--radius-pill); transition: background var(--dur) var(--ease), color var(--dur) var(--ease); }
.ai-chips a:hover { background: var(--accent); color: var(--brand-deep); border-color: var(--accent); text-decoration: none; }

/* 780px must match .ai-hero .wrap above — at 820 the results sat 20px left of the input they answer,
   breaking the one vertical edge a search surface has to hold. */
.ai-results { padding-top: var(--space-6); padding-bottom: var(--space-8); max-width: 780px; }
.ai-reply { font-size: 1.05rem; color: var(--text-strong); margin-bottom: var(--space-4);
  padding-left: var(--space-4); border-left: 3px solid var(--accent); }
.ai-reply em { font-style: normal; color: var(--brand); font-weight: 600; }
.ai-foot { margin-top: var(--space-5); }

/* ---- Checkout -------------------------------------------------------------------------------------- */
.checkout { padding: var(--space-6) var(--space-4) var(--space-8); }
.back-link { display: inline-block; margin-bottom: var(--space-3); color: var(--text-muted); font-size: 0.9rem; }
.checkout-grid { display: grid; grid-template-columns: 1fr 360px; gap: var(--space-6); align-items: start; }
.checkout-main { display: grid; gap: var(--space-4); }
.ck-block { border: 1px solid var(--border); border-radius: var(--radius); background: var(--surface); padding: var(--space-5); box-shadow: var(--shadow-sm); }
.ck-block legend { font-family: var(--font-head); font-weight: 600; font-size: 1.1rem; color: var(--text-strong); padding: 0 var(--space-2); }
.ck-hint { font-size: 0.86rem; color: var(--text-muted); margin: var(--space-3) 0 0; }
.toggle { display: inline-flex; background: var(--surface-2); border-radius: var(--radius-pill); padding: 4px; gap: 4px; }
.toggle-opt { position: relative; padding: var(--space-2) var(--space-4); border-radius: var(--radius-pill); font-weight: 600; font-size: 0.92rem; cursor: pointer; color: var(--text-muted); }
.toggle-opt input { position: absolute; opacity: 0; inset: 0; cursor: pointer; }
.toggle-opt:has(input:checked) { background: var(--brand); color: var(--on-brand); }
.fld { display: block; margin-top: var(--space-3); }
.fld > span { display: block; font-weight: 600; font-size: 0.9rem; margin-bottom: var(--space-1); color: var(--text-strong); }
.fld em { font-weight: 400; color: var(--text-muted); font-style: normal; }
.fld input, .fld textarea, .fld select {
  width: 100%; padding: var(--space-3); font: inherit; color: var(--text-strong);
  border: 1px solid var(--border); border-radius: var(--radius-sm); background: var(--surface);
}
.fld input:focus, .fld textarea:focus, .fld select:focus { outline: 2px solid var(--accent); border-color: var(--accent); }
.zone-msg { font-size: 0.88rem; margin: var(--space-2) 0 0; min-height: 1.2em; }
.zone-msg.ok { color: var(--success); }
.zone-msg.bad { color: var(--danger); }
.zone-msg.is-checking { color: var(--text-muted); }   /* in-flight state — the quote is a round-trip */
/* Inline action inside a message (e.g. "Switch to collection") — a button, because it acts. */
.link-btn { background: none; border: 0; padding: 0; font: inherit; font-weight: 600;
  color: var(--brand); text-decoration: underline; cursor: pointer; }
.link-btn:hover { color: var(--brand-deep); }

.checkout-summary { position: sticky; top: 78px; border: 1px solid var(--border); border-radius: var(--radius); background: var(--surface); padding: var(--space-5); box-shadow: var(--shadow-sm); }
.checkout-summary h2 { font-size: 1.2rem; }
.sum-line { display: grid; grid-template-columns: 28px 1fr auto; gap: var(--space-2); padding: var(--space-2) 0; border-bottom: 1px solid var(--border); font-size: 0.92rem; }
.sum-line .sl-name em { color: var(--text-muted); font-style: normal; font-size: 0.85rem; }
.sl-price { font-variant-numeric: tabular-nums; }
.summary-totals { margin: var(--space-4) 0 0; display: grid; gap: var(--space-2); }
.summary-totals > div { display: flex; justify-content: space-between; }
.summary-totals dt, .summary-totals dd { margin: 0; }
.summary-totals .grand { font-family: var(--font-head); font-weight: 700; font-size: 1.25rem; color: var(--text-strong); border-top: 2px solid var(--accent); padding-top: var(--space-2); margin-top: var(--space-1); }
.min-msg { color: var(--warning); font-size: 0.88rem; margin: var(--space-3) 0 0; }
.ck-error { color: var(--danger); font-size: 0.9rem; margin: var(--space-3) 0 0; }
.btn-place { width: 100%; margin-top: var(--space-4); }
/* Shared empty state (checkout with no basket; renamed from the one-use .notice). A dashed box with a
   bare link read as an error, not a state — mark + line + one primary action is the leaders' pattern. */
.empty-state { display: grid; justify-items: center; gap: var(--space-2); background: var(--surface);
  border: 1px solid var(--border); border-radius: var(--radius); box-shadow: var(--shadow-sm);
  padding: var(--space-8) var(--space-5); text-align: center; color: var(--text-muted); }
.empty-state svg { color: var(--border); }
.empty-state h2 { margin: 0; font-size: 1.3rem; }
.empty-state p { margin: 0 0 var(--space-3); }

/* ---- Order / status -------------------------------------------------------------------------------- */
.order-page { padding: var(--space-7) var(--space-4) var(--space-8); }
.order-hero { text-align: center; margin-bottom: var(--space-6); }
.tick { width: 56px; height: 56px; margin: 0 auto var(--space-3); display: grid; place-items: center; border-radius: var(--radius-pill); background: var(--success); color: var(--on-brand); font-size: 1.6rem; }
/* --gold was never a defined token — both rules were silently rendering their raw-hex fallback, the only
   two colours in the sheet outside the OKLCH system. --accent-deep is the role that was meant. */
.tick.pending { background: var(--accent-deep); }
.await-banner { background: oklch(94% 0.05 85); border: 1px solid var(--accent-deep); border-radius: var(--radius);
  padding: var(--space-4) var(--space-5); margin-bottom: var(--space-5); color: var(--text-strong); line-height: 1.5; }
.order-again { text-align: center; margin-top: var(--space-6); }
.order-again .btn-primary { display: inline-block; }
.order-sub { color: var(--text-muted); }
.status-card { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); padding: var(--space-5); box-shadow: var(--shadow-sm); margin-bottom: var(--space-5); text-align: center; }
.status-label { display: inline-block; font-family: var(--font-head); font-weight: 600; font-size: 1.2rem; color: var(--brand); margin-bottom: var(--space-4); }
.status-track { list-style: none; display: flex; justify-content: space-between; margin: 0; padding: 0; counter-reset: step; gap: var(--space-2); }
.status-track li { flex: 1; position: relative; font-size: 0.82rem; color: var(--text-muted); padding-top: var(--space-5); }
.status-track li::before { content: ""; position: absolute; top: 6px; left: 50%; transform: translateX(-50%); width: 16px; height: 16px; border-radius: var(--radius-pill); background: var(--surface-2); border: 2px solid var(--border); }
.status-track li::after { content: ""; position: absolute; top: 13px; left: -50%; width: 100%; height: 2px; background: var(--border); z-index: -1; }
.status-track li:first-child::after { display: none; }
.status-track li.done { color: var(--text-strong); font-weight: 600; }
.status-track li.done::before { background: var(--success); border-color: var(--success); }
.status-track li.done::after { background: var(--success); }

.order-detail { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); padding: var(--space-5); box-shadow: var(--shadow-sm); }
.order-lines { list-style: none; margin: 0 0 var(--space-4); padding: 0; }
.order-lines li { display: grid; grid-template-columns: 32px 1fr auto; gap: var(--space-2); padding: var(--space-2) 0; border-bottom: 1px solid var(--border); }
.ol-name em { color: var(--text-muted); font-style: normal; font-size: 0.88rem; }
.ol-price { font-variant-numeric: tabular-nums; }
.order-totals { display: grid; gap: var(--space-1); margin: 0 0 var(--space-4); }
.order-totals > div { display: flex; justify-content: space-between; }
.order-totals dt, .order-totals dd { margin: 0; }
.order-totals .grand { font-family: var(--font-head); font-weight: 700; font-size: 1.2rem; color: var(--text-strong); border-top: 2px solid var(--accent); padding-top: var(--space-2); }
.order-meta { color: var(--text-muted); font-size: 0.94rem; border-top: 1px solid var(--border); padding-top: var(--space-4); }
.order-meta .pay-state { font-weight: 600; color: var(--text-strong); }
.email-note em { font-style: normal; font-size: 0.85rem; }
.order-ref { font-size: 0.82rem; color: var(--text-muted); margin-top: var(--space-4); }
.order-ref code { background: var(--surface-2); padding: 2px 6px; border-radius: var(--radius-sm); word-break: break-all; }

/* ---- Sign in / account ----------------------------------------------------------------------------- */
.auth-page { padding: var(--space-7) var(--space-4) var(--space-8); }
.auth-card { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius);
  box-shadow: var(--shadow-sm); padding: var(--space-5) var(--space-6); margin-bottom: var(--space-4); }
.auth-card h2 { font-size: 1.2rem; margin-bottom: var(--space-3); }
.auth-submit { width: 100%; margin-top: var(--space-4); }
.auth-alt { color: var(--text-muted); font-size: 0.94rem; }
.auth-error { color: var(--danger); background: oklch(96% 0.03 20); border: 1px solid var(--danger);
  border-radius: var(--radius-sm); padding: var(--space-3) var(--space-4); margin-bottom: var(--space-4); }
/* The dev-only magic-link display. Deliberately loud: it must never be mistaken for production UI. */
.auth-devlink { margin-top: var(--space-4); padding: var(--space-3) var(--space-4);
  background: oklch(96% 0.06 95); border: 1px dashed var(--accent-deep); border-radius: var(--radius-sm);
  font-size: 0.9rem; word-break: break-all; }

/* Social sign-in buttons. Deliberately NOT in each provider's brand colour: four saturated brand
   blocks would fight the lacquer-red identity and read as a plugin bolted on. One neutral treatment,
   the provider named in words — which is also what Deliveroo and Just Eat do. */
.oauth-list { display: grid; gap: var(--space-2); margin-bottom: var(--space-4); }
.oauth-btn { display: flex; align-items: center; gap: var(--space-4); padding: var(--space-3) var(--space-5);
  min-height: 52px;                                   /* comfortably over the 44px touch floor */
  background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-pill);
  color: var(--text-strong); font-weight: 600; font-size: 0.98rem; box-shadow: var(--shadow-sm);
  transition: border-color var(--dur) var(--ease), box-shadow var(--dur) var(--ease),
              transform var(--dur) var(--ease); }
.oauth-btn:hover { border-color: var(--accent-deep); box-shadow: var(--shadow-md);
  transform: translateY(-1px); text-decoration: none; }
.oauth-btn:active { transform: translateY(0); }
.oauth-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
/* The mark is the only colour on the button — the logos carry brand recognition while the row stays
   calm. No grey disc behind them: a plate under a logo is what made these look switched-off. */
.oauth-mark { display: grid; place-items: center; width: 22px; height: 22px; flex: 0 0 auto; }
.oauth-mark svg { display: block; }
/* Apple's mark is pure black, which disappears on a dark ground — invert it there. */
@media (prefers-color-scheme: dark) {
  .oauth-apple .oauth-mark svg path { fill: var(--text-strong); }
}
/* "or" divider with rules either side, drawn without extra markup. */
.oauth-or { display: flex; align-items: center; gap: var(--space-3); margin: 0 0 var(--space-4);
  color: var(--text-muted); font-size: 0.88rem; }
.oauth-or::before, .oauth-or::after { content: ""; flex: 1; height: 1px; background: var(--border); }

.account-dl { margin: 0; display: grid; gap: var(--space-2); }
.account-dl > div { display: flex; justify-content: space-between; gap: var(--space-4);
  padding-bottom: var(--space-2); border-bottom: 1px solid var(--border); }
.account-dl > div:last-child { border-bottom: 0; padding-bottom: 0; }
.account-dl dt { margin: 0; color: var(--text-muted); }
.account-dl dd { margin: 0; color: var(--text-strong); font-weight: 600; text-align: right; }
.account-dl em { font-weight: 400; color: var(--text-muted); font-style: normal; }

.account-orders { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--space-3); }
.account-orders li { display: grid; grid-template-columns: auto 1fr auto auto; gap: var(--space-3);
  align-items: baseline; padding-bottom: var(--space-3); border-bottom: 1px solid var(--border); }
.account-orders li:last-child { border-bottom: 0; padding-bottom: 0; }
.ao-when { color: var(--text-muted); font-size: 0.9rem; white-space: nowrap; }
.ao-what { color: var(--text); font-size: 0.94rem; }
.ao-total { font-weight: 600; font-variant-numeric: tabular-nums; }
/* The one ACTION in the nav, so it gets the house gold — used as a precious accent (thin outline,
   not a fill) exactly as DESIGN.md prescribes. Fills on hover to confirm it is a button-like target. */
/* .site-nav a is (0,1,1) and would beat a bare .nav-account (0,1,0) — the border went gold but the
   TEXT stayed on-brand white. Qualify the selector so the colour actually lands. */
.site-nav a.nav-account { font-weight: 700; color: var(--accent); border: 1px solid oklch(78% 0.12 82 / 0.55);
  border-radius: var(--radius-pill); padding: 5px var(--space-3); line-height: 1.2; white-space: nowrap; }
.site-nav a.nav-account:hover { background: var(--accent); color: var(--brand-deep); text-decoration: none;
  border-color: var(--accent); }
.site-nav a.nav-account:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

@media (max-width: 520px) {
  .account-orders li { grid-template-columns: 1fr auto; row-gap: var(--space-1); }
  .ao-what { grid-column: 1 / -1; }
}

/* ---- Info + policies ------------------------------------------------------------------------------- */
.info-page, .policy-page { padding: var(--space-7) var(--space-4) var(--space-8); }
/* align-items:start — the default stretch matched every card to the tallest in its row, leaving 116px of
   dead white under the Address card's last line. Cards size to their own content. */
.info-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: var(--space-4); margin-bottom: var(--space-5); align-items: start; }
.info-card { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); padding: var(--space-5); box-shadow: var(--shadow-sm); }
.info-card h2 { font-size: 1.15rem; }
.info-phone a { font-family: var(--font-head); font-weight: 600; font-size: 1.2rem; }
/* Brand gallery — real shopfront + interior photos (props: client's Google profile shots). */
.brand-gallery { margin: var(--space-6) 0 var(--space-5); }
.gallery-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: var(--space-3); }
.gallery-grid img { width: 100%; height: 220px; object-fit: cover; border-radius: var(--radius); box-shadow: var(--shadow-sm); }
.hours-table { width: 100%; border-collapse: collapse; font-size: 0.94rem; }
.hours-table th { text-align: left; font-weight: 500; color: var(--text-muted); padding: 4px 0; }
.hours-table td { text-align: right; padding: 4px 0; }
.hours-table tr.today th, .hours-table tr.today td { color: var(--brand); font-weight: 700; }
.hygiene-score { display: inline-grid; place-items: center; width: 34px; height: 34px; background: var(--success); color: var(--on-brand); border-radius: var(--radius-sm); font-family: var(--font-head); font-weight: 700; }
.zone-list { margin: 0; padding-left: var(--space-4); color: var(--text); }
.allergen-note { background: var(--surface-2); border-radius: var(--radius); padding: var(--space-5); }
.info-cta { margin-top: var(--space-5); }
.policy-stub { color: var(--text-muted); font-style: italic; }

/* ---- Footer ---------------------------------------------------------------------------------------- */
.site-foot { background: var(--brand-deep); color: var(--on-brand); margin-top: var(--space-8); padding: var(--space-7) 0 var(--space-6); }
.foot-brand { display: flex; align-items: center; gap: var(--space-3); font-family: var(--font-head); font-size: 1.2rem; margin-bottom: var(--space-5); }
.foot-cols { display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: var(--space-5); }
.foot-cols h4 { color: var(--accent); font-size: 0.9rem; text-transform: uppercase; letter-spacing: 0.08em; margin-bottom: var(--space-3); }
.foot-cols ul { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--space-2); }
.foot-cols a, .foot-cols p { color: var(--on-brand); opacity: 0.88; }
.foot-cols a:hover { color: var(--accent); text-decoration: none; opacity: 1; }
.foot-legal { margin-top: var(--space-6); font-size: 0.82rem; opacity: 0.7; }

/* ---- Responsive ------------------------------------------------------------------------------------ */
@media (max-width: 860px) {
  .menu-layout { grid-template-columns: 1fr; gap: var(--space-3); }
  /* Category chips WRAP to fit the screen — they used to scroll horizontally with the scrollbar hidden,
     so the last chip was sliced by the viewport edge with nothing to indicate more existed. Operator's
     call (2026-07-26): adjust to the screen, no slider.
     Sticky is dropped with it: 16 categories wrap to several rows, and pinning that block to the top
     would consume most of a phone viewport for the whole scroll. It scrolls away instead. */
  .cat-nav {
    position: static; flex-direction: row; flex-wrap: wrap; overflow: visible;
    gap: var(--space-2); background: var(--bg); padding: var(--space-2) 0;
    max-height: none; border-bottom: 1px solid var(--border);
  }
  .cat-link { white-space: nowrap; border-left: 0; border-bottom: 3px solid transparent; border-radius: var(--radius-pill); background: var(--surface); border: 1px solid var(--border); }
  .cat-link.active { border-color: var(--accent); border-bottom-color: var(--accent); }
  .checkout-grid { grid-template-columns: 1fr; }
  .checkout-summary { position: static; order: -1; }
  /* Every nav destination stays reachable on a phone. The old rule display:none'd all but the first,
     which stranded AI Mode (the differentiator) and Find us on the viewport most orders come from. */
  .site-nav { gap: var(--space-4); margin-left: auto; }
  .site-nav a { font-size: 0.95rem; white-space: nowrap; }
  .hero-grid { flex-direction: column; gap: var(--space-4); }
  .hero-content { margin-top: var(--space-6); }
  /* The 7-row hours table is a desktop side-panel; stacked full-width it ate the entire 844px fold and
     pushed every dish below it. Collapse to today's row + a link — the leaders' "Open until X · info"
     pattern. The full table still lives on /info and on desktop. */
  .hero-hours { align-self: stretch; min-width: 0; margin-top: 0;
    display: flex; align-items: baseline; flex-wrap: wrap; gap: var(--space-2) var(--space-3);
    padding: var(--space-3) var(--space-4); }
  .hero-hours h2 { margin: 0; }
  .hero-hours dl { flex: 1 1 auto; gap: 0; }
  .hero-hours .hh-row:not(.is-today) { display: none; }
  .hero-hours .hh-row.is-today { gap: var(--space-3); }
  .hero-hours-all { display: inline; }
}
@media (max-width: 520px) {
  /* At 390px the name column was only ~124px, so dish names wrapped to 2-3 lines ("Mini Prawn /
     Spring Rolls / (6)") and card heights ranged 118-174px — the mobile twin of the ragged-left-edge
     defect. Give the text the width back: smaller thumb, and the price/Add stack narrows. */
  .item-card { gap: var(--space-3); padding: var(--space-3); }
  .item-thumb { width: 64px; height: 64px; }
  .item-side { min-width: 0; gap: var(--space-1); }
  .item-name { font-size: 1rem; }
  .item-desc { font-size: 0.88rem; }
  .menu-hero { padding: var(--space-6) 0 var(--space-7); }
  /* Reclaim header width for the nav links on the narrowest phones — the mark carries the brand. */
  .brand-name { display: none; }
  .site-nav { gap: var(--space-3); }
  /* Drop the trailing words: "✨ AI Mode" → "✨ AI", "Find us" → "Find". The header row has a hard
     minimum (mark + 4 links + cart, none of which can shrink) and at full length that minimum was
     ~403px — wider than the phone, so the whole PAGE scrolled sideways. Abbreviating keeps every
     destination reachable, unlike hiding a link. */
  .nav-word { display: none; }
  .site-head .wrap { gap: var(--space-3); }
  /* Make room for "Returning? Sign in": the 金 mark links home (so Menu is covered) and Find us is in
     the footer. Hiding a link with a real alternative is not the old all-but-first bug. */
  .site-nav a:not(.nav-ai):not(.nav-account) { display: none; }
}
/* The oldest still-in-use viewport (320 = iPhone SE 1st gen). Eleven pixels over after the
   abbreviations, so the last of it comes from tightening the row rather than dropping a destination. */
@media (max-width: 360px) {
  .site-head .wrap { gap: var(--space-2); }
  .site-nav { gap: var(--space-2); }
  .site-nav a { font-size: 0.88rem; }
  .basket-btn { width: 38px; height: 38px; }
  .ai-form { flex-direction: column; }        /* full-width input + full-width button = two big targets */
  .ai-form .btn-primary { width: 100%; }
}

/* ---- Motion governor ------------------------------------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { transition-duration: 0.01ms !important; animation-duration: 0.01ms !important; }
  .basket-panel { transition: none; }
  .basket-bar { animation: none; }
}

/* Allergen notice — standing "ask us" line on the menu + checkout (owner-editable). Understated but visible. */
.allergen-notice { display: flex; gap: var(--space-2); align-items: baseline; margin: var(--space-4) 0 0;
  padding: var(--space-3) var(--space-4); border: 1px solid var(--border); border-left: 3px solid var(--accent-deep);
  border-radius: var(--radius-sm); background: var(--surface-2); color: var(--text-muted); font-size: 0.86rem; line-height: 1.5; }

/* dyom-reviews strip (client styling for the vendored module — tokens only, no new palette). */
.dyom-reviews { margin: var(--space-4) 0 0; }
.dyom-reviews-heading { font-family: var(--font-head); color: var(--text-strong); font-size: 1.45rem; margin: 0 0 var(--space-2); }
.dyom-reviews-agg { color: var(--text-muted); margin: 0 0 var(--space-5); font-size: 0.95rem; }
.dyom-reviews-agg strong { color: var(--text-strong); }
.dyom-stars { color: var(--accent-deep); letter-spacing: 2px; }
/* One row that AUTO-SCROLLS the tiles across (marquee); each card clickable → full-review modal.
   Tiles are rendered twice into one track; spacing via margin-right (not gap) so translateX(-50%)
   lands seamlessly. Pauses on hover; reduced-motion falls back to a static swipeable row (below). */
.dyom-reviews-rail { position: relative; overflow: hidden;
  /* fade tiles in/out at the left & right edges as the marquee scrolls */
  -webkit-mask-image: linear-gradient(to right, transparent, #000 7%, #000 93%, transparent);
  mask-image: linear-gradient(to right, transparent, #000 7%, #000 93%, transparent); }
.dyom-reviews-list { list-style: none; margin: 0; padding: 2px 0; display: flex;
  width: max-content; animation: dyom-marquee 60s linear infinite; }
.dyom-reviews-rail:hover .dyom-reviews-list { animation-play-state: paused; }
@keyframes dyom-marquee { from { transform: translateX(0); } to { transform: translateX(-50%); } }
.dyom-review-item { flex: 0 0 clamp(240px, 78vw, 320px); margin-right: var(--space-4); display: flex; }
.dyom-review-card { flex: 1; display: flex; flex-direction: column; width: 100%; text-align: left; font: inherit;
  color: inherit; cursor: pointer; background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius); padding: var(--space-4) var(--space-5);
  transition: border-color var(--dur) var(--ease), box-shadow var(--dur) var(--ease), transform var(--dur) var(--ease); }
.dyom-review-card:hover { border-color: var(--accent-deep); box-shadow: var(--shadow-md); transform: translateY(-2px); }
.dyom-review-card:focus-visible { outline: 2px solid var(--accent-deep); outline-offset: 2px; }
.dyom-review-meta { margin: 0 0 var(--space-2); font-size: 0.88rem; color: var(--text-muted); }
.dyom-review-author { font-weight: 600; color: var(--text); margin-left: var(--space-2); }
.dyom-review-when { margin-left: var(--space-1); }
.dyom-review-text { margin: 0; font-size: 0.94rem; line-height: 1.55; color: var(--text);
  display: -webkit-box; -webkit-line-clamp: 4; -webkit-box-orient: vertical; overflow: hidden; }
.dyom-review-more { margin-top: var(--space-3); font-size: 0.82rem; font-weight: 600; color: var(--brand); align-self: flex-start; }
@media (prefers-reduced-motion: reduce) {   /* no auto-motion: static, manually swipeable, no duplicates */
  .dyom-reviews-rail { overflow-x: auto; scroll-snap-type: x mandatory; }
  .dyom-reviews-list { animation: none; width: max-content; }
  .dyom-review-item { scroll-snap-align: start; }
  .dyom-review-item[aria-hidden="true"] { display: none; }
}
/* MUST stay AFTER the base .dyom-reviews-* rules above. A media query adds no specificity, so when this
   block lived up in the Responsive section the later base rules simply won and the mobile treatment was
   dead code — the marquee kept running, clipped at both edges. Order is the whole fix.
   A marquee caught mid-travel leaves a half-cut tile at BOTH edges on a narrow screen: it reads as broken
   layout rather than motion, and none of it is swipeable. Mobile gets the native pattern instead — a
   static, snapping, swipeable row (what reduced-motion already gets), flush from the first card. */
@media (max-width: 860px) {
  .dyom-reviews-rail { overflow-x: auto; scroll-snap-type: x mandatory;
    -webkit-mask-image: none; mask-image: none; }
  .dyom-reviews-list { animation: none; width: max-content; }
  .dyom-review-item { scroll-snap-align: start; }
  .dyom-review-item[aria-hidden="true"] { display: none; }   /* drop the duplicate marquee half */
}

.dyom-review-modal[hidden] { display: none; }
.dyom-review-modal { position: fixed; inset: 0; z-index: 1000; display: flex; align-items: center; justify-content: center;
  padding: var(--space-4); }
.dyom-review-modal-backdrop { position: absolute; inset: 0; background: oklch(20% 0.02 40 / 0.55); }
.dyom-review-modal-box { position: relative; z-index: 1; width: 100%; max-width: 560px; max-height: 85vh; overflow-y: auto;
  background: var(--surface); border-radius: var(--radius); padding: var(--space-6); box-shadow: var(--shadow-md); }
.dyom-review-modal-close { position: absolute; top: var(--space-3); right: var(--space-3); width: 32px; height: 32px; border: 0;
  border-radius: var(--radius-pill); background: var(--surface-2); color: var(--text); font-size: 1.3rem; line-height: 1; cursor: pointer; }
.dyom-review-modal-meta { font-size: 0.92rem; }
.dyom-review-modal-text { margin: var(--space-3) 0 0; font-size: 1.02rem; line-height: 1.62; color: var(--text); }
/* Hero rating cue (above-the-fold trust signal) — same module data, light-on-dark hero variant. */
.dyom-reviews-badge { display: inline-flex; align-items: center; flex-wrap: wrap; gap: var(--space-2);
  margin-top: var(--space-4); padding: var(--space-2) var(--space-4); background: oklch(0% 0 0 / 0.32);
  border-radius: var(--radius-pill); font-size: 1.05rem; backdrop-filter: blur(2px); }
.dyom-reviews-badge .dyom-stars { color: var(--accent); letter-spacing: 2px; font-size: 1.1rem; }
.dyom-reviews-badge strong { color: var(--on-brand); font-weight: 700; }   /* was #fff — last raw hex */
.dyom-reviews-badge-count { color: var(--on-brand); opacity: 0.9; }
.dyom-google-g { vertical-align: -0.15em; }
