/* ============================================================================
   Fairly - customer web
   ----------------------------------------------------------------------------
   Loads on top of /shared/fairly-ui.css. Everything here is either a
   customer-only composition (hero, browse, menu, tracking) or a restyle of a
   legacy class name that app.js already emits - those names are kept as the
   contract so the markup and the stylesheet can move independently.

   This is the warm-light surface: the customer is deciding what to eat, and
   that decision is made from photographs and prices on paper, not from an
   instrument panel. Ink is used deliberately - the order bar, the tracking
   header, the ledger band - not as the ground under everything.
   ========================================================================= */

/* ============================================================================
   Shell
   ========================================================================= */

#app { padding: 0; }

/* Every view but browse is a single centered column. Browse is full-bleed so
   its bands can run edge to edge. */
.view {
  max-width: var(--container);
  margin: 0 auto;
  padding: var(--sp-8) var(--sp-5) var(--sp-16);
}

/* A short settle when a view becomes visible, so moving between browse,
   a restaurant, checkout and confirmation reads as one place changing
   rather than four pages snapping in. Deliberately brief and from a
   visible-safe default (a failed script leaves the view fully shown, not
   hidden) - this is continuity, never a gate on content the customer is
   waiting for. */
.view:not([hidden]) {
  animation: view-enter var(--t-slow) var(--ease-out);
}
@keyframes view-enter {
  from { opacity: 0; transform: translateY(6px); }
  to   { opacity: 1; transform: none; }
}

#view-browse { max-width: none; padding: 0; }
@media (max-width: 640px) {
  .view { padding: var(--sp-5) var(--sp-4) var(--sp-12); }
}
.view[hidden] { display: none; }

/* --- Topbar: brand | location | search | actions -------------------------- */

.topbar {
  position: sticky;
  top: 0;
  z-index: 50;
  background: var(--chrome);
  backdrop-filter: saturate(170%) blur(14px);
  border-bottom: 1px solid var(--border);
}
.topbar-inner {
  max-width: var(--container-wide);
  margin: 0 auto;
  padding: 0 var(--sp-5);
  height: var(--topbar-h);
  display: flex;
  align-items: center;
  gap: var(--sp-4);
}

.brand {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  background: none;
  border: 0;
  padding: 0;
  cursor: pointer;
  color: var(--text-primary);
  flex-shrink: 0;
  border-radius: var(--r-sm);
}
.brand-mark { border-radius: 9px; display: block; width: 36px; height: 36px; }
.brand-word {
  font-family: var(--font-display);
  font-weight: 800;
  font-size: 21px;
  letter-spacing: -0.035em;
  color: var(--purple-ink);
}

/* Marketing-style primary nav: what the header looked like in the reference
   design, kept alongside (not instead of) the real location/search/account
   controls a working delivery app needs. */
.topbar-nav {
  display: flex;
  align-items: center;
  gap: var(--sp-1);
  flex-shrink: 0;
}
.topbar-nav-link {
  display: inline-flex;
  align-items: center;
  background: none;
  border: 0;
  color: var(--text-secondary);
  font-size: var(--fs-xs);
  font-weight: 600;
  text-decoration: none;
  cursor: pointer;
  padding: var(--sp-2) var(--sp-3);
  border-radius: var(--r-sm);
  white-space: nowrap;
  transition: background var(--t-fast) var(--ease), color var(--t-fast) var(--ease);
}
.topbar-nav-link:hover { color: var(--text-primary); background: var(--surface-2); }

.order-now-btn {
  display: flex;
  align-items: center;
  height: 42px;
  padding: 0 var(--sp-5);
  background: var(--purple);
  color: var(--void);
  border: 0;
  border-radius: var(--r-pill);
  font-weight: 700;
  font-size: var(--fs-xs);
  cursor: pointer;
  white-space: nowrap;
  transition: background var(--t-fast) var(--ease), transform var(--t-fast) var(--ease);
}
.order-now-btn:hover { background: var(--purple-hover, var(--purple-ink)); color: var(--text-on-accent-hover); }
/* The press treatment now lives with every other button in fairly-ui.css. */

/* Below ~1150px there isn't room for nav links, location, search and actions
   all in one row - the nav links are the most dispensable (every one of them
   is also reachable from the page body or the mobile tab bar), so they go
   first, before the functional controls start shrinking or hiding. */
@media (max-width: 1150px) {
  .topbar-nav { display: none; }
}
@media (max-width: 860px) {
  .order-now-btn { display: none; }
}

/* Delivery location: a real control, not a label. Opens the address sheet,
   which writes the same four fields checkout validates. */
.location-btn {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  height: 42px;
  padding: 0 var(--sp-4) 0 var(--sp-3);
  background: none;
  border: 1px solid var(--border);
  border-radius: var(--r-pill);
  color: var(--text-primary);
  cursor: pointer;
  flex-shrink: 0;
  max-width: 230px;
  transition: background var(--t-fast) var(--ease), border-color var(--t-fast) var(--ease);
}
.location-btn:hover { background: var(--surface-2); border-color: var(--border-strong); }
.location-btn-icon { color: var(--purple-ink); display: flex; }
.location-btn-chev { color: var(--text-muted-strong); display: flex; }
.location-btn-text { display: flex; flex-direction: column; align-items: flex-start; min-width: 0; line-height: 1.2; }
.location-btn-label { font-size: var(--fs-micro); color: var(--text-muted-strong); font-weight: 500; }
.location-btn-text strong {
  font-size: var(--fs-xs);
  font-weight: 700;
  max-width: 170px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.topbar-search { flex: 1; max-width: 430px; margin-left: auto; }
/* Wide enough for the nav links (>=1150px, see .topbar-nav below): the header
   search competes with them for the same room, and "Order food" plus the
   hero's own search already cover that need, so it steps aside there rather
   than forcing everything into an illegibly narrow strip. */
@media (min-width: 1150px) {
  .topbar-search { display: none; }
}
.topbar-searchbig { height: 42px; padding: 0 var(--sp-4); box-shadow: none; background: var(--surface-2); }
.topbar-searchbig input { font-size: var(--fs-sm); }
/* The hero carries a search field of its own on browse; two of them stacked in
   the same viewport is one too many. */
.topbar-search { transition: opacity var(--t-base) var(--ease); }
body[data-hero-visible="true"] .topbar-search { opacity: 0; pointer-events: none; }

.topbar-actions { display: flex; align-items: center; gap: var(--sp-1); margin-left: auto; flex-shrink: 0; }
.text-btn {
  background: none;
  border: 0;
  color: var(--text-secondary);
  font-size: var(--fs-xs);
  font-weight: 600;
  cursor: pointer;
  padding: var(--sp-2) var(--sp-3);
  border-radius: var(--r-sm);
  transition: background var(--t-fast) var(--ease), color var(--t-fast) var(--ease);
}
.text-btn:hover { color: var(--text-primary); background: var(--surface-2); }
.user-chip { font-size: var(--fs-sm); color: var(--text-secondary); display: flex; align-items: center; gap: var(--sp-1); }
.user-chip strong { color: var(--text-primary); font-weight: 600; }

.cart-btn {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  height: 42px;
  padding: 0 var(--sp-3) 0 var(--sp-4);
  background: var(--text-primary);
  color: var(--bg);
  border: 0;
  border-radius: var(--r-pill);
  font-weight: 700;
  font-size: var(--fs-xs);
  cursor: pointer;
  transition: transform var(--t-fast) var(--ease), background var(--t-fast) var(--ease);
}
.cart-btn:hover { background: var(--purple-ink); color: #fff; }
.cart-btn:active { transform: translateY(1px); }
.cart-count {
  min-width: 22px;
  height: 22px;
  padding: 0 6px;
  display: grid;
  place-items: center;
  border-radius: var(--r-pill);
  background: var(--purple);
  color: var(--void);
  font-size: var(--fs-micro);
  font-weight: 800;
  font-variant-numeric: tabular-nums;
}

/* The one bit of feedback the cart most needs: when something is added, the
   count reacts instead of silently ticking up. JS retriggers `.is-bumped`
   on the badge (and the mobile order-bar count) each time the total rises -
   see renderCart in app.js. Transform-only, so it can't shift layout, and
   the global reduced-motion rule flattens it to an instant, honest count. */
.cart-count.is-bumped,
.ff-orderbar-count.is-bumped {
  animation: cart-pop 300ms var(--ease-out);
}
@keyframes cart-pop {
  0%   { transform: scale(1); }
  35%  { transform: scale(1.32); }
  100% { transform: scale(1); }
}

.cart-btn[data-empty="true"] {
  background: var(--surface-2);
  color: var(--text-primary);
  box-shadow: inset 0 0 0 1px var(--border);
}
.cart-btn[data-empty="true"]:hover { background: var(--surface-3); color: var(--text-primary); }
.cart-btn[data-empty="true"] .cart-count { background: transparent; color: var(--text-muted-strong); }

@media (max-width: 980px) {
  .topbar-search { display: none; }
}
@media (max-width: 720px) {
  .topbar-inner { padding: 0 var(--sp-4); gap: var(--sp-2); }
  .location-btn { max-width: 150px; }
  .location-btn-text strong { max-width: 96px; }
  .brand-word { display: none; }
}

.back-link {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  background: none;
  border: 0;
  color: var(--text-secondary);
  font-size: var(--fs-xs);
  font-weight: 600;
  cursor: pointer;
  padding: var(--sp-2) var(--sp-3) var(--sp-2) var(--sp-2);
  margin-bottom: var(--sp-5);
  border-radius: var(--r-sm);
  transition: background var(--t-fast) var(--ease), color var(--t-fast) var(--ease);
}
.back-link:hover { color: var(--text-primary); background: var(--surface-2); }


/* ============================================================================
   Hero

   Reference is app/order/page.tsx, not the marketing homepage's hero - a
   compact headline and straight into search + categories + the real grid.
   No photo panel, no gradient wash, no floating card: v0 doesn't have one on
   this page, and the actual discovery grid right below already carries the
   food-forward weight.
   ========================================================================= */

.hero {
  border-bottom: 1px solid var(--border);
}
.hero-inner {
  max-width: var(--container-discovery);
  margin: 0 auto;
  padding: var(--sp-8) var(--sp-5) var(--sp-6);
}

/* font-serif text-3xl sm:text-4xl font-semibold tracking-tight, matched. */
.hero-title {
  font-family: var(--font-serif);
  font-weight: 600;
  font-size: clamp(30px, 3vw, 36px);
  line-height: 1.15;
  letter-spacing: -0.01em;
  color: var(--text-primary);
}
/* mt-2 max-w-xl text-muted-foreground, matched (max-w-xl = 576px). */
.hero-sub {
  margin-top: 8px;
  font-size: var(--fs-body);
  line-height: 1.5;
  color: var(--text-secondary);
  max-width: 576px;
}

.hero-search-card {
  margin-top: var(--sp-6);
  width: 100%;
  max-width: 100%;
  padding: 12px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 28.8px;
  box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px -1px rgba(0, 0, 0, 0.1);
  transition: border-color var(--t-fast) var(--ease), box-shadow var(--t-fast) var(--ease);
}
/* The search field sits inside a second, padded white card - focusing it
   used to ring just the inner pill, which read as a small purple box
   floating inside a bigger one. The ring now goes on the outer card instead,
   and the inner pill stays plain, so focus reads as one shape lighting up. */
.hero-search-card:focus-within {
  border-color: var(--purple-ink);
  box-shadow: 0 0 0 3px var(--purple-tint), 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px -1px rgba(0, 0, 0, 0.1);
}
.hero-search { box-shadow: none; background: var(--surface-2); max-width: none; }
.hero-search:focus-within { border-color: var(--border); box-shadow: none; }
.hero-search input { font-size: 14px; }

.hero-cats { margin-top: var(--sp-5); }

/* Plain text pills, not icon tiles: the first question ("what kind of food")
   reads fastest as words you can scan in one line, matched to the reference
   design's flat chip row. */
.ff-cat-pill {
  display: inline-flex;
  align-items: center;
  height: 38px;
  padding: 0 var(--sp-4);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-pill);
  color: var(--text-secondary);
  font-size: var(--fs-sm);
  font-weight: 600;
  white-space: nowrap;
  cursor: pointer;
  transition: background var(--t-fast) var(--ease), color var(--t-fast) var(--ease), border-color var(--t-fast) var(--ease);
}
.ff-cat-pill:hover { border-color: var(--border-strong); color: var(--text-primary); }
.ff-cat-pill.is-active {
  background: var(--purple);
  border-color: var(--purple);
  color: var(--void);
}

@media (max-width: 640px) {
  .hero-inner { padding: var(--sp-5) var(--sp-4) var(--sp-5); }
  .hero-title { font-size: 26px; }
  .hero-search-card { margin-top: var(--sp-4); }
}

/* The fee sentence the hero panel used to close with now closes the band that
   actually makes the argument. */
.ledger-band-foot {
  display: flex;
  align-items: flex-start;
  gap: var(--sp-2);
  margin-top: var(--sp-5);
  font-size: var(--fs-xs);
  line-height: 1.55;
  color: var(--text-secondary);
}
.ledger-band-foot [data-icon-done] { color: var(--green); flex: none; display: flex; margin-top: 2px; }

/* ----------------------------------------------------------------------------
   Menu - dish rows, not a photo-first card grid: matched to the reference
   design and to DESIGN.md's own "Dish row" spec (text left, a small square
   photo right, an inline quantity control) rather than the shared
   component's photo-top card default (shared/fairly-ui.css .ff-dish is
   only ever used here, so this fully overrides it rather than forking a
   parallel class name).
   ------------------------------------------------------------------------- */
.ff-menu { grid-template-columns: 1fr; gap: 0; }

.ff-dish {
  display: flex;
  flex-direction: row;
  align-items: center;
  gap: var(--sp-4);
  width: 100%;
  padding: var(--sp-5) 0;
  border-bottom: 1px solid var(--border);
  border-radius: 0;
}
.ff-dish:last-child { border-bottom: 0; }
.ff-dish:hover { transform: none; }
.ff-dish-open {
  display: flex;
  flex-direction: row;
  align-items: center;
  gap: var(--sp-4);
  flex: 1;
  min-width: 0;
  background: none;
  border: 0;
  padding: 0;
  color: inherit;
  font: inherit;
  text-align: left;
  cursor: pointer;
}
.ff-dish-open:hover .ff-dish-name { color: var(--purple-ink); }
.ff-dish-open:hover .ff-dish-media > img { transform: scale(1.05); }
.ff-dish-text { flex: 1; min-width: 0; padding: 0; }
.ff-dish-media { order: 0; width: 116px; flex-shrink: 0; box-shadow: none; border-radius: var(--r-md); }
.ff-dish-controls { flex-shrink: 0; }

.ff-dish-quickadd {
  width: 38px;
  height: 38px;
  display: grid;
  place-items: center;
  border-radius: var(--r-pill);
  border: 1px solid var(--border);
  background: var(--surface);
  color: var(--text-primary);
  cursor: pointer;
  transition: background var(--t-fast) var(--ease), color var(--t-fast) var(--ease), transform var(--t-fast) var(--ease);
}
.ff-dish-quickadd:hover { background: var(--purple); color: var(--text-on-accent); border-color: transparent; }
/* A real press: the button gives under the tap before the item lands in the
   cart, so the add feels physical rather than instantaneous. */
.ff-dish-quickadd:active { transform: scale(0.88); }
.ff-dish-quickadd:active { transform: scale(0.92); }
.ff-stepper-sm button { width: 32px; height: 32px; }
.ff-stepper-sm .ff-stepper-value { min-width: 22px; font-size: var(--fs-sm); }

@media (max-width: 640px) {
  .ff-dish { padding: var(--sp-4) 0; gap: var(--sp-3); }
  .ff-dish-media { width: 80px; }
  .ff-dish-desc { font-size: var(--fs-xs); }
}

/* ============================================================================
   Browse
   ========================================================================= */

/* ----------------------------------------------------------------------------
   Footer - brand + tagline, three link columns (Order/Company/Support), then
   a bottom bar with copyright and the trust line. Customer-web-only markup
   (shared/fairly-ui.css's .site-footer stays the plain legal-links row the
   other three surfaces use); every link here reuses a real nav target
   already elsewhere in the app rather than pointing anywhere new.
   ------------------------------------------------------------------------- */
.ff-footer {
  margin-top: var(--sp-10);
  padding-top: var(--sp-10);
  border-top: 1px solid var(--border);
}
.ff-footer-top {
  display: grid;
  grid-template-columns: minmax(240px, 1.6fr) repeat(3, 1fr);
  gap: var(--sp-8);
}
.ff-footer-brand { display: flex; flex-direction: column; gap: var(--sp-4); }
/* The source file is a flat JPG on white, not a transparent PNG - multiply
   blends that white into the footer's own warm ground instead of sitting on
   top of it as a visible rectangle, so the mark can run large without
   looking like a pasted-in logo card. */
.ff-footer-logo {
  height: 116px;
  width: auto;
  flex-shrink: 0;
  align-self: flex-start;
  margin: -14px 0 -10px -10px;
  mix-blend-mode: multiply;
}
.ff-footer-tagline {
  font-size: var(--fs-sm);
  line-height: 1.55;
  color: var(--text-secondary);
  max-width: 320px;
}
.ff-footer-col { display: flex; flex-direction: column; gap: var(--sp-1); min-width: 0; }
.ff-footer-col h3 {
  font-family: var(--font-display);
  font-size: var(--fs-sm);
  font-weight: 800;
  color: var(--text-primary);
  margin-bottom: var(--sp-2);
}
.ff-footer-col a,
.ff-footer-col button {
  background: none;
  border: 0;
  padding: var(--sp-1) 0;
  color: var(--text-secondary);
  font-size: var(--fs-sm);
  text-align: left;
  cursor: pointer;
  width: fit-content;
  min-height: 32px;
  display: flex;
  align-items: center;
}
.ff-footer-col a:hover, .ff-footer-col button:hover { color: var(--purple-ink); }

.ff-footer-bottom {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: var(--sp-3);
  margin-top: var(--sp-10);
  padding-top: var(--sp-6);
  border-top: 1px solid var(--border);
  font-size: var(--fs-xs);
  color: var(--text-muted-strong);
}
.ff-footer-legal {
  display: flex;
  gap: var(--sp-4);
  margin-top: var(--sp-3);
  padding-bottom: var(--sp-8);
}
.ff-footer-legal a { font-size: var(--fs-xs); color: var(--text-muted-strong); }
.ff-footer-legal a:hover { color: var(--text-primary); }

@media (max-width: 860px) {
  .ff-footer-top { grid-template-columns: 1fr 1fr; }
  .ff-footer-brand { grid-column: 1 / -1; }
}
@media (max-width: 560px) {
  .ff-footer-top { grid-template-columns: 1fr; gap: var(--sp-6); }
  .ff-footer-bottom { flex-direction: column; align-items: flex-start; }
}

.browse-body { padding: var(--sp-8) 0 var(--sp-16); }
/* Discovery content earns more width than the rest of the site (task:
   "SEARCH/DISCOVERY CONTENT can use a wider layout") - scoped to this page
   alone rather than widening the shared .ff-container class other
   surfaces/pages also use at 1140px. On a large monitor this is the fix
   for "two narrow cards in a sea of empty margin": the restaurant grid
   itself is already fluid (auto-fill, see .ff-rgrid) and simply gets more
   room to place more columns in. */
#view-browse .ff-container { max-width: var(--container-discovery); }
.browse-h2 {
  font-family: var(--font-serif);
  font-weight: 600;
  font-size: var(--fs-display);
  letter-spacing: -0.005em;
  color: var(--text-primary);
}
.browse-cats { margin-bottom: var(--sp-12); }
.browse-cats .ff-rail { margin-top: var(--sp-5); }

/* Cuisine filter's two homes: a horizontal rail under the hero search on a
   phone (the default - see .hero-cats above), a sticky vertical list beside
   the grid at >=860px. #cuisine-chips itself physically moves between them
   (updateFilterLayoutMode, app.js); everything here just styles whichever
   home currently holds it. */
.browse-results-layout { display: flex; flex-direction: column; gap: var(--sp-6); }
.browse-results { flex: 1; min-width: 0; }
.browse-filters-sidebar { display: none; }
.browse-filters-title {
  font-family: var(--font-display);
  font-weight: 800;
  font-size: var(--fs-sm);
  color: var(--text-primary);
  margin-bottom: var(--sp-4);
}
@media (min-width: 860px) {
  .browse-results-layout { flex-direction: row; align-items: flex-start; gap: var(--sp-10); }
  .browse-filters-sidebar {
    display: block;
    width: 200px;
    flex-shrink: 0;
    position: sticky;
    top: calc(var(--topbar-h) + var(--sp-5));
  }
  .browse-filters-sidebar #cuisine-chips {
    flex-direction: column;
    align-items: stretch;
    overflow: visible;
    gap: var(--sp-2);
    margin-inline: 0;
    padding-inline: 0;
    padding-bottom: 0;
  }
  .browse-filters-sidebar .ff-cat-pill { width: 100%; justify-content: flex-start; }
}

/* A launch market with only a few restaurants: cap the grid instead of
   stretching one or two cards across the full discovery width (see the
   filtered.length <= 3 toggle in renderRestaurantGrid, app.js). auto-fit
   collapses the tracks a auto-fill would otherwise leave empty, so the
   card(s) size up to a deliberate width and sit at the start of a bounded
   measure - read as curated, not like an empty database with one row. */
.ff-rgrid.ff-rgrid-few {
  grid-template-columns: repeat(auto-fit, minmax(272px, 380px));
  max-width: 1180px;
}
.browse-results-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--sp-4);
  flex-wrap: wrap;
  margin-bottom: var(--sp-6);
}
.results-meta {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  font-size: var(--fs-xs);
  color: var(--text-muted-strong);
}
@media (max-width: 640px) {
  .browse-body { padding: var(--sp-8) 0 var(--sp-12); }
  .browse-h2 { font-size: var(--fs-h1); }
  .browse-cats { margin-bottom: var(--sp-8); }
}

/* --- How it works: a sequence, drawn as one, not three identical cards ---- */

.how-steps {
  list-style: none;
  margin: var(--sp-10) 0 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--sp-8);
  counter-reset: how;
  position: relative;
}
/* The rule that turns three columns into one flow. */
.how-steps::before {
  content: '';
  position: absolute;
  left: 30px;
  right: 30px;
  top: 30px;
  height: 1px;
  background: var(--border);
}
.how-step { position: relative; }
.how-step-art {
  position: relative;
  z-index: 1;
  width: 60px;
  height: 60px;
  border-radius: var(--r-pill);
  display: grid;
  place-items: center;
  color: #fff;
  margin-bottom: var(--sp-5);
  box-shadow: 0 0 0 6px var(--surface);
}
.how-step-art[data-appetite="1"] { background: linear-gradient(148deg, var(--appetite-1a), var(--appetite-1b)); }
.how-step-art[data-appetite="2"] { background: linear-gradient(148deg, var(--appetite-2a), var(--appetite-2b)); }
.how-step-art[data-appetite="5"] { background: linear-gradient(148deg, var(--appetite-5a), var(--appetite-5b)); }
.how-step h3 {
  font-size: var(--fs-h2);
  letter-spacing: -0.02em;
  margin-bottom: var(--sp-2);
  counter-increment: how;
}
.how-step h3::before {
  content: counter(how) ' / ';
  color: var(--text-muted);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}
.how-step p { color: var(--text-secondary); font-size: var(--fs-sm); line-height: 1.6; max-width: 34ch; }
@media (max-width: 800px) {
  .how-steps { grid-template-columns: 1fr; gap: var(--sp-8); margin-top: var(--sp-8); }
  .how-steps::before { left: 30px; right: auto; top: 30px; bottom: 30px; width: 1px; height: auto; }
  .how-step { padding-left: 84px; }
  .how-step-art { position: absolute; left: 0; top: 0; margin: 0; }
}

/* --- The open ledger band ------------------------------------------------- */

.ledger-band-inner {
  display: grid;
  grid-template-columns: minmax(0, 0.9fr) minmax(0, 1.1fr);
  gap: var(--sp-16);
  align-items: center;
}
.ledger-table {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--fs-sm);
}
.ledger-table th, .ledger-table td { text-align: left; padding: var(--sp-4) var(--sp-3); }
.ledger-table thead th {
  font-size: var(--fs-micro);
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-muted-strong);
  border-bottom: 1px solid var(--border);
  padding-bottom: var(--sp-2);
}
.ledger-table tbody th { font-weight: 700; color: var(--text-primary); }
.ledger-table tbody td { color: var(--text-secondary); }
.ledger-table tbody tr { border-bottom: 1px solid var(--border); }
.ledger-table tbody tr:last-child { border-bottom: 0; }
.ledger-table th:last-child, .ledger-table td:last-child { text-align: right; font-variant-numeric: tabular-nums; }
.ledger-zero { color: var(--green); font-weight: 700; }
.ledger-ours { color: var(--purple); font-weight: 800; font-family: var(--font-display); }
.ledger-table tbody tr.is-ours th { color: var(--purple); }
@media (max-width: 900px) {
  .ledger-band-inner { grid-template-columns: 1fr; gap: var(--sp-8); }
}
@media (max-width: 560px) {
  .ledger-table { font-size: var(--fs-xs); }
  .ledger-table th, .ledger-table td { padding: var(--sp-3) var(--sp-2); }
}

/* --- Partners ------------------------------------------------------------- */

.partners-grid {
  display: grid;
  grid-template-columns: minmax(0, 1.25fr) minmax(0, 1fr);
  gap: var(--sp-6);
  align-items: stretch;
}
.partner-card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-2xl);
  padding: var(--sp-8);
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--sp-3);
  box-shadow: var(--shadow-card);
}
.partner-art {
  width: 64px;
  height: 64px;
  border-radius: var(--r-lg);
  display: grid;
  place-items: center;
  color: #fff;
  margin-bottom: var(--sp-2);
}
.partner-art[data-appetite="1"] { background: linear-gradient(148deg, var(--appetite-1a), var(--appetite-1b)); }
.partner-art[data-appetite="5"] { background: linear-gradient(148deg, var(--appetite-5a), var(--appetite-5b)); }
.partner-card h3 { font-size: var(--fs-h1); letter-spacing: -0.025em; }
.partner-card p { color: var(--text-secondary); font-size: var(--fs-sm); line-height: 1.6; flex: 1; }
.partner-card .ff-btn { margin-top: var(--sp-3); text-decoration: none; }
.partner-card-lead h3 { font-size: var(--fs-display); }
@media (max-width: 800px) {
  .partners-grid { grid-template-columns: 1fr; }
  .partner-card { padding: var(--sp-6); }
  .partner-card-lead h3 { font-size: var(--fs-h1); }
}

/* --- Restaurant grid skeletons: shaped like the real card ---------------- */

.skeleton-card { display: flex; flex-direction: column; gap: var(--sp-3); }
.skeleton-card-media { aspect-ratio: 16 / 10; border-radius: var(--r-lg); }
.skeleton-card-body { display: flex; flex-direction: column; gap: var(--sp-2); }

.empty-state { color: var(--text-secondary); padding: var(--sp-10) 0; text-align: center; font-size: var(--fs-sm); }

/* ============================================================================
   Restaurant storefront

   Reference pattern: one bounded, rounded photo and a vertically-centered
   identity column, side by side - not a full-bleed band with a card
   overlapping it. Get the customer to the menu quickly.
   ========================================================================= */

#view-restaurant { max-width: var(--container); margin: 0 auto; padding: var(--sp-8) var(--sp-5) var(--sp-16); }
#view-restaurant .back-link { margin-bottom: var(--sp-5); }
/* #view-restaurant is an ID selector (specificity beats .view's own mobile
   padding rule regardless of media-query nesting), so its mobile padding
   needs its own explicit override here rather than relying on .view's. */
@media (max-width: 640px) {
  #view-restaurant { padding: var(--sp-5) var(--sp-4) var(--sp-12); }
}

/* Kept deliberately compact - a customer who already tapped in from the
   browse card (which carried the big photo) is here for the menu, not a
   second look at the restaurant's identity. A small mark beside the name
   is enough to confirm "yes, this is the place," in a fraction of the
   height the old full-width photo band used to take before any dish was
   visible. */
.restaurant-top {
  display: flex;
  align-items: center;
  gap: var(--sp-4);
  margin-bottom: var(--sp-5);
}
.restaurant-photo { position: relative; flex-shrink: 0; width: 72px; }
.restaurant-hero-media {
  width: 72px;
  height: 72px;
  aspect-ratio: 1 / 1;
  border-radius: var(--r-lg);
  border: 1px solid var(--border);
  box-shadow: var(--shadow-sm);
}
.restaurant-hero-media .ff-media-initial { font-size: 26px; }

.restaurant-identity { display: flex; flex-direction: column; min-width: 0; }
.restaurant-identity h1 {
  font-family: var(--font-serif);
  font-weight: 600;
  font-size: 21px;
  letter-spacing: -0.01em;
  line-height: 1.2;
  text-wrap: balance;
}

@media (max-width: 640px) {
  .restaurant-top { gap: var(--sp-3); margin-bottom: var(--sp-4); }
  .restaurant-photo { width: 56px; }
  .restaurant-hero-media { width: 56px; height: 56px; }
  .restaurant-hero-media .ff-media-initial { font-size: 20px; }
  .restaurant-identity h1 { font-size: var(--fs-h2); }
}

/* Facts row: each fact is its own chip, so the customer scans rather than
   parses one long dot-separated line. */
.restaurant-facts { display: flex; align-items: center; gap: var(--sp-2) var(--sp-4); flex-wrap: wrap; margin-top: var(--sp-1); }
.restaurant-fact {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  font-size: var(--fs-xs);
  color: var(--text-secondary);
}
.restaurant-fact strong { color: var(--text-primary); font-weight: 600; }
.restaurant-fact [data-icon] { color: var(--text-muted-strong); display: flex; }

.restaurant-body { padding-top: 0; }

/* A 1140px reading-width container is right for a phone/laptop, but a
   three-column menu (rail | menu | cart, see .menu-layout below) still has
   real, useful work to do with the rest of a large monitor - more comfortable
   dish rows, a cart that isn't squeezed. */
@media (min-width: 1440px) {
  #view-restaurant { max-width: 1400px; }
}
@media (min-width: 1680px) {
  #view-restaurant { max-width: var(--container-discovery); }
}

.closed-notice { margin-top: var(--sp-5); }

.menu-layout { display: grid; grid-template-columns: 200px 1fr; gap: var(--sp-10); align-items: start; }
/* Pinned to the second track explicitly: .category-rail hides itself
   (rail.hidden, see app.js loadMenu) whenever a restaurant has fewer than
   two categories, and an unplaced grid item auto-flows into whatever track
   the hidden one vacated - collapsing the menu grid into the 200px rail
   column instead of the 1fr one meant for it. Reset to auto below 860px,
   where .menu-layout drops to a single track and an explicit column 2
   would otherwise force a phantom second column back into existence. */
#menu-items { grid-column: 2; }
@media (max-width: 860px) {
  .menu-layout { grid-template-columns: 1fr; gap: var(--sp-4); }
  #menu-items { grid-column: auto; }
}

.category-rail {
  /* Deliberately NOT align-self:stretch. A sticky grid item that's
     stretched to fill its whole row has no room left to move within its
     own box, so it scrolls away with the content instead of sticking
     (confirmed live: stretching this to match the menu column's height
     made it track scroll 1:1 like a static element). Left at the
     container's default align-items:start, the rail's box stays its own
     small content height, and CSS Grid still gives position:sticky the
     full row height as headroom to stick within - which is what actually
     keeps it pinned for the whole scroll. */
  position: sticky;
  top: calc(var(--topbar-h) + var(--sp-5));
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.category-rail button {
  background: none;
  border: none;
  color: var(--text-secondary);
  text-align: left;
  padding: var(--sp-3);
  border-radius: var(--r-md);
  font-size: var(--fs-sm);
  font-weight: 600;
  cursor: pointer;
  min-height: 42px;
  transition: background var(--t-fast) var(--ease), color var(--t-fast) var(--ease);
}
.category-rail button:hover { background: var(--surface-2); color: var(--text-primary); }
.category-rail button.is-active { background: var(--purple-tint); color: var(--purple-ink); }

@media (max-width: 860px) {
  /* On mobile the rail becomes a sticky horizontal scroller directly under the
     topbar - always reachable while scrolling a long menu. */
  .category-rail {
    position: sticky;
    top: var(--topbar-h);
    z-index: 20;
    flex-direction: row;
    overflow-x: auto;
    scrollbar-width: none;
    gap: var(--sp-2);
    background: var(--chrome);
    backdrop-filter: saturate(160%) blur(14px);
    margin: 0 calc(var(--sp-4) * -1);
    padding: var(--sp-3) var(--sp-4);
    border-bottom: 1px solid var(--border);
  }
  .category-rail::-webkit-scrollbar { display: none; }
  .category-rail button {
    flex-shrink: 0;
    border: 1px solid var(--border);
    border-radius: var(--r-pill);
    padding: var(--sp-2) var(--sp-4);
    min-height: 44px;
    font-size: var(--fs-xs);
    background: var(--surface);
  }
  .category-rail button.is-active { border-color: var(--purple-ink); background: var(--purple-tint); }
}

/* Group heading (Food / Drinks / Desserts) - genuinely one step above a
   category in the type scale, not a small-caps label sitting on top of it.
   Only rendered when a restaurant has actually organized categories into
   groups. Generous space above (a real section break), none of that
   borrowed by the category title that follows it. */
.menu-group-title {
  font-family: var(--font-display);
  font-weight: 800;
  font-size: var(--fs-display);
  letter-spacing: -0.03em;
  color: var(--text-primary);
  margin: var(--sp-14) 0 var(--sp-4);
  padding-bottom: var(--sp-3);
  border-bottom: 2px solid var(--border);
  scroll-margin-top: calc(var(--topbar-h) + var(--sp-16));
}
.menu-group-title:first-child { margin-top: 0; }
@media (max-width: 640px) {
  /* Stays a size step above the category title's own mobile size
     (fs-h1, see .ff-menu-section-title) rather than shrinking to match it. */
  .menu-group-title { font-size: 27px; margin-top: var(--sp-10); }
}

.menu-featured-note {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  font-size: var(--fs-xs);
  color: var(--text-secondary);
  margin-bottom: var(--sp-2);
}
.menu-featured-note [data-icon] { color: var(--purple-ink); display: flex; }

.item-featured-star { color: var(--purple-ink); display: inline-flex; }
.item-soldout {
  font-size: var(--fs-micro);
  color: var(--text-muted-strong);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.06em;
}

/* --- Group ordering (unchanged behavior, warm surfaces) ------------------ */

.group-order-banner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-3);
  flex-wrap: wrap;
  background: var(--purple-tint);
  border: 1px solid var(--purple);
  border-radius: var(--r-md);
  padding: var(--sp-3) var(--sp-4);
  margin-bottom: var(--sp-4);
  font-size: var(--fs-sm);
}

.group-order-participant {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  padding: var(--sp-4);
  margin-bottom: var(--sp-3);
}
.group-order-participant-name { font-weight: 700; margin-bottom: var(--sp-2); }
.group-order-item-row {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--sp-3);
  padding: var(--sp-2) 0;
}
.group-order-item-row + .group-order-item-row { border-top: 1px solid var(--border); }
.group-order-item-mods { color: var(--text-secondary); font-size: var(--fs-xs); }
.group-order-item-remove { background: none; border: none; color: var(--text-secondary); cursor: pointer; font-size: var(--fs-xs); text-decoration: underline; flex-shrink: 0; }

/* ============================================================================
   Item sheet (modal on desktop, bottom sheet on mobile)
   ========================================================================= */

.item-modal, .cart-drawer, .scrim { position: fixed; inset: 0; }
.scrim {
  background: rgba(26, 20, 32, 0.44);
  backdrop-filter: blur(3px);
  z-index: 60;
  animation: ff-fade var(--t-base) var(--ease);
}
.item-modal {
  z-index: 70;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--sp-5);
  pointer-events: none;
}
.item-modal-card {
  pointer-events: auto;
  background: var(--surface);
  border: 1px solid var(--border-strong);
  border-radius: var(--r-xl);
  max-width: 480px;
  width: 100%;
  max-height: min(88vh, 780px);
  display: flex;
  flex-direction: column;
  overflow: hidden;
  box-shadow: var(--shadow-lg);
  animation: ff-modal-in var(--t-base) var(--ease);
  position: relative;
}
@media (max-width: 640px) {
  .item-modal { align-items: flex-end; padding: 0; }
  .item-modal-card {
    max-width: none;
    max-height: 92vh;
    border-radius: var(--r-xl) var(--r-xl) 0 0;
    border-bottom: none;
    animation: ff-sheet-in var(--t-base) var(--ease);
  }
}

/* Scroll region + pinned footer, so the add-to-cart action never scrolls away
   on a long modifier list. */
.item-modal-scroll { overflow-y: auto; padding: var(--sp-6); flex: 1; -webkit-overflow-scrolling: touch; }
.item-modal-foot {
  padding: var(--sp-4) var(--sp-6);
  padding-bottom: calc(var(--sp-4) + env(safe-area-inset-bottom));
  border-top: 1px solid var(--border);
  background: var(--surface);
  flex-shrink: 0;
  display: flex;
  align-items: center;
  gap: var(--sp-4);
}
@media (max-width: 640px) {
  .item-modal-scroll { padding: var(--sp-5); }
  .item-modal-foot { padding: var(--sp-4) var(--sp-5); padding-bottom: calc(var(--sp-4) + env(safe-area-inset-bottom)); }
}

.icon-btn {
  background: var(--surface-2);
  border: 1px solid var(--border);
  color: var(--text-primary);
  width: 36px;
  height: 36px;
  border-radius: var(--r-md);
  cursor: pointer;
  font-size: 17px;
  line-height: 1;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  transition: border-color var(--t-fast) var(--ease), background var(--t-fast) var(--ease);
}
.icon-btn:hover { border-color: var(--border-strong); background: var(--surface-3); }
.item-modal-close {
  position: absolute;
  top: var(--sp-4);
  right: var(--sp-4);
  z-index: 2;
  background: var(--chrome);
  backdrop-filter: saturate(150%) blur(10px);
  box-shadow: var(--shadow-sm);
}

.modal-item-image {
  width: calc(100% + var(--sp-12));
  margin: calc(var(--sp-6) * -1) calc(var(--sp-6) * -1) var(--sp-5);
  max-height: 200px;
  object-fit: cover;
}
@media (max-width: 640px) {
  .modal-item-image { width: calc(100% + var(--sp-10)); margin: calc(var(--sp-5) * -1) calc(var(--sp-5) * -1) var(--sp-4); }
}

.modal-title { font-size: var(--fs-h1); font-family: var(--font-display); font-weight: 800; letter-spacing: -0.02em; margin-bottom: var(--sp-2); padding-right: var(--sp-10); }
.modal-desc { color: var(--text-secondary); font-size: var(--fs-sm); margin-bottom: var(--sp-5); line-height: 1.55; }
.modal-base-price { font-size: var(--fs-body); font-weight: 700; color: var(--text-primary); margin-bottom: var(--sp-5); font-variant-numeric: tabular-nums; }

/* --- Modifier groups ----------------------------------------------------- */

.modifier-group {
  margin-bottom: var(--sp-5);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  overflow: hidden;
}
.modifier-group-head {
  padding: var(--sp-3) var(--sp-4);
  background: var(--surface-2);
  border-bottom: 1px solid var(--border);
}
.modifier-group-title {
  font-size: var(--fs-sm);
  font-weight: 700;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-3);
}
.modifier-group-sub { font-size: var(--fs-2xs); color: var(--text-muted-strong); margin-top: 2px; }

/* Required vs optional is stated in words, not implied by an asterisk. */
.req-tag {
  font-size: var(--fs-micro);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  padding: 3px var(--sp-2);
  border-radius: 5px;
  white-space: nowrap;
}
.req-tag.is-required { background: var(--purple-tint); color: var(--purple-ink); }
.req-tag.is-optional { background: var(--surface-3); color: var(--text-muted-strong); }
/* Turns green with a check the moment the group's requirement is satisfied,
   so the customer can see at a glance what is still outstanding. */
.req-tag.is-satisfied { background: var(--success-tint); color: var(--success); }
.req-tag.is-satisfied::before { content: '\2713 '; }

.modifier-row {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  padding: var(--sp-3) var(--sp-4);
  border-bottom: 1px solid var(--border);
  font-size: var(--fs-sm);
  cursor: pointer;
  min-height: 48px;
  transition: background var(--t-fast) var(--ease);
}
.modifier-row:last-child { border-bottom: none; }
.modifier-row:hover { background: var(--surface-2); }
.modifier-row input { accent-color: var(--purple); width: 18px; height: 18px; margin: 0; flex-shrink: 0; cursor: pointer; }
.modifier-row-label { flex: 1; min-width: 0; }
.modifier-price { color: var(--text-secondary); font-size: var(--fs-xs); font-variant-numeric: tabular-nums; white-space: nowrap; }
/* Selected rows are tinted as well as checked - two signals, not one. */
.modifier-row:has(input:checked) { background: var(--purple-tint); }
.modifier-row:has(input:checked) .modifier-price { color: var(--purple-ink); }
/* A disabled option (group limit reached) explains itself rather than just
   going dead under the cursor. */
.modifier-row:has(input:disabled) { opacity: 0.45; cursor: not-allowed; }
.modifier-row:has(input:disabled):hover { background: none; }

.qty-row { display: flex; align-items: center; justify-content: space-between; gap: var(--sp-4); }
.qty-controls { display: flex; align-items: center; gap: var(--sp-1); background: var(--surface-2); border: 1px solid var(--border); border-radius: var(--r-md); padding: 3px; }
.qty-controls button {
  background: none;
  border: none;
  color: var(--text-primary);
  width: 36px;
  height: 36px;
  border-radius: 7px;
  cursor: pointer;
  font-size: 17px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  transition: background var(--t-fast) var(--ease);
}
.qty-controls button:hover:not(:disabled) { background: var(--surface-3); }
.qty-controls button:disabled { color: var(--text-muted); cursor: not-allowed; }
.qty-value { min-width: 28px; text-align: center; font-weight: 700; font-variant-numeric: tabular-nums; }

.primary-btn {
  background: var(--purple);
  color: var(--text-on-accent);
  border: none;
  border-radius: var(--r-md);
  padding: 0 var(--sp-5);
  min-height: 48px;
  font-size: var(--fs-sm);
  font-weight: 700;
  cursor: pointer;
  width: 100%;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--sp-2);
  transition: background var(--t-fast) var(--ease), transform var(--t-fast) var(--ease);
}
.primary-btn:hover:not(:disabled) { background: var(--purple-hover); color: var(--text-on-accent-hover); }
.primary-btn:active:not(:disabled) { transform: translateY(1px); }
.primary-btn:disabled { background: var(--surface-2); color: var(--text-muted); cursor: not-allowed; }

.secondary-btn {
  background: var(--surface-2);
  border: 1px solid var(--border);
  color: var(--text-primary);
  border-radius: var(--r-md);
  padding: 0 var(--sp-4);
  min-height: 40px;
  font-size: var(--fs-xs);
  font-weight: 600;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--sp-2);
  transition: border-color var(--t-fast) var(--ease), background var(--t-fast) var(--ease);
}
.secondary-btn:hover:not(:disabled) { border-color: var(--border-strong); background: var(--surface-3); }
.secondary-btn:disabled { color: var(--text-muted); cursor: not-allowed; }

/* Add-to-cart shows the live total so the price is settled before the tap,
   not discovered in the cart afterwards. */
.add-btn-price { margin-left: auto; font-variant-numeric: tabular-nums; }
.add-btn-label { font-weight: 700; }

/* ============================================================================
   Cart drawer
   ========================================================================= */

.cart-drawer {
  inset: 0 0 0 auto;
  width: 400px;
  max-width: 92vw;
  background: var(--surface);
  border-left: 1px solid var(--border-strong);
  z-index: 70;
  display: flex;
  flex-direction: column;
  box-shadow: var(--shadow-lg);
  animation: ff-drawer-in var(--t-base) var(--ease);
}
@media (max-width: 640px) {
  .cart-drawer {
    inset: auto 0 0 0;
    width: 100%;
    max-width: none;
    max-height: 88vh;
    border-left: none;
    border-top: 1px solid var(--border-strong);
    border-radius: var(--r-xl) var(--r-xl) 0 0;
    animation: ff-sheet-in var(--t-base) var(--ease);
  }
}

/* Persistent cart column (wide viewports, restaurant page only) - see
   updateCartLayoutMode() in app.js, which moves the real #cart-drawer node
   into #menu-layout-cart-slot at this width and moves it back below it.
   Browse food, see the running order, and reach checkout without ever
   opening an overlay - the point of a "persistent panel," not a bigger
   drawer. Selector only matches once the drawer actually lives inside the
   slot, so this never leaks into the normal overlay styling above. */
.menu-layout-cart-slot { display: none; }
@media (min-width: 1180px) {
  .menu-layout { grid-template-columns: 200px 1fr 380px; }
  #menu-items { grid-column: 2; }
  .menu-layout-cart-slot { display: block; grid-column: 3; }
  #menu-layout-cart-slot .cart-drawer {
    position: sticky;
    inset: auto;
    top: calc(var(--topbar-h) + var(--sp-5));
    width: 100%;
    max-width: none;
    max-height: calc(100vh - var(--topbar-h) - var(--sp-10));
    border: 1px solid var(--border-strong);
    border-radius: var(--r-xl);
    box-shadow: var(--shadow-md);
    animation: none;
  }
  /* Nothing to dismiss when the cart is never an overlay in the first place. */
  #menu-layout-cart-slot #cart-close,
  body.cart-persistent #scrim,
  body.cart-persistent .ff-orderbar {
    display: none !important;
  }
}
/* The wider .restaurant-body container above (>=1680px) gives this row more
   to work with - a wider cart column and rail rather than just a bigger
   empty margin beside the same fixed-width grid. */
@media (min-width: 1680px) {
  .menu-layout { grid-template-columns: 240px 1fr 420px; gap: var(--sp-12); }
}
.cart-drawer-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-3);
  padding: var(--sp-5);
  border-bottom: 1px solid var(--border);
  flex-shrink: 0;
}
.cart-drawer-header h2 {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  font-family: var(--font-serif);
  font-weight: 600;
  font-size: 26px;
  letter-spacing: -0.005em;
}
.cart-drawer-header h2 [data-icon], .cart-drawer-header h2 [data-icon-done] { color: var(--purple-ink); }
.cart-drawer-items { flex: 1; overflow-y: auto; padding: 0 var(--sp-5); }
.cart-line { padding: var(--sp-4) 0; border-bottom: 1px solid var(--border); }
.cart-line:last-child { border-bottom: none; }
.cart-line-top { display: flex; justify-content: space-between; gap: var(--sp-3); font-size: var(--fs-sm); font-weight: 600; margin-bottom: var(--sp-1); }
.cart-line-top .cart-line-price { font-variant-numeric: tabular-nums; white-space: nowrap; }
.cart-line-qty {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 22px;
  height: 22px;
  padding: 0 6px;
  border-radius: 6px;
  background: var(--purple-tint);
  color: var(--purple-ink);
  font-size: var(--fs-micro);
  font-weight: 800;
  margin-right: var(--sp-2);
  font-variant-numeric: tabular-nums;
}
.cart-line-mods { font-size: var(--fs-2xs); color: var(--text-secondary); margin-bottom: var(--sp-2); line-height: 1.5; }
.cart-line-remove {
  font-size: var(--fs-2xs);
  color: var(--text-muted-strong);
  background: none;
  border: none;
  cursor: pointer;
  padding: var(--sp-1) 0;
  font-weight: 600;
}
.cart-line-remove:hover { color: var(--danger); }
.cart-drawer-footer {
  padding: var(--sp-5);
  padding-bottom: calc(var(--sp-5) + env(safe-area-inset-bottom));
  border-top: 1px solid var(--border);
  flex-shrink: 0;
}
.cart-subtotal-row {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  font-size: var(--fs-body);
  font-weight: 700;
  margin-bottom: var(--sp-2);
}
.cart-subtotal-row .ff-num, .cart-subtotal-row span:last-child { font-variant-numeric: tabular-nums; }
.cart-footnote { font-size: var(--fs-2xs); color: var(--text-muted-strong); margin-bottom: var(--sp-4); }
.cart-empty { color: var(--text-secondary); font-size: var(--fs-sm); padding: var(--sp-12) 0; text-align: center; }

/* The order bar is a floating pill (see .ff-orderbar in the shared system) -
   one thumb-tap to the cart at every width, and it never becomes a full-width
   slab that covers the last dish on the menu. */
body.has-cart-bar { padding-bottom: 84px; }

/* ============================================================================
   Checkout
   ========================================================================= */

.checkout-layout { display: grid; grid-template-columns: 1fr 340px; gap: var(--sp-8); align-items: start; }
@media (max-width: 860px) { .checkout-layout { grid-template-columns: 1fr; gap: var(--sp-5); } }

/* Progressive checkout: one section open at a time. Numbered sections give an
   obvious top-to-bottom order; collapsing a finished one into a one-line
   summary keeps the customer's attention on the single task in front of them
   instead of a wall of equally-weighted fields. Every section stays tappable
   to reopen and edit - this is a focus aid, not a locked wizard. */
.checkout-section {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-xl);
  padding: var(--sp-5);
  margin-bottom: var(--sp-4);
  transition: border-color var(--t-fast) var(--ease), background var(--t-fast) var(--ease), box-shadow var(--t-fast) var(--ease);
}
.checkout-section.is-open { border-color: var(--purple); box-shadow: 0 0 0 1px var(--purple); }
.checkout-section-head {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  margin-bottom: var(--sp-4);
  cursor: pointer;
  min-height: 32px;
  -webkit-tap-highlight-color: transparent;
}
.checkout-section.is-collapsed .checkout-section-head { margin-bottom: 0; }
.checkout-step-num {
  width: 30px; height: 30px;
  border-radius: 50%;
  background: var(--purple-tint);
  color: var(--purple-ink);
  font-size: var(--fs-xs);
  font-weight: 800;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  transition: background var(--t-fast) var(--ease), color var(--t-fast) var(--ease);
}
.checkout-section.is-open .checkout-step-num { background: var(--purple); color: var(--text-on-accent); box-shadow: 0 0 0 4px var(--purple-tint); }
.checkout-section.is-done .checkout-step-num { background: var(--purple); color: var(--text-on-accent); }
.checkout-section.is-done .checkout-step-num::before { content: '\2713'; }
.checkout-section.is-done .checkout-step-num span { display: none; }
.checkout-section-head h2 { font-size: var(--fs-h3); flex: 1; }
.checkout-section-chev {
  flex-shrink: 0;
  color: var(--text-muted-strong);
  transition: transform var(--t-fast) var(--ease);
}
.checkout-section.is-open .checkout-section-chev { transform: rotate(180deg); }

.checkout-section-summary {
  font-size: var(--fs-sm);
  color: var(--text-secondary);
  line-height: 1.5;
}
.checkout-section-summary strong { color: var(--text-primary); font-weight: 600; }
.checkout-section-body { overflow: hidden; }
.checkout-section.is-collapsed .checkout-section-body { display: none; }
.checkout-section:not(.is-collapsed) .checkout-section-summary { display: none; }

.checkout-continue-btn { margin-top: var(--sp-2); width: auto; padding: 0 var(--sp-6); }

.field-group { margin-bottom: var(--sp-4); }
.field-group:last-child { margin-bottom: 0; }
.field-label { display: block; font-size: var(--fs-xs); font-weight: 600; color: var(--text-primary); margin-bottom: var(--sp-2); }

.segmented { display: flex; gap: var(--sp-2); flex-wrap: wrap; }
.segmented-option {
  flex: 1;
  min-width: fit-content;
  background: var(--surface-2);
  border: 1px solid var(--border);
  color: var(--text-secondary);
  border-radius: var(--r-md);
  padding: var(--sp-3) var(--sp-4);
  min-height: 44px;
  font-size: var(--fs-xs);
  font-weight: 600;
  cursor: pointer;
  white-space: nowrap;
  transition: all var(--t-fast) var(--ease);
}
.segmented-option:hover { border-color: var(--border-strong); color: var(--text-primary); }
.segmented-option.is-active {
  background: var(--purple-tint);
  border-color: var(--purple-ink);
  color: var(--purple-ink);
  font-weight: 700;
}

input[type="email"], input[type="tel"], input[type="text"], input[type="password"], select {
  width: 100%;
  min-height: 44px;
  background: var(--surface-2);
  border: 1px solid var(--border);
  color: var(--text-primary);
  border-radius: var(--r-md);
  padding: var(--sp-3) var(--sp-4);
  font-size: var(--fs-sm);
  transition: border-color var(--t-fast) var(--ease);
}
input::placeholder { color: var(--text-muted); }
input[type="email"]:hover, input[type="tel"]:hover, input[type="text"]:hover, input[type="password"]:hover, select:hover { border-color: var(--border-strong); }
input[type="email"]:focus, input[type="tel"]:focus, input[type="text"]:focus, input[type="password"]:focus, select:focus {
  outline: none;
  border-color: var(--purple);
  box-shadow: 0 0 0 3px var(--purple-tint);
}
input[type="range"] { width: 100%; accent-color: var(--purple); }

/* Invalid state carries a border color AND an inline message below it (see
   .field-error), so the error is never signalled by color alone. */
input[type="email"][aria-invalid="true"],
input[type="tel"][aria-invalid="true"],
input[type="text"][aria-invalid="true"],
input[type="password"][aria-invalid="true"] { border-color: var(--danger); }
.checkbox-row.is-invalid input[type="checkbox"] { outline: 2px solid var(--danger); outline-offset: 2px; }

.password-field { position: relative; }
.password-field input { padding-right: 46px; }
.password-toggle-btn {
  position: absolute;
  right: 5px;
  top: 50%;
  transform: translateY(-50%);
  width: 34px;
  height: 34px;
  background: none;
  border: none;
  cursor: pointer;
  color: var(--text-muted-strong);
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: var(--r-sm);
}
.password-toggle-btn:hover { color: var(--text-primary); background: var(--surface-3); }

.address-row { display: grid; grid-template-columns: 1fr 72px 100px; gap: var(--sp-2); margin-top: var(--sp-2); }
@media (max-width: 420px) { .address-row { grid-template-columns: 1fr 1fr; } }
.address-autocomplete { position: relative; }
.address-suggestions {
  position: absolute;
  top: calc(100% + 4px);
  left: 0; right: 0;
  z-index: 30;
  margin: 0;
  padding: var(--sp-1);
  list-style: none;
  background: var(--surface-3);
  border: 1px solid var(--border-strong);
  border-radius: var(--r-md);
  box-shadow: var(--shadow-md);
  max-height: 260px;
  overflow-y: auto;
}
.address-suggestion {
  padding: var(--sp-3);
  border-radius: var(--r-sm);
  font-size: var(--fs-xs);
  color: var(--text-secondary);
  cursor: pointer;
  min-height: 42px;
  display: flex;
  align-items: center;
}
.address-suggestion:hover, .address-suggestion.is-active { background: var(--purple-tint); color: var(--text-primary); }

.notes-input {
  width: 100%;
  background: var(--surface-2);
  border: 1px solid var(--border);
  color: var(--text-primary);
  border-radius: var(--r-md);
  padding: var(--sp-3) var(--sp-4);
  font-size: var(--fs-sm);
  font-family: inherit;
  margin-top: var(--sp-2);
  resize: vertical;
  line-height: 1.5;
  min-height: 70px;
}
.notes-input:focus { outline: none; border-color: var(--purple); box-shadow: 0 0 0 3px var(--purple-tint); }

.info-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 16px;
  height: 16px;
  border-radius: 50%;
  border: 1px solid var(--border-strong);
  background: none;
  color: var(--text-muted-strong);
  font-size: 10px;
  font-weight: 700;
  font-family: var(--font-body);
  cursor: pointer;
  margin-left: var(--sp-1);
  padding: 0;
  line-height: 1;
  flex-shrink: 0;
}
.info-icon:hover { border-color: var(--purple-ink); color: var(--purple-ink); }
.info-popover {
  font-size: var(--fs-2xs);
  color: var(--text-secondary);
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  padding: var(--sp-3);
  margin: var(--sp-1) 0 var(--sp-2);
  line-height: 1.5;
}

.card-element-box {
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  padding: var(--sp-4);
  min-height: 48px;
}
.card-element-box.StripeElement--focus { border-color: var(--purple); box-shadow: 0 0 0 3px var(--purple-tint); }
.card-element-box.StripeElement--invalid { border-color: var(--danger); }

.payment-note { font-size: var(--fs-2xs); color: var(--text-muted-strong); line-height: 1.55; margin-bottom: var(--sp-4); }

#wallet-pay-button { min-height: 40px; }
.wallet-pay-divider {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  margin-top: var(--sp-4);
  color: var(--text-muted-strong);
  font-size: var(--fs-2xs);
}
.wallet-pay-divider::before,
.wallet-pay-divider::after {
  content: '';
  flex: 1;
  height: 1px;
  background: var(--border);
}

.field-error {
  display: flex;
  align-items: flex-start;
  gap: var(--sp-2);
  color: var(--danger);
  font-size: var(--fs-xs);
  margin-top: var(--sp-3);
  line-height: 1.45;
}
.field-error::before {
  content: '!';
  flex-shrink: 0;
  width: 15px; height: 15px;
  margin-top: 1px;
  border-radius: 50%;
  background: var(--danger);
  color: var(--text-on-status);
  font-size: 10px;
  font-weight: 800;
  display: flex;
  align-items: center;
  justify-content: center;
}

.checkbox-row {
  display: flex;
  align-items: flex-start;
  gap: var(--sp-3);
  font-size: var(--fs-xs);
  color: var(--text-secondary);
  cursor: pointer;
  line-height: 1.5;
  padding: var(--sp-1) 0;
}
.checkbox-row input[type="checkbox"] { accent-color: var(--purple); width: 18px; height: 18px; margin: 0; flex-shrink: 0; margin-top: 1px; cursor: pointer; }
.checkbox-row a { color: var(--purple); }

.promo-row { display: flex; gap: var(--sp-2); }
.promo-row input { flex: 1; }
.promo-row .secondary-btn { min-height: 44px; flex-shrink: 0; }

.rewards-card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  padding: var(--sp-4);
  margin-top: var(--sp-5);
}
.rewards-balance { font-size: var(--fs-h3); font-weight: 800; color: var(--text-primary); }

/* --- Alcohol delivery notices --------------------------------------------- */
/* Shared by the checkout warning (.ff-alert-warn) and the tracking-page
   reminder (.ff-alert-info) - same body/title/list treatment either way. */

.alcohol-alert-body p { margin: 0 0 var(--sp-2); }
.alcohol-alert-body p:last-of-type { margin-bottom: 0; }
.alcohol-alert-title {
  text-transform: uppercase;
  letter-spacing: 0.04em;
  font-weight: 800;
  font-size: var(--fs-xs);
  margin-bottom: var(--sp-2) !important;
}
.alcohol-alert-list {
  list-style: none;
  margin: var(--sp-2) 0 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.alcohol-alert-list li { padding-left: var(--sp-5); position: relative; }
.alcohol-alert-list li::before {
  content: '\2713';
  position: absolute;
  left: 0;
  font-weight: 800;
  color: var(--warn);
}

/* --- Order summary ------------------------------------------------------- */

.order-summary {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  padding: var(--sp-5);
  position: sticky;
  top: calc(var(--topbar-h) + var(--sp-4));
}
@media (max-width: 860px) { .order-summary { position: static; } }

.summary-title { font-size: var(--fs-h3); margin-bottom: var(--sp-4); }
.summary-items { margin-bottom: var(--sp-4); padding-bottom: var(--sp-4); border-bottom: 1px solid var(--border); }
.summary-item-row {
  display: flex;
  justify-content: space-between;
  gap: var(--sp-3);
  font-size: var(--fs-xs);
  padding: var(--sp-2) 0;
  color: var(--text-secondary);
}
.summary-item-row .summary-item-name { color: var(--text-primary); }
.summary-item-row .summary-item-mods { display: block; color: var(--text-muted-strong); font-size: var(--fs-micro); margin-top: 2px; }

.summary-line {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: var(--sp-4);
  font-size: var(--fs-sm);
  padding: var(--sp-2) 0;
  color: var(--text-secondary);
}
.summary-line > span:last-child { color: var(--text-primary); font-variant-numeric: tabular-nums; white-space: nowrap; }
.summary-line.is-credit > span:last-child { color: var(--success); }
.summary-line.total {
  color: var(--text-primary);
  font-family: var(--font-display);
  font-weight: 800;
  font-size: var(--fs-h2);
  border-top: 1px solid var(--border-strong);
  margin-top: var(--sp-3);
  padding-top: var(--sp-4);
}
.summary-line.total > span:last-child { font-size: var(--fs-h1); }

/* ============================================================================
   Order tracking / confirmation
   ----------------------------------------------------------------------------
   Status leads. The map is supporting evidence, collapsed by default and only
   offered once a driver actually exists to look at.
   ========================================================================= */

.confirmation { max-width: 620px; margin: 0 auto; }
/* Desktop: the status card and its supporting detail get a main column, and
   the three help/action cards (need a hand, something wrong, hungry again -
   never the thing being decided at this point, so never a floating cart bar
   the way browse/menu get one) get a narrower sidebar beside it. Matched to
   the reference's tracking-page split. Below 900px both stack in one
   column, sidebar second, in source order. */
@media (min-width: 900px) {
  .confirmation { max-width: var(--container); }
  .track-layout {
    display: grid;
    grid-template-columns: 1fr 340px;
    gap: var(--sp-8);
    align-items: start;
  }
}

.track-main { min-width: 0; }
.track-sidebar { display: flex; flex-direction: column; gap: var(--sp-5); }

/* Generic white card used for every secondary block on this page (driver,
   proof of delivery, and the three sidebar cards) - one shape so the page
   reads as a set of cards, not a pile of differently-styled sections. */
.track-card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-card);
  padding: var(--sp-5);
  margin-bottom: var(--sp-4);
}
.track-sidebar .track-card { margin-bottom: 0; }
.track-card-dashed { border-style: dashed; box-shadow: none; text-align: center; }
.track-card-title {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  font-family: var(--font-display);
  font-weight: 800;
  font-size: var(--fs-body);
  color: var(--text-primary);
  margin-bottom: var(--sp-2);
}
.track-card-title [data-icon], .track-card-title [data-icon-done] { color: var(--purple-ink); }
.track-card-body { font-size: var(--fs-sm); color: var(--text-secondary); line-height: 1.5; margin-bottom: var(--sp-4); }
.track-card-foot { font-size: var(--fs-xs); color: var(--text-muted-strong); margin-top: var(--sp-3); }
.track-sidebar .secondary-btn, .track-sidebar .primary-btn { width: 100%; justify-content: center; }

/* Driver card */
.driver-card-row { display: flex; align-items: center; gap: var(--sp-3); }
.driver-avatar {
  flex-shrink: 0;
  width: 44px;
  height: 44px;
  border-radius: var(--r-pill);
  background: var(--purple-tint);
  color: var(--purple-ink);
  display: grid;
  place-items: center;
  font-family: var(--font-display);
  font-weight: 800;
  font-size: var(--fs-body);
}
.driver-card-text { flex: 1; min-width: 0; }
.driver-card-name { font-weight: 700; color: var(--text-primary); }
.driver-card-sub { font-size: var(--fs-xs); color: var(--text-secondary); }

.track-restaurant-label { font-size: var(--fs-xs); font-weight: 600; color: rgba(255, 255, 255, 0.72); margin-bottom: var(--sp-1); }

/* One card, two zones: a solid violet header (restaurant + live headline)
   sitting directly on top of the white stepper - not two separate boxes.
   Matched to the reference's tracking-card pattern. */
.track-head {
  background: var(--purple-ink);
  color: #fff;
  border: 1px solid var(--purple-ink);
  border-bottom: 0;
  border-radius: 28.8px 28.8px 0 0;
  padding: var(--sp-6);
  margin-bottom: 0;
  text-align: left;
}
.track-status-label {
  font-size: var(--fs-xs);
  font-weight: 600;
  letter-spacing: normal;
  text-transform: none;
  color: rgba(255, 255, 255, 0.82);
  margin-bottom: var(--sp-2);
  display: flex;
  align-items: center;
  gap: var(--sp-2);
}
.track-headline { font-size: var(--fs-display); font-family: var(--font-serif); font-weight: 600; letter-spacing: -0.01em; margin-bottom: var(--sp-2); color: #fff; }
.track-sub { font-size: var(--fs-sm); color: rgba(255, 255, 255, 0.82); }
.track-order-no {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  margin-top: var(--sp-4);
  font-size: var(--fs-2xs);
  color: rgba(255, 255, 255, 0.9);
  background: rgba(255, 255, 255, 0.14);
  border-radius: var(--r-pill);
  padding: var(--sp-2) var(--sp-4);
}
.track-order-no strong { color: #fff; font-variant-numeric: tabular-nums; }
/* Not .muted - that's a medium gray meant for the site's normal light
   background, unreadable against this card's dark purple fill. Every
   other text in .track-head got an explicit light-on-purple color for
   the same reason; this one was missed. */
.track-for { color: rgba(255, 255, 255, 0.72); font-size: var(--fs-sm); }

.confirmation h1 { font-size: var(--fs-h1); margin-bottom: var(--sp-2); }

.status-stepper {
  text-align: left;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 0 0 28.8px 28.8px;
  box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px -1px rgba(0, 0, 0, 0.1);
  padding: var(--sp-5);
  margin-bottom: var(--sp-4);
}
.status-step { display: flex; align-items: flex-start; gap: var(--sp-4); padding-bottom: var(--sp-5); position: relative; }
.status-step:last-child { padding-bottom: 0; }
.status-step:not(:last-child)::before {
  content: '';
  position: absolute;
  left: 8px;
  top: 20px;
  bottom: 0;
  width: 2px;
  background: var(--border);
  border-radius: 1px;
}
.status-step.is-done:not(:last-child)::before { background: var(--purple); }
.status-dot {
  width: 18px; height: 18px;
  border-radius: 50%;
  background: var(--surface-2);
  border: 2px solid var(--border-strong);
  flex-shrink: 0;
  margin-top: 1px;
  position: relative;
  z-index: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 9px;
  font-weight: 800;
  color: var(--text-on-accent);
}
.status-step.is-done .status-dot { background: var(--purple); border-color: var(--purple); }
.status-step.is-done .status-dot::after { content: '\2713'; }
.status-step.is-current .status-dot {
  background: var(--purple);
  border-color: var(--purple);
  box-shadow: 0 0 0 5px var(--purple-tint);
}
.status-label { font-size: var(--fs-sm); font-weight: 600; }
.status-step.is-current .status-label { color: var(--purple); }
.status-step:not(.is-done):not(.is-current) .status-label { color: var(--text-muted-strong); font-weight: 500; }

/* Reference pattern: a horizontal row of icon nodes joined by one connecting
   line, label centered underneath - not a vertical list. Switches in once
   there's room for it; stays the vertical list below that, since a 5-7 step
   horizontal row has no room to breathe on a phone. */
@media (min-width: 640px) {
  .status-stepper { display: flex; padding: 28px var(--sp-6) var(--sp-5); }
  .status-step {
    flex: 1;
    flex-direction: column;
    align-items: center;
    text-align: center;
    gap: var(--sp-2);
    padding-bottom: 0;
  }
  .status-step:not(:last-child)::before {
    left: 50%;
    right: auto;
    top: 19px;
    bottom: auto;
    width: 100%;
    height: 2px;
  }
  .status-dot { width: 40px; height: 40px; font-size: 15px; }
  .status-body { padding-top: 0; align-items: center; }
  .status-next { display: none; }
}

/* Map is opt-in and secondary. */
.track-map-toggle {
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-3);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  padding: var(--sp-4) var(--sp-5);
  color: var(--text-primary);
  font-size: var(--fs-sm);
  font-weight: 600;
  cursor: pointer;
  min-height: 52px;
  margin-bottom: var(--sp-4);
  font-family: inherit;
}
.track-map-toggle:hover { border-color: var(--border-strong); }
.track-map-toggle .chev { color: var(--text-muted-strong); transition: transform var(--t-fast) var(--ease); }
.track-map-toggle[aria-expanded="true"] .chev { transform: rotate(180deg); }

/* The one number that answers "when will it arrive" - route-based, and
   deliberately never the same line as the kitchen's prep-time estimate
   (see track-sub, driven by TRACK_COPY/renderTrackHead instead). */
.track-eta {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  margin-bottom: var(--sp-4);
  padding: var(--sp-2) var(--sp-4);
  background: var(--purple-tint);
  color: var(--purple-ink);
  border-radius: var(--r-pill);
  font-size: var(--fs-sm);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

.track-stale-notice {
  margin-bottom: var(--sp-4);
  padding: var(--sp-3) var(--sp-4);
  background: var(--surface-2);
  border-radius: var(--r-md);
  color: var(--text-secondary);
  font-size: var(--fs-xs);
  font-weight: 600;
}

.map-box {
  height: 240px;
  border-radius: var(--r-lg);
  overflow: hidden;
  border: 1px solid var(--border);
  margin-bottom: var(--sp-4);
  background: var(--surface-2);
}

/* Scoped override, not a change to the shared .ff-alert used by every
   surface's error/warning banners - just this customer-tracking-page use of
   it, matched to the reference's card radius. */
#alcohol-id-required-box {
  border-radius: 20px;
  box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px -1px rgba(0, 0, 0, 0.1);
  margin-bottom: var(--sp-4);
}

.chat-box {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 20px;
  box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px -1px rgba(0, 0, 0, 0.1);
  padding: var(--sp-4);
  margin-bottom: var(--sp-4);
}
.chat-thread {
  display: flex;
  flex-direction: column;
  gap: 8px;
  max-height: 240px;
  overflow-y: auto;
  margin-bottom: var(--sp-3);
}
.chat-bubble {
  max-width: 80%;
  padding: 8px 12px;
  border-radius: var(--r-md);
  font-size: 14px;
  line-height: 1.4;
  word-break: break-word;
}
.chat-bubble.is-mine {
  align-self: flex-end;
  background: var(--purple);
  color: #fff;
}
.chat-bubble.is-theirs {
  align-self: flex-start;
  background: var(--surface-3);
  border: 1px solid var(--border);
}
.chat-input-row {
  display: flex;
  gap: 8px;
}
.chat-input-row input {
  flex: 1;
}

.delivery-photo-box {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 20px;
  box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px -1px rgba(0, 0, 0, 0.1);
  padding: var(--sp-4);
  margin-bottom: var(--sp-4);
}
.delivery-photo-img {
  width: 100%;
  max-height: 320px;
  object-fit: cover;
  border-radius: 14px;
  display: block;
}
.leaflet-popup-content-wrapper, .leaflet-popup-tip { background: var(--surface-3); color: var(--text-primary); }
.leaflet-container a.leaflet-popup-close-button { color: var(--text-secondary); }
.leaflet-container { background: var(--surface-2); }

/* --- Survey -------------------------------------------------------------- */

.survey-box {
  text-align: left;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 20px;
  box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px -1px rgba(0, 0, 0, 0.1);
  padding: var(--sp-5);
  margin-top: var(--sp-4);
}
.star-row { display: flex; gap: var(--sp-1); }
.star-btn {
  background: none;
  border: none;
  font-size: 30px;
  line-height: 1;
  padding: var(--sp-1);
  cursor: pointer;
  color: var(--border-strong);
  transition: color var(--t-fast) var(--ease), transform var(--t-fast) var(--ease);
}
.star-btn:hover { transform: scale(1.1); }
.star-btn.is-filled { color: var(--purple); }
.survey-issues { display: flex; flex-direction: column; gap: var(--sp-1); }

/* ============================================================================
   Order history
   ========================================================================= */

.orders-list { display: flex; flex-direction: column; gap: var(--sp-3); }
.order-row {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  padding: var(--sp-4) var(--sp-5);
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: var(--sp-4);
  text-align: left;
  color: inherit;
  width: 100%;
  font-family: inherit;
  transition: border-color var(--t-fast) var(--ease);
}
.order-row:hover { border-color: var(--border-strong); }
.order-row-status {
  font-size: var(--fs-micro);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  white-space: nowrap;
  display: inline-flex;
  align-items: center;
  gap: var(--sp-1);
  padding: 4px var(--sp-2);
  border-radius: var(--r-sm);
  background: var(--purple-tint);
  color: var(--purple);
}
.order-row-status::before { content: '\25CF'; font-size: 9px; }
.order-row-status.is-complete { background: var(--success-tint); color: var(--success); }
.order-row-status.is-complete::before { content: '\2713'; }
.order-row-status.is-ended { background: var(--surface-2); color: var(--text-muted-strong); }
.order-row-status.is-ended::before { content: '\25CB'; }

/* ============================================================================
   Support
   ========================================================================= */

.support-detail { background: var(--surface); border: 1px solid var(--border); border-radius: var(--r-lg); padding: var(--sp-5); margin-top: var(--sp-3); }
.support-thread { display: flex; flex-direction: column; gap: var(--sp-3); margin: var(--sp-4) 0; }
.support-message { border-radius: var(--r-md); padding: var(--sp-3) var(--sp-4); max-width: 85%; font-size: var(--fs-sm); line-height: 1.55; }
.support-message.from-requester { background: var(--surface-2); align-self: flex-start; }
.support-message.from-admin { background: var(--purple-tint); color: var(--text-primary); align-self: flex-end; }
.support-message-meta { font-size: var(--fs-micro); color: var(--text-muted-strong); margin-bottom: var(--sp-1); text-transform: uppercase; letter-spacing: 0.04em; }
.support-category-pill { font-size: var(--fs-2xs); color: var(--text-secondary); }

/* ============================================================================
   Bottom tab bar (mobile)
   ========================================================================= */

/* The "more" menu next to the cart icon - mobile's stand-in for the
   Browse/Orders/Support/Account items that live directly in the header
   above 640px (replaced the old fixed bottom tab bar, which reserved a full
   row of screen height on every mobile page for four links most sessions
   never touch more than once). */
.topbar-more-btn {
  display: none;
  width: 42px;
  height: 42px;
  align-items: center;
  justify-content: center;
  background: var(--surface-2);
  border: 0;
  border-radius: var(--r-pill);
  color: var(--text-primary);
  cursor: pointer;
  transition: background var(--t-fast) var(--ease);
}
.topbar-more-btn:hover, .topbar-more-btn[aria-expanded="true"] { background: var(--surface-3); }
@media (max-width: 640px) {
  .topbar-more-btn { display: flex; }
}
.topbar-more-menu {
  position: fixed;
  top: calc(var(--topbar-h) + var(--sp-2));
  right: var(--gutter);
  z-index: 60;
  min-width: 190px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-lg);
  padding: var(--sp-2);
}
.topbar-more-item {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  width: 100%;
  min-height: 44px;
  padding: var(--sp-2) var(--sp-3);
  background: none;
  border: 0;
  border-radius: var(--r-sm);
  font-size: var(--fs-sm);
  font-weight: 600;
  color: var(--text-primary);
  text-align: left;
  cursor: pointer;
}
.topbar-more-item:hover { background: var(--surface-2); }
.topbar-more-item [data-icon], .topbar-more-item [data-icon-done] { color: var(--text-muted-strong); flex-shrink: 0; }

/* ============================================================================
   Light-surface corrections
   ----------------------------------------------------------------------------
   Rules that were written against a near-black ground and need a different
   value now that the customer surface is warm paper. Kept together at the end
   so the intent of the change is legible rather than scattered.
   ========================================================================= */

/* The item sheet leads with the dish: full-bleed media across the top of the
   scroll region, in the same frame vocabulary as the menu it opened from. */
.modal-item-image {
  width: calc(100% + var(--sp-12));
  margin: calc(var(--sp-6) * -1) calc(var(--sp-6) * -1) var(--sp-5);
  height: 220px;
  max-height: none;
  border-radius: 0;
}
.modal-item-image .ff-media-initial { font-size: 76px; }
@media (max-width: 640px) {
  .modal-item-image {
    width: calc(100% + var(--sp-10));
    margin: calc(var(--sp-5) * -1) calc(var(--sp-5) * -1) var(--sp-4);
    height: 176px;
    max-height: none;
  }
}

.modal-age-note {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  flex-wrap: wrap;
  font-size: var(--fs-xs);
  color: var(--text-secondary);
  margin-bottom: var(--sp-4);
}

.start-group-btn { margin-bottom: var(--sp-6); }

/* Primary/secondary buttons: the legacy class names app.js already emits,
   restated for the warm ground. Geometry is unchanged. */
.primary-btn { background: var(--purple); color: var(--text-on-accent); }
.primary-btn:hover:not(:disabled) { background: var(--purple-hover); color: var(--text-on-accent-hover); }
.primary-btn:disabled { background: var(--surface-3); color: var(--text-muted-strong); }
.secondary-btn { background: var(--surface); border: 1px solid var(--border); color: var(--text-primary); }
.secondary-btn:hover:not(:disabled) { background: var(--surface-2); border-color: var(--border-strong); }

/* Selected modifier rows keep their two signals (tint plus the control's own
   checked state), tuned for paper. */
.modifier-row:has(input:checked) { background: var(--purple-tint); }
.modifier-row:has(input:checked) .modifier-price { color: var(--purple-ink); }
.req-tag.is-required { background: var(--purple-tint); color: var(--purple-ink); }
.menu-item-card:hover .item-name, .ff-dish:hover .ff-dish-name { color: var(--purple-ink); }
.category-rail button.is-active { color: var(--purple-ink); }
.rating-pill .star { color: var(--purple-ink); }

/* Gutter and touch-target corrections ------------------------------------- */

@media (max-width: 640px) {
  :root { --gutter: 16px; }
}

/* Every control in the chrome clears the 44px comfortable-tap floor, including
   the ones whose text is small. */
.text-btn { min-height: 40px; display: inline-flex; align-items: center; }
.brand { min-height: 44px; }
.hero-locline-edit { padding: var(--sp-2) 0; }

/* --- Order tracking: the one screen a customer watches ------------------- */

.track-head {
  padding: var(--sp-8) var(--sp-6);
}
/* Reference header shows restaurant + headline only, no icon badge - the
   status icon still exists (it's meaningful: done/live/ended), just moved
   out of the flow of this specific band to match. */
.track-mark { display: none; }
.track-mark[data-state="done"] { background: var(--success-tint); color: var(--green); }
.track-mark[data-state="ended"] { background: var(--surface-2); color: var(--text-muted-strong); }
/* One authored moment: the live stage breathes, so a waiting customer can see
   at a glance that the page is still watching. */
.track-mark[data-state="live"] { animation: ff-track-pulse 2.6s var(--ease) infinite; }
@keyframes ff-track-pulse {
  0%, 100% { box-shadow: 0 0 0 0 rgba(139, 110, 245, 0.35); }
  60%      { box-shadow: 0 0 0 14px rgba(139, 110, 245, 0); }
}
@media (prefers-reduced-motion: reduce) {
  .track-mark[data-state="live"] { animation: none; }
}
.track-headline { font-size: var(--fs-display); letter-spacing: -0.03em; text-wrap: balance; }
@media (max-width: 640px) {
  .track-headline { font-size: var(--fs-h1); }
  .track-head { padding: var(--sp-6) var(--sp-5); }
}

@media (max-width: 639px) {
  .status-dot { width: 26px; height: 26px; border-width: 1px; }
  .status-step:not(:last-child)::before { left: 12px; top: 28px; }
}
/* The dot fills with --purple-ink (the darker calibration), so its mark is
   white, not Void Black - black on that fill is only 3:1. */
.status-step.is-done .status-dot { color: #fff; }
.status-step.is-current .status-dot { color: #fff; }
.status-step:not(.is-done):not(.is-current) .status-dot { color: var(--text-muted); }
.status-body { display: flex; flex-direction: column; gap: 2px; padding-top: 3px; }
.status-next { font-size: var(--fs-2xs); color: var(--text-secondary); }
.status-step.is-current .status-label { color: var(--purple-ink); }
.status-step.is-done:not(:last-child)::before { background: var(--purple-ink); }
.status-step.is-current .status-dot,
.status-step.is-done .status-dot { background: var(--purple-ink); border-color: var(--purple-ink); }
.track-map-toggle .chev { display: flex; }

/* Accent-on-tint text needs the AA-corrected violet on paper; the fill keeps
   the brand violet, which only ever prints Void Black on top of it. */
.checkout-step-num { color: var(--purple-ink); }
.order-summary .summary-line.total { color: var(--text-primary); }
.info-btn:hover, .info-btn:focus-visible { border-color: var(--purple-ink); color: var(--purple-ink); }
/* The total is the figure the whole block exists to state; it is the largest
   number in it. */
.summary-line.total > span:last-child { font-size: var(--fs-display); letter-spacing: -0.03em; }

/* Accessibility corrections found by measuring the built page ----------------
   Accent text on an accent tint, and inline accent links in body copy, both
   need the AA-corrected violet rather than the fill violet. The two controls
   below were under the 44px comfortable-tap floor. */

.segmented-option.is-active { color: var(--purple-ink); }
.checkbox-row a { color: var(--purple-ink); font-weight: 600; }
.checkbox-row { min-height: 44px; }
.checkbox-row input[type="checkbox"] { width: 20px; height: 20px; accent-color: var(--purple-ink); }

/* The fee explainer is a real control, not a decoration; it gets a real
   target and keeps its 16px mark inside it. */
.info-btn {
  width: 24px;
  height: 24px;
  font-size: var(--fs-2xs);
}
.back-link { min-height: 44px; }

/* Inline accent controls: the AA-corrected violet, and a real target. */
.ff-link-btn { color: var(--purple-ink); }
.hero-locline-edit { min-height: 44px; padding: var(--sp-2) 0; }
.partner-card .ff-btn-primary { color: var(--text-on-accent); }

/* Comfortable-tap floor, measured on the built page rather than assumed. */
.icon-btn { width: 44px; height: 44px; }
.info-icon { width: 24px; height: 24px; min-width: 24px; }
.cart-line-remove { min-height: 44px; display: inline-flex; align-items: center; }
.start-group-btn { min-height: 44px; font-size: var(--fs-sm); }
.ff-stepper button { width: 44px; height: 44px; }

/* --- Corrections from the design review ---------------------------------- */

/* The star control now holds a drawn mark rather than a 30px text glyph, so it
   is sized as a control instead of as type. */
.star-btn {
  font-size: 0;
  width: 44px;
  height: 44px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0;
}
.star-btn .ff-i { width: 30px; height: 30px; }
.star-btn.is-filled { color: var(--purple-ink); }

/* The location button's text could run under its own chevron at 375px, because
   the label was capped by a fixed max-width while the row was flexible. Letting
   the text column shrink and giving the chevron its own track fixes it at every
   width instead of at the two that were tested. */
.location-btn { min-width: 0; }
.location-btn-text { flex: 1; min-width: 0; }
.location-btn-text strong { max-width: 100%; display: block; }
.location-btn-chev { flex: none; }
@media (max-width: 720px) {
  .location-btn-text strong { max-width: 100%; }
}

/* A single restaurant in an auto-fill grid left three empty columns; cap the
   track count so one card stays card-sized instead of stretching. */
.ff-rgrid { grid-template-columns: repeat(auto-fill, minmax(272px, 1fr)); }
/* A charge that isn't known yet says so, rather than printing a zero that
   reads as "free" in a product whose promise is no surprises. */
.summary-pending { color: var(--text-muted-strong); font-weight: 500; font-size: var(--fs-xs); }
/* Accent text on an accent tint: the corrected calibration, same as every
   other badge in the system. */
.cart-line-qty { color: var(--purple-ink); }
.hero-picks-more { min-height: 44px; display: inline-flex; align-items: center; }

/* Prep time on a card sits with the other facts and carries its own mark, so
   it never reads as a delivery promise the product cannot make. */
.ff-rcard-meta [data-icon-done], .ff-rcard-meta svg { color: var(--text-muted-strong); }

/* Two cards per row on a phone instead of one - shared/fairly-ui.css's
   default (one full-width card) reads better per-card, but a customer
   scanning six restaurants on a 375px screen would rather compare more of
   them in the same scroll. Every sub-element below is scaled down to match
   a ~160px-wide card instead of the shared component's 272px+ assumption:
   smaller radius (28.8px reads as a corner, not a curve, at this size),
   tighter body padding/type, single-line truncation instead of wrapping,
   and the prep-time row (the second .ff-rcard-meta, right after the
   cuisine/location one) dropped entirely - a tap into the restaurant still
   shows it, and keeping it here just bloats every card by a line. */
@media (max-width: 560px) {
  .ff-rgrid, .ff-rgrid.ff-rgrid-few { grid-template-columns: repeat(2, 1fr); max-width: none; gap: var(--sp-3) var(--sp-3); }
  .ff-rcard { border-radius: var(--r-lg); }
  .ff-rcard-body { padding: 10px; gap: 2px; }
  .ff-rcard-name { font-size: 14px; line-height: 1.25; }
  .ff-rcard-top { gap: var(--sp-1); }
  .ff-rcard-meta {
    font-size: 11px;
    gap: 4px;
    flex-wrap: nowrap;
    overflow: hidden;
  }
  .ff-rcard-meta span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .ff-rcard-meta + .ff-rcard-meta { display: none; }
  .ff-rating { font-size: 11px; }
  .ff-media-tag { padding: 3px 7px; font-size: 10px; }
}

/* ---------- Restaurant reviews (customer-facing) ---------- */
.restaurant-reviews {
  margin-top: var(--sp-10);
  padding-top: var(--sp-8);
  border-top: 1px solid var(--border);
}
.restaurant-reviews-title {
  font-size: var(--fs-h3, 20px);
  font-weight: 700;
  letter-spacing: -0.01em;
  margin: 0 0 var(--sp-5);
  color: var(--text-primary);
}
.restaurant-reviews-list {
  display: grid;
  gap: var(--sp-4);
  grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
}
.ff-review {
  border: 1px solid var(--border);
  border-radius: var(--r-lg, 16px);
  padding: var(--sp-4) var(--sp-5);
  background: var(--surface);
}
.ff-review-top {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  margin-bottom: var(--sp-2);
}
.ff-review-author {
  font-size: var(--fs-xs);
  font-weight: 600;
  color: var(--text-muted-strong);
}
.ff-review-comment {
  margin: 0;
  color: var(--text-secondary);
  line-height: 1.55;
  font-size: var(--fs-body, 15px);
}

/* ============================================================================
   Cart + checkout: quantity, and "your usual"

   Changing how many of something you want is the commonest edit anyone makes
   at this point, and it used to mean leaving checkout, finding the dish on the
   menu and adding it again. The stepper puts it where the decision happens.
   ========================================================================= */

.cart-line-actions { display: flex; justify-content: flex-end; margin-top: var(--sp-2); }
.cart-line-name { font-weight: 600; }

.summary-item-right { display: flex; align-items: center; gap: var(--sp-3); }
/* Tabular figures so a price does not shuffle sideways as the quantity above
   it changes - the row should feel like it is updating, not redrawing. */
.summary-item-price { font-variant-numeric: tabular-nums; min-width: 4.5ch; text-align: right; }

/* "Add your usual" - the items this customer keeps reordering here, offered
   at the moment they are already committed to buying. Visually quieter than
   anything in the summary itself: it is an offer, not part of the order, and
   it must never be mistaken for something already being paid for. */
.summary-usual { margin-top: var(--sp-4); padding-top: var(--sp-4); border-top: 1px dashed var(--border); }
.summary-usual-title {
  margin: 0 0 var(--sp-2);
  font-size: var(--fs-xs);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--text-secondary);
}
.summary-usual-row { display: flex; flex-wrap: wrap; gap: var(--sp-2); }

.usual-chip {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  padding: var(--sp-2) var(--sp-3);
  border: 1px solid var(--border-strong);
  border-radius: var(--r-pill, 999px);
  background: var(--surface);
  color: var(--text-primary);
  font-size: var(--fs-sm);
  cursor: pointer;
  /* Comfortably past the 44px minimum once padding is counted, because this
     sits among priced rows people are about to pay for - a mis-tap here adds
     money to somebody's bill. */
  min-height: 40px;
  transition: background 120ms ease, border-color 120ms ease, transform 120ms ease;
}
.usual-chip:hover { background: var(--surface-2); border-color: var(--purple); }
.usual-chip:active { transform: scale(0.97); }
.usual-chip-price { color: var(--text-secondary); font-variant-numeric: tabular-nums; }

@media (prefers-reduced-motion: reduce) {
  .usual-chip { transition: none; }
  .usual-chip:active { transform: none; }
}

/* ============================================================================
   View transitions

   The app swaps `hidden` between full-screen views, so without this a screen
   change is an instantaneous redraw - the eye reads it as a flicker rather
   than as having gone somewhere. A short rise-and-fade gives the change a
   direction and makes the app feel like it is responding rather than
   repainting.

   Transform and opacity only: those are the two properties the browser can
   animate on the compositor without re-laying-out the page, so this stays
   free on a mid-range phone. Nothing here shifts layout, so it cannot cause
   a click to land on the wrong thing mid-animation.
   ========================================================================= */

@keyframes viewEnter {
  from { opacity: 0; transform: translateY(8px); }
  to   { opacity: 1; transform: none; }
}

.view-entering {
  animation: viewEnter 220ms cubic-bezier(0.22, 1, 0.36, 1) both;
}

/* Motion here is decoration - it carries no information the layout does not
   already give - so it is removed outright rather than merely shortened for
   anyone who has asked for less of it. */
@media (prefers-reduced-motion: reduce) {
  .view-entering { animation: none; }
}

/* ---------------------------------------------------------------------------
   Adding to an order that has already been placed.

   The card only appears while the server says the window is genuinely open,
   and the countdown is a display of that answer rather than the rule - see
   renderAddOnCard in app.js. It sits high on the tracking screen because it
   is only useful for a few minutes and is worthless the moment it is missed.
   ------------------------------------------------------------------------ */
.add-on-card { border-color: var(--purple); }

.add-on-card-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-3);
  margin-bottom: var(--sp-2);
}

/* Tabular figures so a ticking clock doesn't jitter the layout every second. */
.add-on-countdown {
  font-size: var(--fs-xs);
  font-weight: 800;
  color: var(--purple);
  background: var(--purple-tint);
  padding: 2px var(--sp-2);
  border-radius: var(--r-sm);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.add-on-card .primary-btn { margin-top: var(--sp-3); }

/* Shown above the menu for as long as the cart is destined for an existing
   order. Without it there is nothing to distinguish this from starting a new
   order, and that mistake costs a second delivery fee. */
.add-on-banner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-3);
  flex-wrap: wrap;
  margin-bottom: var(--sp-4);
  padding: var(--sp-3) var(--sp-4);
  border-radius: var(--r-md);
  background: var(--purple-tint);
  border: 1px solid var(--purple);
  color: var(--purple-ink, var(--purple));
  font-size: var(--fs-sm);
  font-weight: 600;
}

.add-on-banner .link-btn {
  background: none;
  border: none;
  padding: 0;
  font: inherit;
  font-weight: 800;
  color: inherit;
  text-decoration: underline;
  cursor: pointer;
}

.add-on-totals {
  margin-top: var(--sp-4);
  padding-top: var(--sp-3);
  border-top: 1px solid var(--border);
}

.add-on-total-row {
  display: flex;
  justify-content: space-between;
  gap: var(--sp-3);
  font-size: var(--fs-sm);
  padding: 2px 0;
}

.add-on-total-row-strong {
  font-weight: 800;
  font-size: var(--fs-base);
  margin-top: var(--sp-2);
  padding-top: var(--sp-2);
  border-top: 1px solid var(--border);
}

/* The reason someone uses this instead of placing a second order, so it is
   stated where the money is, not buried in the card above. */
.add-on-total-note {
  margin-top: var(--sp-2);
  font-size: var(--fs-2xs);
  color: var(--text-muted);
}

/* Tip presets on the tracking screen. Flat amounts rather than percentages:
   a percentage asks someone to do arithmetic about a total they have stopped
   thinking about, which is not the decision they are trying to make. */
.tip-preset-row {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-2);
  margin-top: var(--sp-3);
}

.tip-preset {
  padding: var(--sp-2) var(--sp-4);
  border-radius: var(--r-pill);
  border: 1px solid var(--border-strong);
  background: var(--surface);
  color: var(--text-primary);
  font-size: var(--fs-sm);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  cursor: pointer;
}

.tip-preset:hover { border-color: var(--purple); }

.tip-preset.is-selected {
  background: var(--purple);
  border-color: var(--purple);
  color: #fff;
}

/* ---------------------------------------------------------------------------
   Browsing by dietary requirement.

   Grouped by restaurant rather than a flat list of dishes: ordering happens
   per restaurant, so a flat list reads well and then leaves the customer to
   work out which dishes they could actually combine into one order.
   ------------------------------------------------------------------------ */
.diet-group {
  margin-bottom: var(--sp-6);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  overflow: hidden;
  background: var(--surface);
}

.diet-group-head {
  display: flex;
  align-items: baseline;
  gap: var(--sp-3);
  width: 100%;
  padding: var(--sp-4);
  border: none;
  border-bottom: 1px solid var(--border);
  background: var(--surface-2);
  text-align: left;
  cursor: pointer;
}

.diet-group-name { font-size: var(--fs-base); font-weight: 800; color: var(--text-primary); }
.diet-group-cuisine { font-size: var(--fs-xs); color: var(--text-muted-strong); }
.diet-group-go { margin-left: auto; font-size: var(--fs-xs); font-weight: 700; color: var(--purple); white-space: nowrap; }

.diet-dish {
  display: flex;
  align-items: flex-start;
  gap: var(--sp-4);
  width: 100%;
  padding: var(--sp-4);
  border: none;
  border-top: 1px solid var(--border);
  background: transparent;
  text-align: left;
  cursor: pointer;
}
.diet-dish:first-of-type { border-top: none; }
.diet-dish:hover { background: var(--surface-2); }

.diet-dish-main { display: flex; flex-direction: column; gap: 3px; flex: 1; min-width: 0; }
.diet-dish-name { font-size: var(--fs-sm); font-weight: 700; color: var(--text-primary); }
.diet-dish-desc { font-size: var(--fs-xs); color: var(--text-muted-strong); }
.diet-dish-tags { display: flex; flex-wrap: wrap; gap: 4px; margin-top: 2px; }

/* An allergen note is a warning, not a claim, so it does not get the same
   reassuring green as the category badges. */
.diet-dish-allergen {
  font-size: var(--fs-2xs);
  color: var(--warn);
  margin-top: 2px;
}

.diet-dish-price {
  font-size: var(--fs-sm);
  font-weight: 800;
  color: var(--text-primary);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.diet-badge {
  display: inline-block;
  padding: 1px var(--sp-2);
  border-radius: var(--r-pill);
  background: var(--success-tint);
  color: var(--success);
  font-size: var(--fs-2xs);
  font-weight: 700;
}

.ff-center { text-align: center; }

/* A dietary category nobody has tagged a dish for yet. Shown rather than
   removed - this row is the picker, and hiding an option makes the app look
   like it cannot do something it can. Muted and unclickable, with the reason
   said out loud, so it reads as "not yet" rather than "broken". */
.ff-cat-pill.is-empty {
  opacity: 0.5;
  cursor: not-allowed;
  border-style: dashed;
}

.diet-chip-none {
  margin-left: var(--sp-1);
  font-size: 11px;
  font-weight: 600;
  color: var(--text-muted);
}

/* The dish photo on the cross-restaurant diet page. Same size as the
   restaurant menu's own thumbnail so a dish looks the same wherever it is
   met, and pushed to the end of the row so the name and the dietary tags
   still lead. */
.diet-dish-media {
  order: 2;
  width: 96px;
  flex-shrink: 0;
  border-radius: var(--r-md);
  box-shadow: none;
}

.diet-dish-price { order: 1; }

.diet-group-logo {
  width: 36px;
  flex-shrink: 0;
  border-radius: var(--r-sm);
  box-shadow: none;
}

@media (max-width: 480px) {
  .diet-dish-media { width: 72px; }
}

/* The arrival window on the tracking page. Given real size because it is
   the one thing a waiting customer is actually looking for. */
.track-arrival {
  margin-top: var(--sp-4);
  padding-top: var(--sp-4);
  border-top: 1px solid var(--border);
}

.track-arrival-label {
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-muted-strong);
  margin: 0;
}

.track-arrival-range {
  font-size: 28px;
  font-weight: 800;
  letter-spacing: -0.02em;
  color: var(--text-primary);
  margin: 2px 0 0;
  font-variant-numeric: tabular-nums;
}

.track-arrival-caveat {
  font-size: 12px;
  color: var(--text-muted-strong);
  margin: 4px 0 0;
}

/* The remembered dietary requirement, offered first on the browse rail.
   Marked out from the plain category chips because it is a shortcut back
   to something the customer already told us, not another filter. */
.ff-cat-pill.is-usual {
  border-color: var(--purple);
  color: var(--purple-ink);
}

.diet-usual-tag {
  font-size: 10px;
  font-weight: 800;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--text-muted-strong);
  margin-right: 2px;
}

/* A restaurant's own offers on its storefront. */
.restaurant-offers { display: flex; flex-wrap: wrap; gap: var(--sp-2); margin-top: var(--sp-3); }
.restaurant-offer {
  display: inline-flex; align-items: center; gap: var(--sp-2);
  padding: var(--sp-2) var(--sp-3);
  border-radius: var(--r-pill);
  background: var(--purple-tint);
  color: var(--purple-ink);
  font-size: 13px; font-weight: 700;
}
.restaurant-offer-when { font-weight: 500; opacity: 0.85; }

/* Saving a restaurant. Outline until saved, filled after, so the state is
   readable without relying on colour alone - the word changes too. */
.favorite-btn {
  display: inline-flex;
  /* Its parent is a stretching flex column; without this the pill ran the
     full width of the restaurant header. */
  align-self: flex-start;
  align-items: center;
  gap: var(--sp-2);
  margin-top: var(--sp-3);
  padding: var(--sp-2) var(--sp-4);
  border: 1px solid var(--border);
  border-radius: var(--r-pill);
  background: var(--surface);
  font-size: 13px;
  font-weight: 700;
  color: var(--text-primary);
  cursor: pointer;
}

.favorite-btn:hover { border-color: var(--purple); }

/* Outline heart until saved, filled after. */
.favorite-btn.is-on .favorite-icon svg { fill: currentColor; }

.favorite-btn.is-on {
  border-color: var(--purple);
  background: var(--purple-tint);
  color: var(--purple-ink);
}

.favorites-row { margin-bottom: var(--sp-6); }

.favorites-title {
  font-size: 18px;
  font-weight: 800;
  letter-spacing: -0.02em;
  margin: 0 0 var(--sp-3);
}

.favorites-rail {
  display: flex;
  gap: var(--sp-3);
  overflow-x: auto;
  padding-bottom: var(--sp-2);
}

.favorite-card {
  flex: 0 0 auto;
  width: 132px;
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding: var(--sp-3);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  background: var(--surface);
  text-align: left;
  cursor: pointer;
}

.favorite-card:hover { border-color: var(--purple); }
.favorite-card-media { width: 44px; border-radius: var(--r-sm); box-shadow: none; }
.favorite-card-name { font-size: 13px; font-weight: 700; color: var(--text-primary); }
.favorite-card-meta { font-size: 11px; color: var(--text-muted-strong); }

/* The receipt on the tracking page. Two columns, right-aligned money, the
   shape a paper receipt has - anything cleverer makes the numbers harder to
   compare against a bank statement, which is the only reason to open it. */
.receipt-row {
  display: flex;
  justify-content: space-between;
  gap: 12px;
  padding: 4px 0;
  font-size: var(--fs-sm);
}
.receipt-row > span:last-child { font-variant-numeric: tabular-nums; white-space: nowrap; }
.receipt-note { margin: -2px 0 6px; font-size: var(--fs-xs); color: var(--text-muted-strong); }
.receipt-rule { height: 1px; background: var(--border); margin: 8px 0; }
.receipt-total { font-weight: 700; }
#receipt-card summary { cursor: pointer; }
#receipt-card[open] summary { margin-bottom: 10px; }

/* ============================================================================
   The mark, moving
   ----------------------------------------------------------------------------
   The logo is an F with three speed lines trailing behind it: the brand
   already draws motion, it just never performed any. So on hover it does the
   one thing the drawing is about - it darts forward and settles, easing out
   like something that was already travelling. Deliberately not a spin: the
   mark reads left-to-right, and rotating it would argue with its own shape.
   ========================================================================= */
.brand-mark { transition: transform var(--t-slow) var(--ease-out); }
.brand:hover .brand-mark,
.brand:focus-visible .brand-mark { transform: translateX(3px) scale(1.05); }
.brand:active .brand-mark { transform: translateX(1px) scale(0.96); transition-duration: 90ms; }

/* --- The bag catches it -------------------------------------------------
   The count popping says the number changed. The bag tipping says something
   was put into it, which is the half that makes adding a dish feel like it
   landed somewhere rather than incrementing a variable. */
.cart-btn.is-catching { animation: cart-catch 420ms var(--ease-out); }
@keyframes cart-catch {
  0%   { transform: rotate(0deg) scale(1); }
  30%  { transform: rotate(-9deg) scale(1.06); }
  60%  { transform: rotate(5deg) scale(1.02); }
  100% { transform: rotate(0deg) scale(1); }
}

/* --- Reaching for things ------------------------------------------------- */
.cart-btn { transition: transform var(--t-base) var(--ease-out), background var(--t-fast) var(--ease); }
.cart-btn:hover { transform: translateY(-1px) scale(1.04); }

/* The pin drops, the way a pin does. */
.location-btn-icon { transition: transform var(--t-base) var(--ease-out); }
.location-btn:hover .location-btn-icon { transform: translateY(-2px); }
.location-btn:active .location-btn-icon { transform: translateY(0) scale(0.92); transition-duration: 90ms; }

/* The magnifier leans in when the field it belongs to wakes up. */
.ff-searchbig [data-icon="search"],
.ff-searchbig [data-icon-done="search"] { transition: transform var(--t-base) var(--ease-out); }
.ff-searchbig:focus-within [data-icon="search"],
.ff-searchbig:focus-within [data-icon-done="search"] { transform: scale(1.12) rotate(-8deg); }

/* --- A dish, wanted -----------------------------------------------------
   The card lifts a little under the cursor and the plus leans toward being
   pressed. Small: this is a list somebody is scanning, and a grid where every
   tile jumps is a grid nobody can read. */
.ff-dish-open { transition: transform var(--t-base) var(--ease-out); }
.ff-dish-row:hover .ff-dish-open { transform: translateX(2px); }
.ff-dish-controls button { transition: transform var(--t-base) var(--ease-out), background var(--t-fast) var(--ease); }
.ff-dish-row:hover .ff-dish-controls button { transform: scale(1.06); }
.ff-dish-controls button:active { transform: scale(0.92); transition-duration: 80ms; }
