/* ══════════════════════════════════════════════════════════════════════
   Magneta kiosk — portrait first, responsive everywhere.

   Every size is clamp()ed against viewport units, so the same build holds
   from a 10" tablet through a 55" portrait stand, and stays usable on a
   landscape laptop during setup.
   ══════════════════════════════════════════════════════════════════════ */

*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }

/* Palette, typography, radii and motion all come from brand.css.
   Only kiosk-specific measurements live here. */
:root {
  --bar-h: clamp(76px, 11vh, 148px);
}

/* Kiosk type runs large — 19px and up on every control — so white on the
   brand red clears the large-text contrast bar comfortably. The admin
   panel, whose labels are 13px, uses --brand-primary-deep instead. */

html, body {
  height: 100%; width: 100%;
  overflow: hidden;
  background: var(--plum);
  color: var(--ink);
  font-family: var(--font-body);
  -webkit-font-smoothing: antialiased;
  -webkit-tap-highlight-color: transparent;
  user-select: none;
  touch-action: manipulation;
}
button, input { font-family: inherit; color: inherit; }
button { cursor: pointer; border: none; background: none; }

.screen { position: fixed; inset: 0; }


/* ── Logo placements ────────────────────────────────────────────────── */
.attract-logo {
  position: absolute;
  top: clamp(20px, 4vh, 52px);
  left: 0; right: 0;
  z-index: 3;
  display: flex; justify-content: center;
  pointer-events: none;
}
.attract-logo .brand-logo {
  height: clamp(46px, 8vh, 110px);
  /* The logo artwork is dark-on-transparent; over video it needs a lift. */
  filter: brightness(0) invert(1) drop-shadow(0 3px 14px rgba(0,0,0,.45));
  opacity: .96;
}

.hub-logo {
  height: clamp(42px, 7vh, 96px);
  margin: 0 auto clamp(8px, 1.6vh, 20px);
}

.page-logo {
  flex: none;
  height: clamp(30px, 4.6vh, 58px);
  align-self: center;
  opacity: .92;
}

/* ══════════════════════════════════════════════════════════════════════
   1. ATTRACT
   ══════════════════════════════════════════════════════════════════════ */
.attract { background: var(--plum); }
.attract.leaving { animation: attract-out .45s var(--ease) forwards; }
@keyframes attract-out { to { opacity: 0; transform: scale(1.03); } }

.attract-media { position: absolute; inset: 0; overflow: hidden; }
.ad-layer {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  object-fit: cover;
  opacity: 0;
  transition: opacity .7s ease;
}
.ad-layer.showing { opacity: 1; }

.ad-fallback {
  position: absolute; inset: 0;
  display: flex; flex-direction: column;
  align-items: center; justify-content: center;
  gap: .4vh;
  background:
    radial-gradient(120% 70% at 50% 22%, var(--rose-deep) 0%, transparent 62%),
    linear-gradient(170deg, var(--plum-soft) 0%, var(--plum) 100%);
}
.fallback-mark {
  font-family: var(--font-display);
  font-weight: 800;
  font-size: clamp(42px, 9vw, 130px);
  letter-spacing: -.03em;
  color: var(--cream);
  animation: drift 7s ease-in-out infinite;
}
.fallback-sub {
  font-size: clamp(12px, 2.4vw, 30px);
  letter-spacing: .42em;
  text-transform: uppercase;
  color: var(--rose-soft);
  opacity: .72;
}
@keyframes drift { 0%,100% { transform: translateY(0); } 50% { transform: translateY(-1.4vh); } }

.attract-scrim {
  position: absolute; inset: 0;
  background: linear-gradient(to top,
    rgba(26,11,18,.92) 0%, rgba(26,11,18,.28) 22%, transparent 44%);
  pointer-events: none;
}

/* The start bar owns the bottom edge of the screen. */
.start-bar {
  position: absolute;
  left: 0; right: 0; bottom: 0;
  height: var(--bar-h);
  display: flex;
  align-items: center;
  justify-content: center;
  gap: clamp(10px, 2vw, 22px);
  background: var(--rose);
  color: #fff;
  overflow: hidden;
  box-shadow: 0 -10px 44px rgba(230, 0, 126, .38);
}
.start-bar:active { filter: brightness(.94); }

.start-bar-text {
  position: relative; z-index: 2;
  font-family: var(--font-display);
  font-weight: var(--weight-semi);
  font-size: clamp(19px, 3.6vw, 46px);
  letter-spacing: -.01em;
}
.start-bar-arrow {
  position: relative; z-index: 2;
  font-size: clamp(18px, 3vw, 38px);
  animation: nudge-up 1.9s ease-in-out infinite;
}
@keyframes nudge-up {
  0%,100% { transform: translateY(0); opacity: .75; }
  50%     { transform: translateY(-22%); opacity: 1; }
}

/* A slow sweep so the bar reads as live, not a static block. */
.start-bar-shine {
  position: absolute; top: 0; bottom: 0; width: 45%;
  background: linear-gradient(100deg, transparent, rgba(255,255,255,.22), transparent);
  animation: sweep 3.4s ease-in-out infinite;
}
@keyframes sweep {
  0%   { transform: translateX(-160%); }
  60%,100% { transform: translateX(340%); }
}


/* ══════════════════════════════════════════════════════════════════════
   WELCOME — dine mode, then language

   Both questions on one screen, numbered, with the continue button locked
   until both are answered. Splitting them across two screens adds a tap
   and a transition for no gain; leaving the button live invites people to
   skip past and get plated food they wanted packed.
   ══════════════════════════════════════════════════════════════════════ */
.page-welcome {
  display: flex;
  flex-direction: column;
  background:
    radial-gradient(120% 55% at 50% 0%, var(--brand-primary-tint) 0%, transparent 58%),
    var(--brand-page);
  padding: clamp(14px, 3vh, 40px) clamp(14px, 4vw, 44px) clamp(12px, 2.4vh, 28px);
  overflow-y: auto;
}

.welcome-head {
  flex: none;
  display: grid;
  grid-template-columns: auto 1fr auto;
  align-items: center;
  gap: clamp(8px, 1.6vw, 18px);
  margin-bottom: clamp(12px, 3vh, 34px);
}
.welcome-progress { display: flex; gap: 6px; }
.welcome-progress .dot {
  width: 7px; height: 7px;
  border-radius: 50%;
  background: var(--line-md);
  transition: background var(--t-base) var(--ease), transform var(--t-base) var(--ease);
}
.welcome-progress .dot.on { background: var(--brand-primary); transform: scale(1.3); }
.welcome-logo { height: clamp(34px, 6vh, 72px); margin: 0 auto; justify-self: center; }
.welcome-wordmark { font-size: clamp(20px, 4vw, 38px); text-align: center; }

.welcome-body {
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
  gap: clamp(16px, 3.4vh, 42px);
  justify-content: center;
}

.welcome-step { position: relative; }
.step-num {
  width: clamp(22px, 3.4vw, 32px);
  height: clamp(22px, 3.4vw, 32px);
  border-radius: 50%;
  background: var(--brand-primary);
  color: #fff;
  display: grid;
  place-items: center;
  font-weight: var(--weight-bold);
  font-size: clamp(11px, 1.6vw, 16px);
  margin-bottom: clamp(6px, 1.2vh, 12px);
}
.welcome-q {
  font-family: var(--font-display);
  font-size: clamp(17px, 3.4vw, 34px);
  font-weight: var(--weight-bold);
  letter-spacing: -.02em;
  color: var(--ink);
  margin-bottom: clamp(9px, 1.8vh, 20px);
}

.mode-grid, .lang-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: clamp(9px, 1.8vw, 20px);
}

.mode-card {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: clamp(4px, .8vh, 9px);
  padding: clamp(14px, 2.8vh, 34px) clamp(8px, 1.4vw, 18px);
  background: var(--surface);
  border: 2px solid var(--line);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-sm);
  transition: transform var(--t-fast) var(--ease),
              border-color var(--t-fast) var(--ease),
              box-shadow var(--t-fast) var(--ease);
}
.mode-card:active { transform: scale(.97); }
.mode-card.active {
  border-color: var(--brand-primary);
  background: var(--brand-primary-tint);
  box-shadow: var(--shadow-brand);
}
.mode-icon {
  width: clamp(44px, 8vw, 84px);
  height: clamp(44px, 8vw, 84px);
  border-radius: 50%;
  background: var(--brand-primary-soft);
  color: var(--brand-primary-deep);
  display: grid;
  place-items: center;
  font-size: clamp(21px, 4vw, 40px);
}
.mode-card.active .mode-icon { background: var(--brand-primary); color: #fff; }
.mode-label {
  font-family: var(--font-display);
  font-size: clamp(14px, 2.4vw, 24px);
  font-weight: var(--weight-bold);
  color: var(--ink);
}
.mode-sub {
  font-size: clamp(10px, 1.4vw, 14px);
  color: var(--ink-faint);
}

.lang-card {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  padding: clamp(12px, 2.4vh, 28px) clamp(8px, 1.4vw, 18px);
  background: var(--surface);
  border: 2px solid var(--line);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-sm);
  transition: transform var(--t-fast) var(--ease),
              border-color var(--t-fast) var(--ease);
}
.lang-card:active { transform: scale(.97); }
.lang-card.active {
  border-color: var(--brand-primary);
  background: var(--brand-primary-tint);
}
/* Kannada glyphs carry more vertical detail than Latin at the same size,
   so the native line gets the room. */
.lang-native {
  font-family: var(--font-display);
  font-size: clamp(18px, 3.6vw, 36px);
  font-weight: var(--weight-bold);
  line-height: 1.35;
  color: var(--ink);
}
.lang-latin {
  font-size: clamp(10px, 1.4vw, 14px);
  color: var(--ink-faint);
  letter-spacing: .06em;
  text-transform: uppercase;
}
.lang-card.active .lang-native { color: var(--brand-primary-deep); }

.welcome-cta {
  flex: none;
  margin-top: clamp(14px, 3vh, 36px);
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  width: 100%;
  padding: clamp(14px, 2.4vh, 26px);
  border-radius: var(--r-pill);
  background: var(--brand-primary-deep);
  color: #fff;
  font-family: var(--font-display);
  font-size: clamp(14px, 2.2vw, 22px);
  font-weight: var(--weight-bold);
  transition: opacity var(--t-base) var(--ease), transform var(--t-fast) var(--ease);
}
.welcome-cta:disabled { opacity: .35; }
.welcome-cta:active:not(:disabled) { transform: scale(.98); }


/* ══════════════════════════════════════════════════════════════════════
   PAYMENT — scan to pay
   ══════════════════════════════════════════════════════════════════════ */
.page-pay {
  display: flex;
  flex-direction: column;
  background: var(--brand-page);
  padding: clamp(12px, 2.6vh, 32px) clamp(14px, 3vw, 34px) 0;
  overflow: hidden;
}
.pay-head { flex: none; }

.pay-body {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: clamp(12px, 2.4vh, 28px);
  padding-bottom: clamp(10px, 2vh, 22px);
}

.pay-amount { text-align: center; }
.pay-amount-label {
  display: block;
  font-size: clamp(10px, 1.4vw, 14px);
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--ink-faint);
  font-weight: var(--weight-bold);
}
.pay-amount-value {
  display: block;
  font-family: var(--font-accent);
  font-size: clamp(30px, 7vw, 68px);
  font-weight: var(--weight-black);
  line-height: 1.05;
  color: var(--brand-primary-deep);
}
.pay-order-ref {
  display: block;
  font-family: var(--font-mono);
  font-size: clamp(10px, 1.3vw, 13px);
  color: var(--ink-faint);
  margin-top: 3px;
}

.pay-qr-card {
  width: 100%;
  max-width: clamp(240px, 72vw, 420px);
  background: var(--surface);
  border: 1.5px solid var(--line);
  border-radius: var(--r-xl);
  box-shadow: var(--shadow-md);
  padding: clamp(12px, 2.2vh, 24px);
  text-align: center;
}
.pay-qr-frame {
  aspect-ratio: 1 / 1;
  border-radius: var(--r-lg);
  background: var(--brand-page);
  border: 2px dashed var(--line-md);
  display: grid;
  place-items: center;
  overflow: hidden;
  margin-bottom: clamp(9px, 1.6vh, 18px);
}
.pay-qr-frame img { width: 100%; height: 100%; object-fit: contain; }
.pay-qr-placeholder {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  color: var(--ink-faint);
}
.pay-qr-placeholder i { font-size: clamp(48px, 13vw, 110px); opacity: .35; }
.pay-qr-placeholder span {
  font-size: clamp(10.5px, 1.4vw, 14px);
  letter-spacing: .1em;
  text-transform: uppercase;
  font-weight: var(--weight-semi);
}

.pay-apps-label {
  display: block;
  font-size: clamp(10px, 1.3vw, 13px);
  color: var(--ink-faint);
  margin-bottom: 6px;
}
.pay-app-row {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 6px;
}
.pay-app {
  padding: 4px 11px;
  border-radius: var(--r-pill);
  background: var(--brand-page);
  border: 1px solid var(--line);
  font-size: clamp(9.5px, 1.25vw, 12.5px);
  font-weight: var(--weight-semi);
  color: var(--ink-soft);
}

.pay-status {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  padding: 9px 18px;
  border-radius: var(--r-pill);
  background: var(--warn-soft);
  color: var(--ink);
  font-size: clamp(11px, 1.5vw, 15px);
  font-weight: var(--weight-semi);
}
.pay-dot {
  width: 9px; height: 9px;
  border-radius: 50%;
  background: var(--warn);
  animation: pay-pulse 1.4s ease-in-out infinite;
}
@keyframes pay-pulse { 50% { opacity: .25; transform: scale(.7); } }

.pay-actions {
  flex: none;
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: clamp(8px, 1.4vw, 14px);
  padding: clamp(10px, 1.8vh, 20px) 0 clamp(12px, 2.2vh, 26px);
}


/* ══════════════════════════════════════════════════════════════════════
   2. HUB — two halves, each a full section
   ══════════════════════════════════════════════════════════════════════ */
.hub {
  display: grid;
  grid-template-rows: auto 1fr auto;
  background: var(--cream);
  animation: fade-in .35s var(--ease);
}
@keyframes fade-in { from { opacity: 0; } }

.hub-bar {
  text-align: center;
  padding: clamp(16px, 3.4vh, 42px) 5vw clamp(10px, 2vh, 22px);
}
.hub-mark {
  font-family: var(--font-mono);
  font-size: clamp(10px, 1.4vw, 16px);
  letter-spacing: .42em;
  color: var(--rose);
  margin-bottom: .8vh;
}
.hub-title {
  font-family: var(--font-display);
  font-weight: var(--weight-bold);
  font-size: clamp(22px, 4.4vw, 54px);
  letter-spacing: -.02em;
  line-height: 1.08;
  color: var(--ink);
}

/* Portrait: stacked halves. Landscape: side by side. */
.hub-split {
  display: grid;
  grid-template-rows: 1fr 1fr;
  gap: clamp(10px, 1.6vh, 20px);
  padding: 0 clamp(14px, 4vw, 40px);
  min-height: 0;
}

.half {
  position: relative;
  border-radius: var(--r-xl);
  overflow: hidden;
  display: grid;
  place-items: center;
  min-height: 0;
  cursor: pointer;
  transition: transform .2s var(--ease), box-shadow .2s var(--ease);
}
.half:active { transform: scale(.985); }

.half-order  { background: linear-gradient(150deg, var(--rose) 0%, var(--rose-deep) 100%); color: #fff; }
.half-selfie { background: linear-gradient(150deg, var(--plum-lift) 0%, var(--plum) 100%); color: var(--cream); }

/* A soft light source so each half has depth rather than reading flat. */
.half-glow {
  position: absolute;
  width: 120%; aspect-ratio: 1;
  top: -55%; left: -10%;
  background: radial-gradient(circle, rgba(255,255,255,.20) 0%, transparent 62%);
  pointer-events: none;
}
.half-selfie .half-glow { background: radial-gradient(circle, rgba(255,228,234,.14) 0%, transparent 62%); }

.half-inner {
  position: relative; z-index: 2;
  text-align: center;
  padding: clamp(14px, 3vh, 36px) clamp(16px, 5vw, 48px);
  display: flex; flex-direction: column; align-items: center;
  gap: clamp(4px, .9vh, 12px);
}
.half-icon {
  font-size: clamp(34px, 8vw, 92px);
  opacity: .95;
  margin-bottom: clamp(2px, .8vh, 10px);
}
.half-title {
  font-family: var(--font-display);
  font-weight: var(--weight-bold);
  font-size: clamp(22px, 5vw, 58px);
  line-height: 1.06;
  letter-spacing: -.02em;
}
.half-sub {
  font-size: clamp(12px, 2vw, 23px);
  opacity: .8;
  max-width: 26ch;
  line-height: 1.35;
}
.half-cta {
  display: inline-flex; align-items: center; gap: 8px;
  margin-top: clamp(6px, 1.4vh, 18px);
  padding: clamp(8px, 1.3vh, 15px) clamp(16px, 3vw, 30px);
  border-radius: 999px;
  background: rgba(255,255,255,.18);
  font-size: clamp(12px, 1.8vw, 20px);
  font-weight: 600;
}
.half-selfie .half-cta { background: rgba(255,228,234,.14); }

.hub-back {
  justify-self: center;
  margin: clamp(10px, 2vh, 22px) 0 clamp(14px, 2.6vh, 30px);
  padding: clamp(10px, 1.5vh, 17px) clamp(24px, 4vw, 44px);
  border-radius: 999px;
  border: 1.5px solid var(--line-md);
  color: var(--ink-soft);
  font-size: clamp(12px, 1.7vw, 19px);
  font-weight: 600;
}


/* ══════════════════════════════════════════════════════════════════════
   3. PAGES
   ══════════════════════════════════════════════════════════════════════ */
.page {
  display: flex;
  flex-direction: column;
  min-height: 0;
  animation: page-in .3s var(--ease);
}
@keyframes page-in { from { transform: translateY(2vh); opacity: 0; } }

.page-order  { background: var(--cream); padding: clamp(14px, 3.4vh, 40px) clamp(14px, 4vw, 44px) 0; }

.page-head {
  flex: none;
  display: flex;
  align-items: flex-start;
  gap: clamp(10px, 2vw, 20px);
  margin-bottom: clamp(10px, 2vh, 22px);
  min-width: 0;
}
.page-titles { flex: 1; min-width: 0; }

.back-btn {
  flex: none;
  width: clamp(38px, 5.6vw, 60px);
  aspect-ratio: 1;
  border-radius: 50%;
  background: var(--cream-warm);
  border: 1.5px solid var(--line);
  color: var(--ink);
  display: grid; place-items: center;
  font-size: clamp(17px, 2.6vw, 28px);
}
.back-btn.dark {
  background: rgba(255,247,248,.08);
  border-color: rgba(255,228,234,.18);
  color: var(--rose-soft);
}
.back-btn:active { transform: scale(.93); }

.page-eyebrow {
  font-family: var(--font-mono);
  font-size: clamp(9px, 1.2vw, 14px);
  letter-spacing: .32em;
  text-transform: uppercase;
  color: var(--rose);
  margin-bottom: .4vh;
}
.selfie-eyebrow { color: var(--rose-soft); opacity: .85; }
.page-title {
  font-family: var(--font-display);
  font-weight: var(--weight-bold);
  font-size: clamp(20px, 4vw, 48px);
  letter-spacing: -.02em;
  line-height: 1.05;
}
.selfie-title { color: var(--cream); }


/* ══════════════════════════════════════════════════════════════════════
   ORDER — self-service kiosk layout

   Categories left, products centre, the order itself always along the
   bottom. Three things drive the shape:

   * A vertical category rail keeps every category reachable without a
     sideways scroll. On a screen someone is standing at, off-screen
     categories are categories nobody buys from.
   * Price above the image, name below. People scan a grid by picture and
     price; the name confirms the choice rather than leading it.
   * The order stays visible. Behind a badge, people lose track of what
     they've added and what it costs.
   ══════════════════════════════════════════════════════════════════════ */

/* The page owns its own padding now — the rail must reach the left edge
   and the tray the bottom, so the outer padding moves inward. */
.page-order { padding: 0 !important; overflow: hidden; }
/* Logo on top and centred, title centred beneath it, back arrow floated
   out of flow at the left.
   
   The previous version put the arrow and the title in the same grid
   column, so they stacked and the logo ended up beside a two-line block
   rather than above it — which is what made it look pasted on. */
.order-head, .pay-head {
  flex: none;
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0;                       /* the eyebrow supplies its own spacing */
  padding: clamp(10px, 2vh, 24px) clamp(58px, 12vw, 96px) clamp(8px, 1.5vh, 16px);
  margin-bottom: 0;
  text-align: center;
}
.order-head .page-logo, .pay-head .page-logo {
  height: clamp(44px, 7.5vh, 92px);
  margin: 0;
  flex: none;
}
.order-head .page-titles, .pay-head .page-titles {
  width: 100%;
  min-width: 0;
  text-align: center;
}

.order-body {
  flex: 1;
  min-height: 0;
  display: grid;
  grid-template-columns: clamp(92px, 15vw, 168px) 1fr;
}

/* ── Category rail ─────────────────────────────────────────────────── */
.cat-rail {
  overflow-y: auto;
  overflow-x: hidden;
  background: var(--surface);
  border-right: 1.5px solid var(--line);
  padding: clamp(6px, 1vh, 12px) clamp(4px, .7vw, 9px);
  display: flex;
  flex-direction: column;
  gap: clamp(4px, .7vh, 9px);
  scrollbar-width: none;
}
.cat-rail::-webkit-scrollbar { display: none; }

@keyframes pop-in {
  from { opacity: 0; transform: scale(.72) translateY(8px); }
  60%  { transform: scale(1.04) translateY(0); }
  to   { opacity: 1; transform: scale(1) translateY(0); }
}

.cat-tile {
  flex: none;
  animation: pop-in .42s var(--ease) both;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 5px;
  padding: clamp(7px, 1.1vh, 13px) 5px;
  border-radius: var(--r-md);
  border: 2px solid transparent;
  transition: background var(--t-fast) var(--ease),
              border-color var(--t-fast) var(--ease),
              transform var(--t-fast) var(--ease);
}
.cat-tile:active { transform: scale(.97); }
.cat-tile.active {
  background: var(--brand-primary-tint);
  border-color: var(--brand-primary);
  animation: cat-pop .3s var(--ease);
}
@keyframes cat-pop {
  40% { transform: scale(1.07); }
}

.cat-thumb {
  width: clamp(42px, 7vw, 76px);
  height: clamp(42px, 7vw, 76px);
  border-radius: var(--r-sm);
  overflow: hidden;
  background: var(--raised);
  display: grid;
  place-items: center;
  flex: none;
}
.cat-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
.cat-fallback {
  font-size: clamp(19px, 3.2vw, 34px);
  color: var(--brand-primary);
  opacity: .5;
}
.cat-tile.active .cat-fallback { opacity: 1; }

.cat-name {
  font-size: clamp(9.5px, 1.25vw, 14px);
  font-weight: var(--weight-semi);
  line-height: 1.2;
  text-align: center;
  color: var(--ink-soft);
  overflow-wrap: anywhere;
}
.cat-tile.active .cat-name { color: var(--brand-primary-deep); }

/* ── Product grid ──────────────────────────────────────────────────── */
.product-grid {
  overflow-y: auto;
  padding: clamp(10px, 1.8vh, 22px) clamp(10px, 1.6vw, 22px);
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(clamp(128px, 21vw, 220px), 1fr));
  gap: clamp(9px, 1.5vw, 18px);
  align-content: start;
}
.product-grid::-webkit-scrollbar { width: 6px; }
.product-grid::-webkit-scrollbar-thumb {
  background: var(--line-md); border-radius: 3px;
}

.card {
  display: flex;
  flex-direction: column;
  align-items: center;
  animation: pop-in .38s var(--ease) both;
  padding: clamp(8px, 1.3vh, 14px) clamp(6px, 1vw, 12px) clamp(10px, 1.6vh, 16px);
  background: var(--surface);
  border: 1.5px solid var(--line);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-sm);
  text-align: center;
  transition: transform var(--t-fast) var(--ease),
              border-color var(--t-fast) var(--ease),
              box-shadow var(--t-fast) var(--ease);
}
.card:active {
  transform: scale(.97);
  border-color: var(--brand-primary);
  box-shadow: var(--shadow-md);
}

/* Price leads, as on the reference. */
.card-price {
  font-family: var(--font-accent);
  font-size: clamp(14px, 2.1vw, 22px);
  font-weight: var(--weight-bold);
  color: var(--ink);
  margin-bottom: 4px;
}

.card-media {
  position: relative;
  width: 100%;
  aspect-ratio: 1 / 1;
  border-radius: var(--r-md);
  overflow: hidden;
  background: var(--brand-primary-tint);
  display: grid;
  place-items: center;
  margin-bottom: clamp(5px, .9vh, 10px);
}
.card-media img { width: 100%; height: 100%; object-fit: cover; display: block; }

/* An item with no photo still has to look deliberate — the reference prints
   "NO IMAGE" rather than leaving a hole. */
.no-image-text {
  font-size: clamp(9px, 1.2vw, 13px);
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--ink-faint);
  font-weight: var(--weight-bold);
}
.card-media.no-image::after {
  content: "No image";
  font-size: clamp(9px, 1.2vw, 13px);
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--ink-faint);
  font-weight: var(--weight-bold);
}

.veg-dot {
  position: absolute;
  top: 6px; left: 6px;
  width: clamp(12px, 1.7vw, 17px);
  height: clamp(12px, 1.7vw, 17px);
  border-radius: 3px;
  border: 2px solid var(--veg);
  background: #fff;
}
.veg-dot::after {
  content: "";
  position: absolute;
  inset: 2px;
  border-radius: 50%;
  background: var(--veg);
}
.veg-dot.nonveg { border-color: var(--nonveg); }
.veg-dot.nonveg::after { background: var(--nonveg); }

.card-name {
  font-family: var(--font-display);
  font-size: clamp(11px, 1.5vw, 16px);
  font-weight: var(--weight-semi);
  line-height: 1.25;
  color: var(--ink);
}
.card-note {
  font-size: clamp(9.5px, 1.15vw, 12.5px);
  color: var(--ink-faint);
  margin-top: 3px;
}

.grid-loading, .grid-empty {
  grid-column: 1 / -1;
  padding: clamp(30px, 6vh, 70px) 20px;
  text-align: center;
  color: var(--ink-faint);
  font-size: clamp(13px, 1.8vw, 18px);
}

/* ── Order tray ────────────────────────────────────────────────────── */
.order-tray {
  flex: none;
  background: var(--surface);
  border-top: 2px solid var(--line);
  box-shadow: 0 -6px 24px rgba(51, 65, 85, .07);
  display: flex;
  flex-direction: column;
  max-height: 42vh;
}

.tray-head {
  display: flex;
  align-items: baseline;
  gap: clamp(7px, 1.2vw, 14px);
  padding: clamp(8px, 1.3vh, 14px) clamp(12px, 1.8vw, 22px);
  background: var(--brand-primary-deep);
  color: #fff;
}
.tray-head .sep { flex: 1; }
.tray-title {
  font-family: var(--font-display);
  font-size: clamp(12px, 1.7vw, 17px);
  font-weight: var(--weight-bold);
  letter-spacing: .04em;
  text-transform: uppercase;
}
.tray-count { font-size: clamp(10.5px, 1.35vw, 14px); opacity: .85; }
.tray-tax   { font-size: clamp(10px, 1.25vw, 13px); opacity: .8; }
.tray-total {
  font-family: var(--font-accent);
  font-size: clamp(16px, 2.4vw, 26px);
  font-weight: var(--weight-bold);
}

.tray-lines {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  padding: clamp(4px, .7vh, 9px) clamp(10px, 1.6vw, 20px);
}
.tray-empty {
  padding: clamp(12px, 2.2vh, 26px) 0;
  text-align: center;
  color: var(--ink-faint);
  font-size: clamp(11.5px, 1.5vw, 15px);
}

.tray-line {
  display: flex;
  align-items: center;
  gap: clamp(7px, 1.1vw, 13px);
  padding: clamp(6px, 1vh, 11px) 0;
  border-bottom: 1px solid var(--line);
}
.tray-line:last-child { border-bottom: none; }

.line-qty {
  flex: none;
  min-width: clamp(24px, 3.2vw, 36px);
  font-family: var(--font-mono);
  font-size: clamp(11px, 1.4vw, 15px);
  font-weight: var(--weight-bold);
  color: var(--brand-primary-deep);
}
.line-name {
  flex: 1;
  min-width: 0;
  font-size: clamp(11.5px, 1.5vw, 16px);
  font-weight: var(--weight-semi);
  color: var(--ink);
  line-height: 1.3;
}
.line-variant, .line-note {
  display: block;
  font-weight: var(--weight-normal);
  font-size: clamp(9.5px, 1.2vw, 12.5px);
  color: var(--ink-faint);
}
.line-money {
  flex: none;
  font-family: var(--font-accent);
  font-size: clamp(12px, 1.6vw, 17px);
  font-weight: var(--weight-semi);
  color: var(--ink);
}
.line-controls { flex: none; display: flex; gap: 5px; }
.line-btn {
  width: clamp(30px, 4.2vw, 44px);
  height: clamp(30px, 4.2vw, 44px);
  border-radius: 50%;
  background: var(--brand-primary-tint);
  color: var(--brand-primary-deep);
  display: grid;
  place-items: center;
  font-size: clamp(13px, 1.8vw, 19px);
  transition: background var(--t-fast) var(--ease), transform var(--t-fast) var(--ease);
}
.line-btn:active { background: var(--brand-primary-soft); transform: scale(.92); }

.tray-actions {
  flex: none;
  display: grid;
  grid-template-columns: 1fr;   /* Done alone now that Cancel is gone */
  gap: clamp(7px, 1.1vw, 13px);
  padding: clamp(8px, 1.3vh, 15px) clamp(10px, 1.6vw, 20px);
  border-top: 1.5px solid var(--line);
}
.tray-btn {
  padding: clamp(11px, 1.9vh, 20px);
  border-radius: var(--r-pill);
  font-family: var(--font-display);
  font-size: clamp(13px, 1.9vw, 20px);
  font-weight: var(--weight-bold);
  color: #fff;
  transition: filter var(--t-fast) var(--ease), transform var(--t-fast) var(--ease);
}
.tray-btn:active:not(:disabled) { transform: scale(.98); }
.tray-btn:disabled { opacity: .4; }
.tray-btn.cancel { background: var(--danger); }
.tray-btn.done   { background: var(--ok); }
.tray-btn.ghost {
  background: transparent;
  border: 1.5px solid var(--line-md);
  color: var(--ink-soft);
}

.confirm-actions {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 10px;
  padding: 0 clamp(14px, 2.4vw, 26px) clamp(14px, 2.4vh, 26px);
}

/* Short screens: the tray gives up height before the grid does — browsing
   is what people came to do. */
@media (max-height: 780px) {
  .order-tray { max-height: 34vh; }
}


/* ══════════════════════════════════════════════════════════════════════
   4. SELFIE
   ══════════════════════════════════════════════════════════════════════ */
.shot-counter {
  flex: none;
  font-family: var(--font-mono);
  font-size: clamp(12px, 1.8vw, 21px);
  color: var(--rose-soft);
  opacity: .85;
  white-space: nowrap;
}


.stage-waking {
  position: absolute; inset: 0;
  display: flex; flex-direction: column;
  align-items: center; justify-content: center;
  gap: 14px;
  background: var(--plum);
  color: var(--rose-soft);
  font-size: clamp(12px, 1.8vw, 19px);
}
.waking-spinner {
  width: 34px; aspect-ratio: 1;
  border: 3px solid rgba(255,228,234,.2);
  border-top-color: var(--rose);
  border-radius: 50%;
  animation: spin .85s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }


.countdown {
  position: absolute; inset: 0;
  display: grid; place-items: center;
  font-family: var(--font-display);
  font-weight: 800;
  font-size: clamp(80px, 22vw, 260px);
  color: #fff;
  text-shadow: 0 6px 40px rgba(0,0,0,.55);
  background: rgba(26,11,18,.35);
}
.countdown.tick { animation: tick 1s var(--ease); }
@keyframes tick {
  0%   { transform: scale(1.5); opacity: 0; }
  22%  { transform: scale(1);   opacity: 1; }
  100% { transform: scale(.85); opacity: .15; }
}

.flash { position: absolute; inset: 0; background: #fff; opacity: 0; pointer-events: none; }
.flash.fire { animation: flash .4s ease-out; }
@keyframes flash { 0% { opacity: .92; } 100% { opacity: 0; } }

/* ── Filmstrip ──────────────────────────────────────────────────────── */
.filmstrip::-webkit-scrollbar { display: none; }

.strip-shot {
  position: relative;
  flex: none;
  aspect-ratio: 3 / 4;
  height: clamp(52px, 8vh, 116px);
  border-radius: var(--r-sm);
  overflow: hidden;
  border: 2px solid rgba(255,228,234,.22);
  animation: chip-in .32s var(--ease);
  cursor: pointer;
}
.strip-shot img { width: 100%; height: 100%; object-fit: cover; }
.strip-shot:active { transform: scale(.95); }

.strip-edit {
  position: absolute; inset: auto 0 0 0;
  padding: 3px 0;
  background: rgba(26,11,18,.72);
  color: var(--rose-soft);
  font-size: clamp(8px, 1.05vw, 12px);
  font-weight: 700;
  letter-spacing: .06em;
  text-align: center;
  text-transform: uppercase;
}
.strip-index {
  position: absolute; top: 3px; left: 4px;
  width: 17px; aspect-ratio: 1;
  border-radius: 50%;
  background: rgba(26,11,18,.76);
  color: #fff;
  display: grid; place-items: center;
  font-size: 10px; font-weight: 700;
}

/* ── Background rail ────────────────────────────────────────────────
   Real thumbnails, not arrows. A customer choosing a backdrop needs to
   see the backdrop. */

.bg-rail {
  flex: none;
  min-width: 0; max-width: 100%;
  display: flex;
  gap: clamp(6px, 1vw, 12px);
  overflow-x: auto;
  margin-top: clamp(5px, .9vh, 10px);
  padding-bottom: 4px;
  scrollbar-width: none;
}
.bg-rail::-webkit-scrollbar { display: none; }

.bg-chip {
  flex: none;
  width: clamp(56px, 9vw, 108px);
  border-radius: var(--r-sm);
  overflow: hidden;
  border: 2px solid rgba(255, 228, 234, .16);
  background: rgba(255, 247, 248, .06);
  transition: border-color var(--t-fast) var(--ease),
              transform var(--t-fast) var(--ease);
}
.bg-chip:active { transform: scale(.95); }
.bg-chip.active {
  border-color: var(--rose);
  box-shadow: 0 0 0 2px rgba(230, 0, 126, .28);
}

.bg-chip img,
.bg-chip .bg-none {
  display: block;
  width: 100%;
  aspect-ratio: 4 / 3;
  object-fit: cover;
}
.bg-chip .bg-none {
  display: grid; place-items: center;
  background: rgba(255, 247, 248, .07);
  color: var(--rose-soft);
  font-size: clamp(15px, 2.4vw, 26px);
  opacity: .7;
}
.bg-name {
  display: block;
  padding: 4px 5px;
  font-size: clamp(8.5px, 1.05vw, 12px);
  color: var(--rose-soft);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  text-align: center;
}
.bg-chip.active .bg-name { color: #fff; font-weight: var(--weight-semi); }

/* ── Filter rail ────────────────────────────────────────────────────── */
.filter-rail::-webkit-scrollbar { display: none; }

.filter-chip {
  flex: none;
  display: flex; align-items: center; gap: 6px;
  padding: clamp(6px, .95vh, 12px) clamp(11px, 1.7vw, 22px);
  border-radius: 999px;
  background: rgba(255,247,248,.07);
  border: 1.5px solid rgba(255,228,234,.16);
  color: var(--rose-soft);
  font-size: clamp(10.5px, 1.5vw, 18px);
  font-weight: 500;
  white-space: nowrap;
  transition: all .16s var(--ease);
}
.filter-chip.active { background: var(--rose); border-color: var(--rose); color: #fff; }
.filter-swatch {
  width: 10px; aspect-ratio: 1; border-radius: 50%; flex: none;
  /* A pale swatch vanishes against the white active chip without this. */
  box-shadow: inset 0 0 0 1px rgba(0, 0, 0, .18);
}

/* ── Shutter ────────────────────────────────────────────────────────── */
.selfie-actions {
  flex: none;
  display: flex; align-items: center; justify-content: center;
  gap: clamp(18px, 6vw, 60px);
  margin-top: clamp(10px, 1.8vh, 22px);
}
.ghost-btn {
  width: clamp(38px, 5.6vw, 60px);
  aspect-ratio: 1;
  border-radius: 50%;
  background: rgba(255,247,248,.08);
  border: 1.5px solid rgba(255,228,234,.18);
  color: var(--rose-soft);
  display: grid; place-items: center;
  font-size: clamp(17px, 2.6vw, 29px);
}
.ghost-btn:active { background: rgba(255,247,248,.16); }

.shutter:active .shutter-core { transform: scale(.86); }
.shutter:disabled { opacity: .4; }

.selfie-foot {
  flex: none;
  display: flex;
  gap: clamp(8px, 1.4vw, 14px);
  margin-top: clamp(9px, 1.6vh, 20px);
}
.foot-btn {
  flex: 1;
  padding: clamp(12px, 1.9vh, 22px);
  border-radius: 999px;
  font-weight: 700;
  font-size: clamp(13px, 1.9vw, 22px);
  display: flex; align-items: center; justify-content: center; gap: 9px;
  animation: chip-in .3s var(--ease);
}
.foot-btn.primary { background: var(--cream); color: var(--plum); }
.foot-btn.primary i { color: var(--rose); font-size: 1.2em; }
.foot-btn.ghost {
  flex: 0 0 auto;
  padding-inline: clamp(16px, 3vw, 30px);
  background: transparent;
  border: 1.5px solid rgba(255,228,234,.22);
  color: var(--rose-soft);
}


/* ══════════════════════════════════════════════════════════════════════
   MODALS — centred
   ══════════════════════════════════════════════════════════════════════ */
.modal-backdrop {
  position: fixed; inset: 0;
  background: rgba(26,11,18,.62);
  backdrop-filter: blur(7px);
  z-index: 60;
  display: none;
  align-items: center;
  justify-content: center;
  padding: clamp(14px, 4vw, 48px);
}
.modal-backdrop.open { display: flex; animation: fade-in .2s ease; }

.modal {
  position: relative;
  width: 100%;
  max-width: min(92vw, 620px);
  max-height: 88vh;
  background: var(--cream);
  border-radius: var(--r-xl);
  display: flex;
  flex-direction: column;
  overflow: hidden;
  box-shadow: var(--shadow-lg);
  animation: modal-pop .34s var(--ease);
}
@keyframes modal-pop {
  from { transform: translateY(18px) scale(.94); opacity: 0; }
}

.modal-x {
  position: absolute; top: 12px; right: 12px;
  z-index: 5;
  width: clamp(34px, 4.6vw, 46px);
  aspect-ratio: 1;
  border-radius: 50%;
  background: rgba(26,11,18,.5);
  color: #fff;
  display: grid; place-items: center;
  font-size: clamp(16px, 2.2vw, 22px);
  backdrop-filter: blur(4px);
}
.modal-x:active { transform: scale(.9); }

.modal-hero {
  position: relative;
  flex: none;
  aspect-ratio: 16 / 10;
  max-height: 34vh;
  background: linear-gradient(150deg, var(--rose-tint), var(--cream-warm));
  overflow: hidden;
}
.modal-hero img { width: 100%; height: 100%; object-fit: cover; display: block; }

/* A product with no photo collapses the hero rather than showing an empty
   band — the modal reads better tight than padded with nothing. */
.modal-hero.empty { aspect-ratio: auto; height: clamp(70px, 11vh, 110px); }
.modal-hero.empty::after {
  content: "";
  position: absolute; inset: 0;
  background: radial-gradient(circle at 50% 60%, rgba(225,29,72,.14), transparent 62%);
}
.modal-veg {
  position: absolute; top: 12px; left: 12px;
  width: 20px; aspect-ratio: 1;
  border-radius: 4px;
  border: 2px solid var(--veg);
  background: #fff;
  display: grid; place-items: center;
}
.modal-veg::after { content: ""; width: 42%; aspect-ratio: 1; border-radius: 50%; background: var(--veg); }
.modal-veg.nonveg { border-color: var(--nonveg); }
.modal-veg.nonveg::after { background: var(--nonveg); }

.modal-body {
  padding: clamp(16px, 2.6vh, 28px) clamp(18px, 4vw, 34px);
  overflow-y: auto;
  flex: 1;
  min-height: 0;
}
.modal-title {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: clamp(19px, 3.2vw, 34px);
  line-height: 1.15;
  margin-bottom: 8px;
  color: var(--ink);
}
.modal-desc {
  font-size: clamp(12.5px, 1.8vw, 20px);
  line-height: 1.5;
  color: var(--ink-soft);
  margin-bottom: 14px;
}
.modal-meta {
  display: flex; flex-wrap: wrap; gap: 7px;
  margin-bottom: 16px;
}
.meta-chip {
  padding: 5px 12px;
  border-radius: 999px;
  background: var(--cream-warm);
  border: 1.5px solid var(--line);
  font-size: clamp(10.5px, 1.4vw, 15px);
  color: var(--ink-soft);
  font-weight: 500;
}

.modal-cta {
  flex: none;
  margin: 0 clamp(18px, 4vw, 34px) clamp(18px, 3vh, 30px);
  padding: clamp(14px, 2.1vh, 24px);
  border-radius: 999px;
  background: var(--rose);
  color: #fff;
  font-weight: 700;
  font-size: clamp(14px, 2vw, 23px);
  display: flex; align-items: center; justify-content: center; gap: 10px;
}
.modal-cta:disabled { background: var(--line-md); color: var(--ink-faint); }
.modal-cta.ghost {
  background: transparent;
  border: 1.5px solid var(--line-md);
  color: var(--ink-soft);
}
.modal-actions {
  display: flex; gap: 10px;
  margin: 0 clamp(18px, 4vw, 34px) clamp(18px, 3vh, 30px);
}
.modal-actions .modal-cta { margin: 0; flex: 1; }

/* Variants + quantity */
.variant-list { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 18px; }
.variant-chip {
  padding: clamp(9px,1.3vh,14px) clamp(14px,2.4vw,24px);
  border-radius: 999px;
  background: #fff;
  border: 1.5px solid var(--line-md);
  font-size: clamp(12px, 1.7vw, 19px);
  font-weight: 500;
  color: var(--ink-soft);
  transition: all .15s var(--ease);
}
.variant-chip.active { border-color: var(--rose); background: var(--rose-soft); color: var(--rose-deep); font-weight: 600; }

.qty-row { display: flex; align-items: center; justify-content: space-between; gap: 14px; }
.qty-label { font-size: clamp(12.5px, 1.8vw, 20px); font-weight: 600; color: var(--ink-soft); }
.qty-stepper {
  display: flex; align-items: center; gap: clamp(10px, 2vw, 18px);
  background: #fff;
  border: 1.5px solid var(--line-md);
  border-radius: 999px;
  padding: 5px;
}
.qty-btn {
  width: clamp(34px, 4.6vw, 48px);
  aspect-ratio: 1;
  border-radius: 50%;
  background: var(--cream-warm);
  font-size: clamp(18px, 2.6vw, 28px);
  color: var(--ink);
  display: grid; place-items: center;
}
.qty-btn:active { background: var(--rose-soft); }
.qty-value {
  font-family: var(--font-display);
  font-size: clamp(18px, 2.6vw, 30px);
  font-weight: 600;
  min-width: clamp(26px, 4vw, 44px);
  text-align: center;
}

/* Cart lines */
.cart-line {
  display: flex; align-items: center; gap: clamp(9px, 1.6vw, 15px);
  padding: clamp(9px, 1.4vh, 15px) 0;
  border-bottom: 1px solid var(--line);
}
.cart-line-img {
  width: clamp(44px, 6.5vw, 74px);
  aspect-ratio: 1;
  border-radius: var(--r-sm);
  object-fit: cover;
  background: var(--cream-warm);
  flex: none;
}
.cart-line-main { flex: 1; min-width: 0; }
.cart-line-name { font-weight: 600; font-size: clamp(12.5px, 1.8vw, 20px); }
.cart-line-meta { font-size: clamp(10.5px, 1.4vw, 16px); color: var(--ink-faint); }
.cart-line-qty { display: flex; align-items: center; gap: clamp(7px,1.2vw,12px); flex: none; }
.cart-line-price {
  font-family: var(--font-display); font-weight: 600;
  font-size: clamp(12.5px, 1.9vw, 21px); color: var(--rose);
  min-width: clamp(52px, 7vw, 76px); text-align: right;
}
.mini-btn {
  width: clamp(27px, 3.8vw, 38px); aspect-ratio: 1;
  border-radius: 50%; background: var(--cream-warm);
  border: 1.5px solid var(--line);
  display: grid; place-items: center;
  font-size: clamp(14px, 1.9vw, 20px);
}

.coupon-row { display: flex; gap: 8px; margin: 18px 0 6px; }
.coupon-input {
  flex: 1; min-width: 0;
  padding: clamp(10px, 1.5vh, 17px) 15px;
  border-radius: var(--r-sm);
  border: 1.5px solid var(--line-md);
  background: #fff;
  font-family: var(--font-mono);
  font-size: clamp(12.5px, 1.8vw, 19px);
  text-transform: uppercase;
}
.coupon-input:focus { outline: none; border-color: var(--rose); }
.coupon-apply {
  flex: none;
  padding: 0 clamp(16px, 3vw, 30px);
  border-radius: var(--r-sm);
  background: var(--ink);
  color: var(--cream);
  font-weight: 600;
  font-size: clamp(12.5px, 1.7vw, 18px);
}
.coupon-msg { font-size: clamp(11px, 1.5vw, 16px); min-height: 20px; }
.coupon-msg.ok  { color: var(--mint); }
.coupon-msg.bad { color: var(--rose); }

.totals { margin-top: 16px; font-size: clamp(12.5px, 1.8vw, 20px); }
.totals > div { display: flex; justify-content: space-between; padding: 6px 0; color: var(--ink-soft); }
.totals-discount { color: var(--mint) !important; }
.totals-grand {
  border-top: 1.5px solid var(--line);
  margin-top: 6px; padding-top: 12px !important;
  font-family: var(--font-accent);
  font-weight: var(--weight-semi);
  font-size: clamp(17px, 2.6vw, 31px);
  color: var(--ink) !important;
}

/* ── Photo editor ───────────────────────────────────────────────────── */
.editor-preview {
  border-radius: var(--r-lg);
  overflow: hidden;
  background: var(--plum);
  margin-bottom: 16px;
  max-height: 42vh;
}
.editor-preview img { width: 100%; height: 100%; object-fit: contain; display: block; max-height: 42vh; }
.editor-label {
  font-size: clamp(11px, 1.4vw, 15px);
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--ink-faint);
  font-weight: 700;
  margin-bottom: 9px;
}
.editor-filters {
  display: flex; flex-wrap: wrap; gap: 7px;
  max-height: 26vh; overflow-y: auto;
}
.editor-filters .filter-chip {
  background: #fff;
  border-color: var(--line-md);
  color: var(--ink-soft);
}
.editor-filters .filter-chip.active { background: var(--rose); border-color: var(--rose); color: #fff; }

/* ── Chooser ────────────────────────────────────────────────────────── */
.chooser-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(clamp(90px, 24vw, 150px), 1fr));
  gap: clamp(8px, 1.6vw, 16px);
}
.chooser-tile {
  position: relative;
  aspect-ratio: 3 / 4;
  border-radius: var(--r-md);
  overflow: hidden;
  border: 3px solid transparent;
  transition: border-color .16s var(--ease), transform .16s var(--ease);
}
.chooser-tile img { width: 100%; height: 100%; object-fit: cover; display: block; }
.chooser-tile.picked { border-color: var(--rose); }
.chooser-tile:active { transform: scale(.96); }
.chooser-tile::after {
  content: "";
  position: absolute; inset: 0;
  background: rgba(26,11,18,.42);
  opacity: 1;
  transition: opacity .16s;
}
.chooser-tile.picked::after { opacity: 0; }
.chooser-check {
  position: absolute; top: 7px; right: 7px;
  z-index: 2;
  width: clamp(22px, 3.2vw, 30px);
  aspect-ratio: 1;
  border-radius: 50%;
  border: 2px solid #fff;
  background: rgba(26,11,18,.4);
  color: #fff;
  display: grid; place-items: center;
  font-size: clamp(12px, 1.7vw, 17px);
}
.chooser-tile.picked .chooser-check { background: var(--rose); border-color: var(--rose); }

/* ── Instagram send ─────────────────────────────────────────────────── */
.tag-notice {
  display: flex; gap: 12px;
  padding: clamp(13px, 1.9vh, 21px);
  border-radius: var(--r-md);
  background: var(--rose-soft);
  border: 1.5px dashed var(--rose);
  color: var(--rose-deep);
  font-size: clamp(12.5px, 1.8vw, 20px);
  line-height: 1.45;
  margin-bottom: 16px;
}
.tag-notice i { font-size: 1.5em; flex: none; }

.tier-list { display: flex; flex-direction: column; gap: 6px; margin-bottom: 18px; }
.tier-row {
  display: flex; justify-content: space-between; align-items: center;
  padding: clamp(8px, 1.2vh, 14px) 14px;
  border-radius: var(--r-sm);
  background: #fff;
  border: 1.5px solid var(--line);
  font-size: clamp(11.5px, 1.7vw, 18px);
}
.tier-row:first-child { border-color: var(--rose); background: var(--rose-soft); }
.tier-pct { font-family: var(--font-display); font-weight: 600; color: var(--rose); }

.agree-row {
  display: flex; align-items: center; gap: 12px;
  font-size: clamp(12.5px, 1.8vw, 19px);
  color: var(--ink);
  padding: 10px 0;
}
.agree-row input { width: 24px; height: 24px; accent-color: var(--rose); flex: none; }

.qr-wrap { text-align: center; padding: 8px 0; }
.qr {
  width: min(58vw, 280px);
  aspect-ratio: 1;
  border-radius: var(--r-md);
  background: #fff;
  padding: 12px;
}
.pair-code {
  font-family: var(--font-mono);
  font-size: clamp(18px, 3.2vw, 34px);
  letter-spacing: .16em;
  margin-top: 12px;
  color: var(--ink);
}
.pair-expiry {
  font-family: var(--font-mono);
  font-size: clamp(10.5px, 1.4vw, 15px);
  color: var(--ink-faint);
  margin-top: 8px;
}

/* Live progress while the customer works on their own phone. Each step
   lights up as the booth sees it happen — the reassurance that the scan
   worked matters more than the information itself. */
.claim-progress {
  display: flex;
  flex-direction: column;
  gap: clamp(6px, 1vh, 12px);
  margin-top: clamp(14px, 2.4vh, 26px);
  padding-top: clamp(12px, 2vh, 22px);
  border-top: 1.5px solid var(--line);
}
.claim-step {
  display: flex;
  align-items: center;
  gap: 10px;
  font-size: clamp(12px, 1.7vw, 18px);
  color: var(--ink-faint);
  transition: color var(--t-base) var(--ease);
}
.claim-step i {
  font-size: 1.35em;
  opacity: .35;
  transition: opacity var(--t-base) var(--ease),
              color var(--t-base) var(--ease),
              transform var(--t-base) var(--ease);
}
.claim-step.done {
  color: var(--ink);
  font-weight: var(--weight-semi);
}
.claim-step.done i {
  opacity: 1;
  color: var(--ok);
  transform: scale(1.12);
}

.pair-spinner {
  width: 42px; aspect-ratio: 1;
  margin: 34px auto;
  border: 3px solid var(--rose-soft);
  border-top-color: var(--rose);
  border-radius: 50%;
  animation: spin .85s linear infinite;
}

.done-mark {
  width: clamp(54px, 8.5vw, 88px);
  aspect-ratio: 1;
  margin: 6px auto 16px;
  border-radius: 50%;
  background: var(--mint);
  color: #fff;
  display: grid; place-items: center;
  font-size: clamp(26px, 4.4vw, 48px);
}
.reward-note {
  padding: clamp(11px, 1.7vh, 19px);
  border-radius: var(--r-md);
  background: var(--cream-warm);
  border: 1.5px solid var(--line);
  font-size: clamp(11.5px, 1.7vw, 18px);
  color: var(--ink-soft);
  line-height: 1.45;
}

.ghost-link {
  display: inline-flex; align-items: center; gap: 8px;
  margin-top: 14px;
  color: var(--rose);
  font-size: clamp(11.5px, 1.7vw, 18px);
  font-weight: 600;
  text-decoration: none;
}

/* ── Toast ──────────────────────────────────────────────────────────── */
.toast.bad  { background: var(--rose-deep); }


/* ══════════════════════════════════════════════════════════════════════
   RESPONSIVE
   ══════════════════════════════════════════════════════════════════════ */

/* Landscape (a laptop during setup, or a screen mounted wide) */
@media (orientation: landscape) {
  .hub-split { grid-template-rows: none; grid-template-columns: 1fr 1fr; }
  .product-grid { grid-template-columns: repeat(auto-fit, minmax(min(100%, 240px), 1fr)); }
  .modal { max-width: min(80vw, 680px); }
  .modal-editor, .modal-chooser { max-width: min(88vw, 880px); }
  .editor-preview, .editor-preview img { max-height: 34vh; }
  .modal-hero { max-height: 28vh; }
}

/* Big portrait stand: three across reads better than two enormous cards. */
@media (orientation: portrait) and (min-width: 900px) {
  .product-grid { grid-template-columns: repeat(3, 1fr); }
}

/* Very tall portrait — a proper stand screen. Give the halves more room. */
@media (orientation: portrait) and (min-aspect-ratio: 9/21) {
  .half-inner { gap: clamp(6px, 1.2vh, 16px); }
}

/* Short screens: trim the vertical rhythm so nothing gets cut off. */
@media (max-height: 820px) {
  .bg-chip { width: clamp(46px, 7vw, 76px); }
  .rail-label { margin-top: 6px; }
}

@media (max-height: 720px) {
  .hub-bar { padding-top: 14px; padding-bottom: 8px; }
  .filmstrip, .strip-shot { min-height: 46px; height: 46px; }
  .page-title { font-size: clamp(18px, 3.4vw, 30px); }
}

/* Narrow screens: two across, tighter chrome. */
@media (max-width: 560px) {
  .product-grid { grid-template-columns: repeat(2, 1fr); gap: 9px; }
  .modal { max-width: 94vw; border-radius: var(--r-lg); }
  .modal-body { padding-inline: 18px; }
  .modal-cta { margin-inline: 18px; }
  .selfie-foot { flex-direction: column; }
  .foot-btn.ghost { flex: 1; }
  .card-desc { -webkit-line-clamp: 1; }
  .page-head { gap: 9px; }
}

/* Very narrow: one column beats two cramped ones. */
@media (max-width: 340px) {
  .product-grid { grid-template-columns: 1fr; }
}

/* brand.css flattens animation globally under reduced motion; these two
   are progress indicators, so they keep spinning — just slowly. */
@media (prefers-reduced-motion: reduce) {
  .waking-spinner, .pair-spinner {
    animation-duration: 2s !important;
    animation-iteration-count: infinite !important;
  }
}


/* ══════════════════════════════════════════════════════════════════════
   NARROW PORTRAIT — phone widths and Chrome's device view

   The kiosk is designed for a tall mounted screen, but it's built and
   tested in a phone viewport. Below ~430px two things break: the category
   rail eats a third of the width, and two-column grids leave cards too
   small to read a price on.
   ══════════════════════════════════════════════════════════════════════ */
@media (max-width: 560px) {
  .order-body { grid-template-columns: 78px 1fr; }
  .cat-thumb  { width: 40px; height: 40px; }
  .cat-name   { font-size: 9px; }
  .product-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 8px;
    padding: 10px;
  }
  .order-tray { max-height: 46vh; }
  .tray-actions { grid-template-columns: 1fr 1.2fr; }
  .line-controls { gap: 4px; }
}

@media (max-width: 430px) {
  /* The logo stays — it's the centrepiece now — but the header tightens
     and the arrow needs less clearance. */
  .order-head, .pay-head { padding: 10px 48px 8px; gap: 2px; }
  .page-title { font-size: 17px; }
  .page-eyebrow { font-size: 9px; }

  .order-body { grid-template-columns: 68px 1fr; }
  .cat-rail { padding: 6px 3px; gap: 4px; }
  .cat-tile { padding: 6px 3px; }
  .cat-thumb { width: 36px; height: 36px; }
  .cat-name { font-size: 8.5px; line-height: 1.15; }

  .card { padding: 7px 5px 9px; border-radius: 16px; }
  .card-price { font-size: 13px; }
  .card-name  { font-size: 10.5px; }
  .card-note  { font-size: 9px; }

  .tray-head { padding: 7px 12px; gap: 6px; }
  .tray-title { font-size: 11px; }
  .tray-count { font-size: 9.5px; }
  .tray-tax   { display: none; }        /* the total is what matters here */
  .tray-total { font-size: 15px; }
  .tray-lines { padding: 4px 10px; }
  .line-name  { font-size: 11px; }
  .line-money { font-size: 11.5px; }
  .line-btn   { width: 30px; height: 30px; font-size: 13px; }
  .tray-btn   { padding: 11px; font-size: 12.5px; }

  /* Welcome: side-by-side cards get too narrow to read, so they stack. */
  .page-welcome { padding: 14px 14px 12px; }
  .mode-grid { grid-template-columns: 1fr; gap: 8px; }
  .mode-card {
    flex-direction: row;
    justify-content: flex-start;
    text-align: left;
    gap: 12px;
    padding: 12px 14px;
  }
  .mode-icon { width: 42px; height: 42px; font-size: 20px; }
  .mode-label { font-size: 15px; }
  .mode-card > span:not(.mode-icon) { align-items: flex-start; }
  .welcome-q { font-size: 17px; }
  .lang-native { font-size: 19px; }
  .welcome-logo { height: 34px; }
  .welcome-body { gap: 16px; justify-content: flex-start; }

  .pay-amount-value { font-size: 30px; }
  .pay-qr-card { max-width: 100%; padding: 12px; }
  .pay-actions { grid-template-columns: 1fr; }
}

/* Very short viewports — a phone in landscape, or a small device window.
   Vertical space is the scarce resource, so the tray shrinks first. */
@media (max-height: 620px) {
  .order-tray { max-height: 40vh; }
  .welcome-body { gap: 12px; }
  .welcome-logo { height: 30px; }
  .mode-card { padding: 10px 12px; }
  .pay-qr-frame { max-height: 40vh; }
}

/* The mode card's label and subtitle sit in a column even when the card
   turns horizontal, so they stay together beside the icon. */
@media (max-width: 430px) {
  .mode-card {
    display: grid;
    grid-template-columns: auto 1fr;
    grid-template-rows: auto auto;
    align-items: center;
    column-gap: 12px;
  }
  .mode-icon { grid-row: 1 / 3; }
  .mode-label { grid-column: 2; align-self: end; }
  .mode-sub   { grid-column: 2; align-self: start; }
}


/* ══════════════════════════════════════════════════════════════════════
   IMAGE FITTING

   Every picture in the order flow sits in a box with a fixed aspect ratio.
   Without object-fit an image stretches to the box, which on a wide kiosk
   screen turns a round scoop into an oval — subtle enough to ship and
   obvious once you see it.
   ══════════════════════════════════════════════════════════════════════ */
.card-media,
.cat-thumb,
.prod-hero,
.media-tile,
.shot,
.bg-chip {
  overflow: hidden;
}

.card-media img,
.cat-thumb img,
.bg-chip img,
.shot img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center;
  display: block;
}

/* The product modal shows the whole item rather than a crop — a customer
   deciding what to buy should see all of it. */
.prod-hero img {
  width: 100%;
  height: 100%;
  object-fit: contain;
  object-position: center;
  display: block;
}

/* Boxes keep their shape at every width. */
.card-media { aspect-ratio: 1 / 1; width: 100%; }
.cat-thumb  { aspect-ratio: 1 / 1; }
.bg-chip img { aspect-ratio: 4 / 3; }

@media (prefers-reduced-motion: reduce) {
  .cat-tile, .card { animation: none !important; }
}


/* ══════════════════════════════════════════════════════════════════════
   BACK ARROW — top left, on every screen

   `.hub-back` already existed for the hub's Done button and centres it.
   Reusing that name for the new arrow inherited `justify-self: center`,
   which is why it landed in the middle of the screen.
   ══════════════════════════════════════════════════════════════════════ */
.screen-back {
  position: absolute;
  top: clamp(10px, 2vh, 24px);
  left: clamp(10px, 2.4vw, 28px);
  z-index: 5;
  justify-self: start;      /* beats any inherited centring */
  margin: 0;
}

#hub { position: relative; }

/* Every page header puts its arrow first and hard left. */
.page-head .back-btn,
.welcome-head .back-btn {
  justify-self: start;
  margin: 0;
}
.welcome-head .back-btn { grid-column: 1; }

/* ══════════════════════════════════════════════════════════════════════
   TIGHTER TOPS

   The welcome screens were centring their body in the remaining height,
   which on a tall screen pushed the question far below the header and
   left an empty band the eye reads as a loading state.
   ══════════════════════════════════════════════════════════════════════ */
.page-welcome {
  padding-top: clamp(8px, 1.4vh, 18px);
}
.welcome-head { margin-bottom: clamp(6px, 1.4vh, 16px); }
.welcome-body {
  justify-content: flex-start;
  padding-top: clamp(4px, 1vh, 12px);
}
.welcome-q { margin-bottom: clamp(10px, 2vh, 22px); }

.hub-bar { padding-top: clamp(8px, 1.6vh, 20px); }

/* Order header: one tight row, with a properly sized logo in the middle. */
/* Padding-inline keeps the centred logo clear of the floating back arrow at
   every width; the arrow is out of flow, so nothing else reserves room. */
.order-head, .pay-head {
  padding-top: clamp(8px, 1.6vh, 20px);
  padding-bottom: clamp(6px, 1.2vh, 14px);
}
.welcome-logo { height: clamp(42px, 7vh, 88px); }

/* ══════════════════════════════════════════════════════════════════════
   POP-IN EVERYWHERE

   The stagger reads well on the category rail, so every screen that
   presents choices gets it: the two dine-mode cards, the two language
   cards, the hub halves, the filter chips and the background strip.
   ══════════════════════════════════════════════════════════════════════ */
.mode-card,
.lang-card,
.half,
.bg-chip,
.filter-chip,
.shot,
.pay-qr-card,
.pay-amount,
.pay-status {
  animation: pop-in .42s var(--ease) both;
}

/* Staggered by position, so they arrive as a sequence rather than a flash. */
.mode-card:nth-child(1), .lang-card:nth-child(1) { animation-delay: .06s; }
.mode-card:nth-child(2), .lang-card:nth-child(2) { animation-delay: .14s; }

.half:nth-of-type(1) { animation-delay: .05s; }
.half:nth-of-type(2) { animation-delay: .15s; }

.pay-amount    { animation-delay: .05s; }
.pay-qr-card   { animation-delay: .13s; }
.pay-status    { animation-delay: .22s; }

.bg-chip:nth-child(1)  { animation-delay: .02s; }
.bg-chip:nth-child(2)  { animation-delay: .06s; }
.bg-chip:nth-child(3)  { animation-delay: .10s; }
.bg-chip:nth-child(4)  { animation-delay: .14s; }
.bg-chip:nth-child(5)  { animation-delay: .18s; }
.bg-chip:nth-child(n+6){ animation-delay: .22s; }

.filter-chip:nth-child(1)  { animation-delay: .02s; }
.filter-chip:nth-child(2)  { animation-delay: .05s; }
.filter-chip:nth-child(3)  { animation-delay: .08s; }
.filter-chip:nth-child(4)  { animation-delay: .11s; }
.filter-chip:nth-child(5)  { animation-delay: .14s; }
.filter-chip:nth-child(n+6){ animation-delay: .17s; }

/* The welcome question and step heading lead the sequence. */
.welcome-q, .hub-title, .page-title, .page-eyebrow {
  animation: brand-rise .38s var(--ease) both;
}
.page-eyebrow { animation-delay: .02s; }
.page-title   { animation-delay: .07s; }

@media (prefers-reduced-motion: reduce) {
  .mode-card, .lang-card, .half, .bg-chip, .filter-chip, .shot,
  .pay-qr-card, .pay-amount, .pay-status,
  .welcome-q, .hub-title, .page-title, .page-eyebrow {
    animation: none !important;
  }
}

@media (max-width: 430px) {
  .screen-back { top: 8px; left: 8px; }
  .order-head .page-logo { height: 40px; }
  .welcome-logo { height: 38px; }
  .page-welcome { padding-top: 8px; }
}


/* ── Header stack spacing ────────────────────────────────────────────
   The logo, eyebrow and title read as one unit. A gap between the eyebrow
   and the title breaks that: the eyebrow is a label FOR the title, not a
   separate line, so it sits tight against it. */
.order-head .page-eyebrow,
.pay-head .page-eyebrow {
  margin: clamp(4px, .8vh, 10px) 0 0;
  line-height: 1.1;
}
.order-head .page-title,
.pay-head .page-title {
  margin: 0;
  line-height: 1.12;
}
.order-head .page-titles,
.pay-head .page-titles {
  display: flex;
  flex-direction: column;
  gap: 1px;
}


/* ══════════════════════════════════════════════════════════════════════
   CARD SIZING — the same card, whatever the category holds

   `repeat(auto-fill, minmax(…, 1fr))` divides the row between however many
   items there are. A category with eight products gets sensible cards; one
   with two gets two enormous ones, and the aspect-ratio media box makes
   them taller as they widen. Same page, wildly different scale.

   Capping the card rather than the track fixes it: the grid cell may
   stretch, the card doesn't. Cards then look identical whether the
   category holds two items or twenty, at any zoom level.
   ══════════════════════════════════════════════════════════════════════ */
.product-grid {
  grid-template-columns: repeat(auto-fill, minmax(clamp(140px, 19vw, 200px), 1fr));
  justify-content: start;
  align-content: start;
}
.card {
  width: 100%;
  max-width: 240px;
  justify-self: center;
}

/* ── Header, title removed ─────────────────────────────────────────────
   With the title gone the eyebrow is the only text left, so it aligns
   with the content below rather than floating centred under the logo. */
.order-head, .pay-head {
  padding-top: clamp(4px, .8vh, 10px);
  padding-bottom: clamp(4px, .8vh, 10px);
  align-items: stretch;
}
.order-head .page-titles,
.pay-head .page-titles {
  text-align: left;
  align-items: flex-start;
}
.order-head .page-logo,
.pay-head .page-logo {
  align-self: center;
}
.order-head .page-eyebrow,
.pay-head .page-eyebrow {
  margin: clamp(2px, .5vh, 6px) 0 0;
}

/* ══════════════════════════════════════════════════════════════════════
   MODALS — sized to the content, not to the screen

   A product sheet at 620px wide and 88vh tall on a kiosk is a wall. These
   are single-decision dialogs: one photo, a price, a size, a quantity.
   ══════════════════════════════════════════════════════════════════════ */
.modal {
  max-width: min(88vw, 440px);
  max-height: min(84vh, 720px);
}
.modal-cart    { max-width: min(90vw, 480px); }
.modal-confirm { max-width: min(84vw, 380px); }
.modal-chooser,
.modal-editor  { max-width: min(94vw, 680px); }

/* The hero was free to grow with the sheet; cap it so the buying decision
   — price, size, add — stays above the fold. */
.prod-hero {
  max-height: min(38vh, 300px);
  aspect-ratio: 4 / 3;
}

.modal-body { padding: clamp(14px, 2.4vh, 24px); }

/* ── Modal entrance ────────────────────────────────────────────────────
   The same pop the cards use, so opening a product feels like the card
   growing into a sheet rather than a new screen arriving. */
@keyframes modal-pop-in {
  from { opacity: 0; transform: scale(.86) translateY(20px); }
  62%  { transform: scale(1.02) translateY(0); }
  to   { opacity: 1; transform: scale(1) translateY(0); }
}
.modal {
  animation: modal-pop-in .34s var(--ease) both;
  transform-origin: center bottom;
}

/* The contents arrive just behind the sheet, so it doesn't land fully
   formed and flat. */
.modal-body > *,
.modal-cta,
.confirm-actions {
  animation: brand-rise .3s var(--ease) both;
  animation-delay: .1s;
}
.modal-cta, .confirm-actions { animation-delay: .16s; }

.modal-backdrop { animation: brand-fade-in .22s ease both; }

@media (prefers-reduced-motion: reduce) {
  .modal, .modal-body > *, .modal-cta, .confirm-actions, .modal-backdrop {
    animation: none !important;
  }
}

@media (max-width: 560px) {
  .product-grid {
    grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
  }
  .card { max-width: 200px; }
  .modal { max-width: 94vw; }
  .prod-hero { max-height: 30vh; }
}

@media (max-width: 430px) {
  .product-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .card { max-width: none; }
}


/* ══════════════════════════════════════════════════════════════════════
   CARD — image, name, then price

   Conventions borrowed from Tailwind's system, because they solve real
   problems rather than being decoration:

   * RING, NOT BORDER, for selection. A 2px border on select and 1px at
     rest changes the box size, so every neighbouring card shifts by a
     pixel when you tap one. A ring is a shadow — it costs no layout.
   * SHADOWS THAT STACK, not glow. Two layers, low opacity, tight offsets:
     things look lifted rather than lit.
   * ONE RADIUS FAMILY. 16px on cards, 12px on the media inside them —
     the inner radius is always smaller than the outer, or the corners
     look pinched.
   * TYPE HIERARCHY BY WEIGHT, not size alone. The name is the label; the
     price is the fact. Both stay legible at arm's length.
   ══════════════════════════════════════════════════════════════════════ */
.card {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: stretch;
  padding: 0;                       /* the media goes edge to edge */
  overflow: hidden;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 16px;
  box-shadow:
    0 1px 2px rgba(51, 65, 85, .04),
    0 4px 12px rgba(51, 65, 85, .05);
  text-align: left;
  transition: transform .18s var(--ease),
              box-shadow .18s var(--ease),
              border-color .18s var(--ease);
}
.card:hover {
  transform: translateY(-2px);
  border-color: var(--line-md);
  box-shadow:
    0 2px 4px rgba(51, 65, 85, .05),
    0 10px 24px rgba(51, 65, 85, .09);
}
.card:active {
  transform: translateY(0) scale(.985);
  border-color: var(--brand-primary);
  /* The ring replaces a thicker border, so nothing reflows on tap. */
  box-shadow:
    0 0 0 3px var(--brand-primary-soft),
    0 2px 6px rgba(51, 65, 85, .07);
}

.card-media {
  width: 100%;
  aspect-ratio: 4 / 3;
  border-radius: 0;
  margin: 0;
  background: var(--brand-primary-tint);
  border-bottom: 1px solid var(--line);
}

.card-body {
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: clamp(9px, 1.4vh, 14px) clamp(10px, 1.4vw, 15px)
           clamp(10px, 1.6vh, 15px);
  flex: 1;
}

/* Two lines maximum, then ellipsis. A long product name used to push the
   price out of alignment with the cards beside it. */
.card-name {
  font-family: var(--font-display);
  font-size: clamp(11.5px, 1.45vw, 15px);
  font-weight: var(--weight-semi);
  line-height: 1.3;
  color: var(--ink);
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  min-height: 2.6em;                /* keeps every footer on the same line */
}

.card-foot {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 8px;
  margin-top: auto;
}
.card-price {
  font-family: var(--font-accent);
  font-size: clamp(14px, 1.9vw, 20px);
  font-weight: var(--weight-bold);
  color: var(--brand-primary-deep);
  margin: 0;
  letter-spacing: -.01em;
}
.card-note {
  font-size: clamp(9.5px, 1.1vw, 12px);
  font-weight: var(--weight-medium);
  color: var(--ink-faint);
  padding: 2px 7px;
  border-radius: var(--r-pill);
  background: var(--brand-page);
  white-space: nowrap;
  margin: 0;
}

/* The veg mark sits on the image, not in the flow. */
.card-media .veg-dot { top: 8px; left: 8px; }

/* Category tiles get the same ring treatment. */
.cat-tile {
  border: 1px solid transparent;
  transition: background .18s var(--ease), box-shadow .18s var(--ease);
}
.cat-tile.active {
  background: var(--brand-primary-tint);
  border-color: transparent;
  box-shadow: 0 0 0 2px var(--brand-primary);
}

/* ── Header: ORDER aligns with the content below it ───────────────────
   The header reserved 96px of inline padding to clear the floating back
   arrow, which pushed the eyebrow to an arbitrary indent. Making the
   header a grid instead lets the logo centre on its own while the label
   sits where the grid below it starts. */
.order-head, .pay-head {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  padding-left: clamp(12px, 2vw, 22px);
  padding-right: clamp(12px, 2vw, 22px);
  column-gap: clamp(8px, 1.4vw, 16px);
}
.order-head .page-logo,
.pay-head .page-logo {
  grid-column: 2;
  grid-row: 1;
  justify-self: center;
}
.order-head .page-titles,
.pay-head .page-titles {
  grid-column: 1 / -1;
  grid-row: 2;
  padding-left: clamp(52px, 8vw, 74px);   /* clears the arrow above it */
  text-align: left;
}
.order-head .page-eyebrow,
.pay-head .page-eyebrow {
  margin: 0;
  letter-spacing: .22em;
}

@media (max-width: 560px) {
  .card-name { font-size: 11.5px; min-height: 2.6em; }
  .card-price { font-size: 14px; }
  .order-head .page-titles,
  .pay-head .page-titles { padding-left: 46px; }
}


/* ══════════════════════════════════════════════════════════════════════
   BACKGROUND ARROWS

   Two ways to change the backdrop, because they suit different moments:
   tapping a thumbnail when you know which one you want, and the arrows
   when you're browsing. The arrows sit either end of the strip rather
   than over the video — a control on top of the preview covers the face
   the customer is looking at.

   They hide themselves when there are fewer than two backgrounds; an
   arrow that cycles a single item is a control that does nothing.
   ══════════════════════════════════════════════════════════════════════ */
.bg-row {
  position: relative;
  display: flex;
  align-items: center;
  min-width: 0;
}

.bg-arrows {
  position: absolute;
  inset: 0;
  display: flex;
  justify-content: space-between;
  align-items: center;
  pointer-events: none;      /* only the buttons take taps */
  z-index: 2;
}

.bg-arrow {
  pointer-events: auto;
  width: clamp(30px, 4.4vw, 46px);
  height: clamp(30px, 4.4vw, 46px);
  border-radius: 50%;
  display: grid;
  place-items: center;
  font-size: clamp(15px, 2.2vw, 24px);
  color: #fff;
  background: rgba(20, 8, 14, .72);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
  border: 1.5px solid rgba(255, 255, 255, .22);
  box-shadow: 0 3px 12px rgba(0, 0, 0, .35);
  transition: background .16s var(--ease), transform .16s var(--ease);
}
.bg-arrow:active {
  transform: scale(.9);
  background: var(--brand-primary);
  border-color: var(--brand-primary);
}
.bg-arrow.prev { margin-left: -4px; }
.bg-arrow.next { margin-right: -4px; }

/* The strip clears the arrows, so the first and last thumbnails aren't
   sitting underneath them. */
.bg-row .bg-rail {
  flex: 1;
  min-width: 0;
  padding-left: clamp(34px, 5vw, 52px);
  padding-right: clamp(34px, 5vw, 52px);
  scroll-behavior: smooth;
}

@media (max-width: 430px) {
  .bg-arrow { width: 28px; height: 28px; font-size: 14px; }
  .bg-row .bg-rail { padding-left: 30px; padding-right: 30px; }
}


/* ══════════════════════════════════════════════════════════════════════
   LENS CAROUSEL — Snapchat's pattern

   A single row of circles with the shutter fixed at its centre. What that
   buys, and why it beats the two stacked rails it replaces:

   * The capture button never moves. On a rail-per-category layout the
     shutter sits below a variable number of rows, so it shifts as content
     loads. Here it's anchored and the choices slide beneath it.
   * The active circle is larger and full-strength; its neighbours shrink
     and dim. On a row of small circles that contrast is the only reliable
     way to show which is live.
   * Everything is one decision. Backgrounds and filters in the same track
     matches how someone actually thinks about a photo.
   ══════════════════════════════════════════════════════════════════════ */

/* The track spans the full width and scrolls under the shutter. Padding of
   half the width keeps the first and last circles reachable at centre. */
.lens-track::-webkit-scrollbar { display: none; }

.lens-chip.active { opacity: 1; transform: scale(1); }
.lens-chip:active { transform: scale(.9); }

.lens-ring {
  display: block;
  width: clamp(46px, 7.5vw, 78px);
  height: clamp(46px, 7.5vw, 78px);
  border-radius: 50%;
  overflow: hidden;
  border: 2px solid rgba(255, 255, 255, .3);
  background: rgba(255, 255, 255, .08);
  box-shadow: 0 2px 10px rgba(0, 0, 0, .35);
  transition: border-color .22s var(--ease), box-shadow .22s var(--ease);
}
.lens-chip.active .lens-ring {
  border-color: #fff;
  box-shadow: 0 0 0 3px rgba(236, 28, 36, .55),
              0 4px 16px rgba(0, 0, 0, .45);
}

.lens-face {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.lens-face.none,
.lens-face.swatch {
  display: grid;
  place-items: center;
  color: rgba(255, 255, 255, .75);
  font-size: clamp(16px, 2.6vw, 28px);
}
.lens-face.none { background: rgba(255, 255, 255, .1); }

/* The shutter sits above the scrolling row and never moves with it. */
.lens-centre .shutter { pointer-events: auto; }


/* A ring of dark behind the shutter, so a pale background photo scrolling
   past doesn't wash the button out. */
.lens-centre::before {
  content: "";
  position: absolute;
  top: 50%; left: 50%;
  width: clamp(110px, 18vw, 168px);
  height: clamp(110px, 18vw, 168px);
  transform: translate(-50%, -58%);
  border-radius: 50%;
  background: radial-gradient(circle,
              rgba(12, 5, 9, .85) 38%, rgba(12, 5, 9, 0) 72%);
  pointer-events: none;
  z-index: -1;
}

@media (max-width: 430px) {
  .lens-ring { width: 42px; height: 42px; }
  .lens-bar { min-height: 96px; }
  .lens-name { font-size: 9.5px; }
}

@media (prefers-reduced-motion: reduce) {
  .lens-chip { animation: none !important; }
  .lens-track { scroll-behavior: auto; }
}


/* ── Filter row above the background carousel ────────────────────────
   Two controls stacked, so the filter row stays compact — the carousel is
   the taller element and needs the room. */

@media (max-height: 780px) {
  .filter-rail { margin-top: 3px; }
  .lens-bar { padding-top: 4px; min-height: clamp(96px, 15vh, 150px); }
}


/* ══════════════════════════════════════════════════════════════════════
   LOGO GATE

   A locked shutter has to look deliberately locked, not broken. Dimmed
   with a padlock reads as "not yet"; a plain grey circle reads as "this
   booth is faulty" and the guest walks off.
   ══════════════════════════════════════════════════════════════════════ */
.shutter.locked {
  opacity: .45;
  cursor: not-allowed;
  filter: grayscale(.7);
}
.shutter.locked .shutter-core { transform: scale(.82); }
.shutter-lock {
  position: absolute;
  inset: 0;
  display: none;
  place-items: center;
  color: #fff;
  font-size: clamp(16px, 2.6vw, 28px);
  text-shadow: 0 2px 8px rgba(0, 0, 0, .6);
  pointer-events: none;
}
.shutter.locked .shutter-lock { display: grid; }

/* Unlocking is worth a beat of feedback — it's the moment the guest has
   been waiting for. */
.shutter:not(.locked) { animation: shutter-unlock .42s var(--ease); }
@keyframes shutter-unlock {
  0%   { transform: scale(.9); filter: grayscale(.7); opacity: .5; }
  55%  { transform: scale(1.06); }
  100% { transform: scale(1); filter: none; opacity: 1; }
}

.gate-hint {
  position: absolute;
  left: 50%;
  bottom: calc(100% + clamp(8px, 1.6vh, 18px));
  transform: translateX(-50%);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 3px;
  padding: clamp(8px, 1.4vh, 14px) clamp(14px, 2.4vw, 24px);
  border-radius: var(--r-lg);
  background: rgba(20, 8, 14, .86);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  border: 1.5px solid rgba(255, 255, 255, .16);
  color: #fff;
  text-align: center;
  white-space: nowrap;
  z-index: 4;
  animation: gate-in .3s var(--ease) both;
  box-shadow: 0 6px 24px rgba(0, 0, 0, .4);
}
@keyframes gate-in {
  from { opacity: 0; transform: translateX(-50%) translateY(6px); }
}
.gate-hint i {
  font-size: clamp(18px, 3vw, 30px);
  color: var(--brand-primary);
  animation: gate-nudge 1.8s ease-in-out infinite;
}
/* A small movement draws the eye without being a distraction. */
@keyframes gate-nudge {
  50% { transform: translateY(-3px) rotate(-6deg); }
}
.gate-hint span {
  font-size: clamp(11px, 1.6vw, 16px);
  font-weight: var(--weight-semi);
}
.gate-sub {
  font-size: clamp(9.5px, 1.3vw, 13px) !important;
  font-weight: var(--weight-normal) !important;
  color: rgba(255, 255, 255, .68);
}
.gate-sub:empty { display: none; }

@media (prefers-reduced-motion: reduce) {
  .gate-hint i, .shutter:not(.locked) { animation: none !important; }
}


/* ══════════════════════════════════════════════════════════════════════
   TOAST STACK

   Same system as the admin console, sized for a booth. Top centre rather
   than a corner: a guest is looking at their own face in the middle of
   the screen, so a message in the corner is outside where they're looking.

   No close button — the whole toast is the tap target, because a 24px X
   is a poor thing to aim at on a touch screen.
   ══════════════════════════════════════════════════════════════════════ */
/* Top left.
   
   Centre put messages directly over the preview, on top of the face the
   guest is looking at. The left edge is clear of both the video and the
   back arrow's tap area. */
.toast-stack {
  position: fixed;
  top: clamp(12px, 2.4vh, 28px);
  left: clamp(12px, 2.4vw, 28px);
  right: auto;
  transform: none;
  z-index: 200;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 8px;
  width: min(88vw, 420px);
  pointer-events: none;
}

.toast-item {
  position: relative;
  display: flex;
  align-items: center;
  gap: 11px;
  width: 100%;
  padding: clamp(11px, 1.8vh, 16px) clamp(15px, 2.4vw, 22px);
  border-radius: var(--r-lg);
  background: rgba(20, 8, 14, .92);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  border: 1.5px solid rgba(255, 255, 255, .16);
  box-shadow: 0 8px 32px rgba(0, 0, 0, .42);
  color: #fff;
  font-size: clamp(12.5px, 1.8vw, 17px);
  font-weight: var(--weight-semi);
  overflow: hidden;
  pointer-events: auto;
  cursor: pointer;
  animation: toast-in .3s var(--ease) both;
}
/* Drops from above, matching where it lives. */
/* Slides in from the left, matching where it now lives — an entrance that
   contradicts the anchor reads as a glitch. */
@keyframes toast-in {
  from { opacity: 0; transform: translateX(-20px) scale(.96); }
}
.toast-item.leaving { animation: toast-out .22s var(--ease) both; }
@keyframes toast-out {
  to { opacity: 0; transform: translateX(-16px) scale(.96); }
}

.toast-item > i { font-size: clamp(18px, 2.6vw, 24px); flex: none; }
.toast-success > i { color: #4ADE80; }
.toast-error   > i { color: #FF6B72; }
.toast-warn    > i { color: var(--brand-yellow); }
.toast-info    > i { color: #7DD3FC; }

.toast-text { flex: 1; line-height: 1.4; }

.toast-bar {
  position: absolute;
  left: 0; bottom: 0;
  height: 2.5px;
  width: 100%;
  transform-origin: left;
  animation: toast-drain linear both;
  background: rgba(255, 255, 255, .5);
}
@keyframes toast-drain { to { transform: scaleX(0); } }
.toast-success .toast-bar { background: #4ADE80; }
.toast-error   .toast-bar { background: #FF6B72; }
.toast-warn    .toast-bar { background: var(--brand-yellow); }
.toast-info    .toast-bar { background: #7DD3FC; }

@media (prefers-reduced-motion: reduce) {
  .toast-item, .toast-bar { animation: none !important; }
}


/* ── Order tray actions ──────────────────────────────────────────────
   Done is the only button here since Cancel was removed. A single button
   in a grid still fills its column, which on a phone left it hugging the
   left edge with dead space beside it. Centring it with a sane maximum
   reads as deliberate. */
.tray-actions {
  display: flex;
  justify-content: center;
}
.tray-actions .tray-btn {
  width: 100%;
  max-width: 420px;
}

@media (max-width: 620px) {
  .tray-actions .tray-btn { max-width: none; }
}


/* ══════════════════════════════════════════════════════════════════════
   SELFIE POINT — full-bleed camera

   The camera is the screen. Every control floats on top of it.

   The previous layout stacked header, preview, filmstrip and controls as
   separate horizontal bands, so the live view occupied roughly half the
   display with grey furniture filling the rest. A photo booth should read
   as a viewfinder: you are looking at yourself, not at an application that
   contains a picture of you.

   Everything below is an overlay over one full-size video layer.
   ══════════════════════════════════════════════════════════════════════ */

.page-selfie {
  /* Keep the fixed positioning .screen provides.
     
     This rule previously set `position: relative`, which OVERRODE
     `.screen { position: fixed; inset: 0 }` from earlier in the file.
     Without fixed positioning the `inset: 0` no longer applies, so the
     element fell back to auto height — and since everything inside it is
     absolutely positioned, that height was ZERO. The result was a screen
     showing nothing but the black background, even though the camera was
     running and frames were arriving.
     
     A fixed element is already a containing block for absolute children,
     so `relative` bought nothing and cost everything. */
  position: fixed;
  inset: 0;
  display: block;
  padding: 0;
  overflow: hidden;
  background: #000;
}

/* ── The camera layer ────────────────────────────────────────────── */
.stage {
  position: absolute;
  inset: 0;
  background: #000;
  overflow: hidden;
}

/* object-fit: cover, not contain.
   
   The camera's aspect ratio rarely matches the screen's. `contain` would
   letterbox it with black bars; `cover` fills edge to edge and crops the
   overflow, which is what every camera app does and what makes it read as
   a viewfinder rather than a video player. */
.stage-feed {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

/* Scrims. Without them, white text over a bright frame is unreadable —
   and a booth is often pointed at a window. A gradient rather than a solid
   bar so the camera stays visible underneath. */
.page-selfie::before,
.page-selfie::after {
  content: "";
  position: absolute;
  left: 0; right: 0;
  pointer-events: none;
  z-index: 2;
}
.page-selfie::before {
  top: 0;
  height: clamp(70px, 12vh, 130px);
  background: linear-gradient(180deg, rgba(0,0,0,.55), transparent);
}
.page-selfie::after {
  bottom: 0;
  height: clamp(180px, 30vh, 320px);
  background: linear-gradient(0deg, rgba(0,0,0,.72) 20%, transparent);
}

/* ── Top overlay ─────────────────────────────────────────────────── */
.cam-top {
  position: absolute;
  top: 0; left: 0; right: 0;
  z-index: 5;
  display: flex;
  align-items: center;
  gap: 10px;
  padding: clamp(10px, 2vh, 20px) clamp(12px, 2.4vw, 24px);
}

.cam-icon {
  width: clamp(38px, 6vw, 48px);
  height: clamp(38px, 6vw, 48px);
  border-radius: 50%;
  display: grid;
  place-items: center;
  background: rgba(0, 0, 0, .38);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  border: 1.5px solid rgba(255, 255, 255, .22);
  color: #fff;
  font-size: clamp(18px, 2.6vw, 24px);
  flex: none;
  transition: transform var(--t-fast) var(--ease), background var(--t-fast) var(--ease);
}
.cam-icon:active { transform: scale(.92); background: rgba(0,0,0,.55); }

.cam-badge {
  display: flex;
  align-items: center;
  gap: 6px;
  margin: 0 auto;
  padding: clamp(6px, 1vh, 9px) clamp(12px, 1.8vw, 18px);
  border-radius: var(--r-pill);
  background: rgba(236, 28, 36, .92);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  color: #fff;
  font-size: clamp(11px, 1.5vw, 15px);
  font-weight: var(--weight-semi);
  white-space: nowrap;
  animation: badge-pop .3s var(--ease-out) both;
  box-shadow: 0 4px 16px rgba(236, 28, 36, .4);
}
@keyframes badge-pop {
  from { opacity: 0; transform: translateY(-8px) scale(.9); }
}

.cam-count {
  padding: clamp(6px, 1vh, 9px) clamp(11px, 1.6vw, 16px);
  border-radius: var(--r-pill);
  background: rgba(0, 0, 0, .38);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  border: 1.5px solid rgba(255, 255, 255, .18);
  color: #fff;
  font-size: clamp(11px, 1.5vw, 15px);
  font-weight: var(--weight-semi);
  font-variant-numeric: tabular-nums;
  flex: none;
}
.cam-count-sep { opacity: .5; margin: 0 2px; }

/* ── Shots taken, up the right edge ──────────────────────────────── */
.filmstrip {
  position: absolute;
  top: clamp(64px, 11vh, 110px);
  right: clamp(12px, 2.4vw, 24px);
  z-index: 5;
  display: flex;
  flex-direction: column;
  gap: 8px;
  max-height: 46vh;
  overflow: hidden;
}
.filmstrip img,
.filmstrip .shot-thumb {
  width: clamp(46px, 7vw, 64px);
  height: clamp(46px, 7vw, 64px);
  border-radius: var(--r-md);
  object-fit: cover;
  border: 2px solid rgba(255, 255, 255, .85);
  box-shadow: 0 3px 12px rgba(0, 0, 0, .4);
  animation: thumb-in .32s var(--ease-out) both;
}
@keyframes thumb-in {
  from { opacity: 0; transform: translateX(20px) scale(.8); }
}

/* ── Bottom overlay ──────────────────────────────────────────────── */
.cam-bottom {
  position: absolute;
  bottom: 0; left: 0; right: 0;
  z-index: 5;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: clamp(8px, 1.4vh, 14px);
  padding: 0 0 clamp(14px, 3vh, 30px);
}

/* ── Filters: a compact pill row ─────────────────────────────────── */
.filter-rail {
  display: flex;
  gap: 7px;
  overflow-x: auto;
  max-width: 94vw;
  padding: 2px clamp(12px, 2.4vw, 24px);
  scrollbar-width: none;
}
.filter-rail::-webkit-scrollbar { display: none; }

.filter-chip {
  flex: none;
  /* Row, so the colour swatch sits beside the label rather than above it. */
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: clamp(5px, .9vh, 8px) clamp(11px, 1.6vw, 16px);
  border-radius: var(--r-pill);
  background: rgba(0, 0, 0, .38);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  border: 1.5px solid rgba(255, 255, 255, .2);
  color: rgba(255, 255, 255, .88);
  font-size: clamp(10.5px, 1.4vw, 14px);
  font-weight: var(--weight-semi);
  white-space: nowrap;
  transition: all var(--t-fast) var(--ease);
}
.filter-chip.active {
  background: #fff;
  color: #16181D;
  border-color: #fff;
}
.filter-chip:active { transform: scale(.94); }

/* The active background's name, above the carousel. */
.lens-name {
  color: #fff;
  font-size: clamp(11px, 1.5vw, 15px);
  font-weight: var(--weight-semi);
  text-shadow: 0 2px 8px rgba(0, 0, 0, .6);
  min-height: 1.2em;
  transition: opacity var(--t) var(--ease);
}
.lens-name:empty { opacity: 0; }

/* ══════════════════════════════════════════════════════════════════════
   LENS CAROUSEL

   Circles either side of a fixed shutter. Tapping one scrolls it to the
   centre; the shutter never moves, so the capture target stays under the
   same thumb position however far the guest has browsed.

   Backgrounds get circles rather than chips because a backdrop has to be
   SEEN to be chosen — a text label tells you nothing about a photo.
   ══════════════════════════════════════════════════════════════════════ */
.lens-bar {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  height: clamp(84px, 14vh, 128px);
}

.lens-track {
  display: flex;
  align-items: center;
  gap: clamp(8px, 1.4vw, 16px);
  width: 100%;
  padding: 0 calc(50% - clamp(30px, 4.6vw, 42px));
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  scrollbar-width: none;
  -webkit-overflow-scrolling: touch;
}
.lens-track::-webkit-scrollbar { display: none; }

.lens-chip {
  flex: none;
  width: clamp(46px, 7vw, 64px);
  height: clamp(46px, 7vw, 64px);
  border-radius: 50%;
  overflow: hidden;
  border: 2px solid rgba(255, 255, 255, .5);
  background: rgba(0, 0, 0, .35);
  scroll-snap-align: center;
  display: grid;
  place-items: center;
  color: rgba(255, 255, 255, .8);
  font-size: clamp(15px, 2.2vw, 22px);
  opacity: .68;
  transition: transform var(--t) var(--ease-out), opacity var(--t) var(--ease),
              border-color var(--t) var(--ease);
}
.lens-chip img { width: 100%; height: 100%; object-fit: cover; }

/* The chip nearest the shutter is the active one. Scaling it up and
   ringing it in brand red makes the current choice unmistakable without
   needing a label. */
.lens-chip.active {
  opacity: 1;
  transform: scale(1.18);
  border-color: var(--brand-primary);
  box-shadow: 0 0 0 3px rgba(236, 28, 36, .35),
              0 4px 14px rgba(0, 0, 0, .45);
}
.lens-chip:active { transform: scale(1.05); }

/* Arrows for a booth without a touchscreen. Hidden when there is nothing
   to page through — see updateBgArrows(). */
.lens-arrow {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  z-index: 3;
  width: clamp(32px, 4.6vw, 42px);
  height: clamp(32px, 4.6vw, 42px);
  border-radius: 50%;
  display: grid;
  place-items: center;
  background: rgba(0, 0, 0, .42);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  border: 1.5px solid rgba(255, 255, 255, .2);
  color: #fff;
  font-size: clamp(16px, 2.2vw, 22px);
}
.lens-arrow.left  { left: clamp(6px, 1.4vw, 16px); }
.lens-arrow.right { right: clamp(6px, 1.4vw, 16px); }
.lens-arrow:active { transform: translateY(-50%) scale(.9); }
.lens-arrow[hidden] { display: none; }

/* ── Shutter ─────────────────────────────────────────────────────── */
.lens-centre {
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  z-index: 4;
  pointer-events: none;      /* the carousel scrolls underneath it */
}
.lens-centre .shutter { pointer-events: auto; }

.shutter {
  position: relative;
  width: clamp(66px, 10vw, 92px);
  height: clamp(66px, 10vw, 92px);
  border-radius: 50%;
  background: none;
  border: none;
  display: grid;
  place-items: center;
  transition: transform var(--t-fast) var(--ease);
}
.shutter-ring {
  position: absolute;
  inset: 0;
  border-radius: 50%;
  border: 4px solid #fff;
  box-shadow: 0 2px 14px rgba(0, 0, 0, .45);
}
.shutter-core {
  width: 76%;
  height: 76%;
  border-radius: 50%;
  background: #fff;
  transition: transform var(--t-fast) var(--ease), background var(--t) var(--ease);
}
.shutter:not(:disabled):active .shutter-core { transform: scale(.84); }

@media (max-width: 620px) {
  .cam-badge { font-size: 11px; padding: 5px 11px; }
  .filmstrip { top: 58px; }
}


/* ══════════════════════════════════════════════════════════════════════
   SELFIE POINT ON A PHONE

   The booth is designed for a large portrait screen, but it is tested and
   demonstrated on a phone. Two things break at that size if left alone:
   the controls take too much of the frame, and the browser's own chrome
   eats the bottom of the screen.
   ══════════════════════════════════════════════════════════════════════ */

/* dvh, not vh.
   
   On mobile Safari and Chrome the address bar hides on scroll, so 100vh is
   TALLER than the visible area — the bottom of a 100vh element sits behind
   the browser chrome, which is exactly where the shutter lives. dvh tracks
   the actual visible height. The vh line stays first as a fallback for
   older browsers that don't know dvh. */
@supports (height: 100dvh) {
  .page-selfie { height: 100dvh; }
}

@media (max-width: 620px) {
  /* Trim the scrims: at this size the tall bottom gradient darkened most
     of the picture rather than just the area behind the controls. */
  .page-selfie::before { height: 64px; }
  .page-selfie::after  { height: 200px; }

  .cam-top { padding: 10px 12px; }
  .cam-icon { width: 38px; height: 38px; font-size: 19px; }
  .cam-badge { font-size: 11px; padding: 5px 11px; }
  .cam-count { font-size: 11px; padding: 5px 10px; }

  /* Thumbnails move to a horizontal strip. A vertical column down the
     right edge covers a third of a portrait phone screen. */
  .filmstrip {
    top: auto;
    bottom: 218px;
    left: 12px;
    right: 12px;
    flex-direction: row;
    max-height: none;
    overflow-x: auto;
    scrollbar-width: none;
  }
  .filmstrip::-webkit-scrollbar { display: none; }
  .filmstrip img, .filmstrip .shot-thumb { width: 44px; height: 44px; }

  .cam-bottom { gap: 8px; padding-bottom: max(12px, env(safe-area-inset-bottom)); }
  .filter-rail { padding: 2px 12px; gap: 6px; }
  .filter-chip { padding: 5px 10px; font-size: 11px; }

  .lens-bar { height: 96px; }
  .lens-chip { width: 44px; height: 44px; }
  .lens-track { gap: 8px; padding: 0 calc(50% - 26px); }
  .shutter { width: 64px; height: 64px; }
  .shutter-ring { border-width: 3.5px; }

  /* Arrows are for a booth without a touchscreen. On a phone the carousel
     is swiped, and the arrows only sit over the chips. */
  .lens-arrow { display: none; }

  .gate-hint { padding: 8px 14px; }
  .gate-hint span { font-size: 12px; }
  .gate-sub { font-size: 10.5px !important; }
}

/* Landscape phone: very little vertical room, so the controls compress
   further and the thumbnails go back to the side where width is spare. */
@media (max-width: 900px) and (orientation: landscape) {
  .page-selfie::after { height: 128px; }
  .lens-bar { height: 76px; }
  .lens-chip { width: 38px; height: 38px; }
  .shutter { width: 56px; height: 56px; }
  .filmstrip {
    top: 58px; bottom: auto; left: auto; right: 12px;
    flex-direction: column;
  }
  .filmstrip img, .filmstrip .shot-thumb { width: 38px; height: 38px; }
  .filter-rail { padding: 0 12px; }
}

/* A very short screen — a phone in landscape with a keyboard open, or a
   small tablet. Drop the filters rather than squeezing everything. */
@media (max-height: 480px) {
  .filter-rail { display: none; }
  .lens-bar { height: 68px; }
}


/* ══════════════════════════════════════════════════════════════════════
   RETAKE / CONTINUE

   These were lost in the full-bleed rebuild, which left a guest able to
   take photos with no way to proceed — and the JS still setting .hidden
   on the missing nodes, which threw on every filmstrip redraw.

   Hidden until a shot exists, so a guest who has taken nothing sees only
   the shutter.
   ══════════════════════════════════════════════════════════════════════ */
.cam-actions {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: clamp(8px, 1.6vw, 16px);
  padding: 0 clamp(12px, 2.4vw, 24px);
  animation: actions-in .28s var(--ease-out) both;
}
.cam-actions[hidden] { display: none; }
@keyframes actions-in {
  from { opacity: 0; transform: translateY(10px); }
}

.cam-action {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: clamp(9px, 1.5vh, 14px) clamp(16px, 2.6vw, 26px);
  border-radius: var(--r-pill);
  font-size: clamp(12px, 1.7vw, 17px);
  font-weight: var(--weight-semi);
  white-space: nowrap;
  transition: transform var(--t-fast) var(--ease), background var(--t-fast) var(--ease);
}
.cam-action:active { transform: scale(.95); }

.cam-action.ghost {
  background: rgba(0, 0, 0, .42);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  border: 1.5px solid rgba(255, 255, 255, .24);
  color: #fff;
}
.cam-action.ghost:hover { background: rgba(0, 0, 0, .58); }

/* Continue is the intended next step, so it carries the brand colour and
   the only solid fill on the screen. */
.cam-action.primary {
  background: var(--brand-primary);
  border: 1.5px solid var(--brand-primary);
  color: #fff;
  box-shadow: 0 4px 18px rgba(236, 28, 36, .45);
}
.cam-action.primary:hover { background: var(--brand-primary-deep, #C4161C); }

.cam-action-count {
  display: inline-grid;
  place-items: center;
  min-width: 1.55em;
  height: 1.55em;
  padding: 0 .35em;
  border-radius: var(--r-pill);
  background: rgba(255, 255, 255, .26);
  font-size: .85em;
  font-variant-numeric: tabular-nums;
}

@media (max-width: 620px) {
  .cam-action { padding: 9px 15px; font-size: 12.5px; }
  .cam-actions { gap: 8px; }
  /* The thumbnail strip sits above the actions, so it moves up to make
     room once they appear. */
  .filmstrip { bottom: 270px; }
}


/* ══════════════════════════════════════════════════════════════════════
   THE UNLOCK MOMENT

   The single most important beat in the selfie flow: the guest is holding
   up a product, watching the button, waiting to be allowed to press it.

   A ring expanding outward reads as "released" — the same visual language
   as a camera shutter opening. It runs once on the transition rather than
   looping, because a permanent animation on an enabled button is noise.
   ══════════════════════════════════════════════════════════════════════ */
.shutter.just-armed::after {
  content: "";
  position: absolute;
  inset: -6px;
  border-radius: 50%;
  border: 3px solid var(--brand-primary);
  animation: shutter-release .6s var(--ease-out) forwards;
  pointer-events: none;
}
@keyframes shutter-release {
  0%   { transform: scale(.86); opacity: 0; }
  35%  { opacity: 1; }
  100% { transform: scale(1.45); opacity: 0; }
}

/* The badge already says "Magneta spotted". Give it a moment of emphasis
   so the two land together and the cause is obvious. */
.cam-badge {
  animation: badge-pop .3s var(--ease-out) both,
             badge-glow 1.6s var(--ease) 2;
}
@keyframes badge-glow {
  50% { box-shadow: 0 4px 24px rgba(236, 28, 36, .75); }
}

@media (prefers-reduced-motion: reduce) {
  .shutter.just-armed::after,
  .cam-badge { animation: none !important; }
}


/* ── Guidance on the send dialog ─────────────────────────────────────
   Replaces the tier list. Two short notes rather than four rows of
   percentages — the customer is standing at a booth with people behind
   them, and the only thing they must act on is whether their profile is
   public. */
.tier-note {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  padding: clamp(11px, 1.8vh, 16px) clamp(12px, 2vw, 18px);
  margin-bottom: 10px;
  border-radius: var(--r-md);
  background: rgba(236, 28, 36, .07);
  border: 1.5px solid rgba(236, 28, 36, .22);
  text-align: left;
}
.tier-note.alt {
  background: rgba(15, 123, 79, .07);
  border-color: rgba(15, 123, 79, .22);
}
.tier-note i {
  font-size: clamp(17px, 2.4vw, 22px);
  color: var(--brand-primary);
  flex: none;
  margin-top: 1px;
}
.tier-note.alt i { color: var(--ok); }
.tier-note p {
  margin: 0;
  font-size: clamp(12px, 1.7vw, 15.5px);
  line-height: 1.5;
  color: var(--ink);
}
.tier-note strong { font-weight: var(--weight-bold); }


/* ── chip-in ─────────────────────────────────────────────────────────
   Referenced by .strip-shot and .foot-btn but never defined, so both
   elements appeared with no entrance at all — the declaration was simply
   ignored. An undefined animation-name is not an error in CSS, which is
   why this went unnoticed.

   A small rise with a slight scale: enough to register that something
   arrived, short enough not to delay a booth interaction. */
@keyframes chip-in {
  from { opacity: 0; transform: translateY(6px) scale(.94); }
  to   { opacity: 1; transform: translateY(0) scale(1); }
}


/* ══════════════════════════════════════════════════════════════════════
   FLOATING CART

   Replaces the fixed order tray, which took roughly a third of the screen
   from the menu itself. The button carries the count and the total, so a
   customer knows where they stand without opening anything — which is the
   only reason a floating cart is acceptable in place of a visible list.

   Hidden at zero items: a first-time customer sees a clean menu.
   ══════════════════════════════════════════════════════════════════════ */
.cart-fab {
  position: fixed;
  left: 50%;
  bottom: max(clamp(16px, 3vh, 34px), env(safe-area-inset-bottom));
  transform: translateX(-50%);
  z-index: 60;

  display: flex;
  align-items: center;
  gap: clamp(12px, 2vw, 20px);
  min-width: min(440px, 88vw);
  max-width: 88vw;
  padding: clamp(11px, 1.8vh, 16px) clamp(16px, 2.4vw, 24px);

  border: none;
  border-radius: var(--r-pill);
  background: var(--brand-primary);
  color: #fff;
  box-shadow: 0 10px 34px rgba(236, 28, 36, .42),
              0 2px 8px rgba(0, 0, 0, .18);

  animation: cart-fab-in .34s var(--ease-out) both;
  transition: transform var(--t-fast) var(--ease),
              box-shadow var(--t-fast) var(--ease);
}
.cart-fab[hidden] { display: none; }
.cart-fab:active {
  transform: translateX(-50%) scale(.97);
  box-shadow: 0 6px 20px rgba(236, 28, 36, .38);
}

@keyframes cart-fab-in {
  from { opacity: 0; transform: translateX(-50%) translateY(20px) scale(.94); }
  to   { opacity: 1; transform: translateX(-50%) translateY(0) scale(1); }
}

/* Fires when an item is added, so the button acknowledges the tap even
   though it sits at the other end of the screen from the product. */
.cart-fab.bump { animation: cart-fab-bump .42s var(--ease-out); }
@keyframes cart-fab-bump {
  0%, 100% { transform: translateX(-50%) scale(1); }
  30%      { transform: translateX(-50%) scale(1.055); }
  60%      { transform: translateX(-50%) scale(.985); }
}

.cart-fab-icon {
  position: relative;
  display: grid;
  place-items: center;
  font-size: clamp(20px, 2.8vw, 27px);
  flex: none;
}

/* The count sits on the bag rather than beside it — the same shorthand
   every delivery app uses, so it needs no explaining. */
.cart-fab-badge {
  position: absolute;
  top: -6px;
  right: -9px;
  min-width: 20px;
  height: 20px;
  padding: 0 5px;
  border-radius: var(--r-pill);
  background: #fff;
  color: var(--brand-primary);
  font-size: 12px;
  font-weight: var(--weight-bold);
  font-variant-numeric: tabular-nums;
  display: grid;
  place-items: center;
  box-shadow: 0 1px 4px rgba(0, 0, 0, .2);
}

.cart-fab-text {
  flex: 1;
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  text-align: left;
}
.cart-fab-label {
  font-size: clamp(14px, 1.9vw, 18px);
  font-weight: var(--weight-semi);
}
.cart-fab-total {
  font-size: clamp(16px, 2.2vw, 21px);
  font-weight: var(--weight-bold);
  font-variant-numeric: tabular-nums;
}
.cart-fab-go {
  font-size: clamp(17px, 2.3vw, 22px);
  opacity: .85;
  flex: none;
}

/* The menu no longer shares the screen with a tray, so it can use the
   full width — and needs clearance so the last row isn't under the cart. */
.page-order .menu-scroll,
.page-order .cat-scroll {
  padding-bottom: clamp(88px, 15vh, 140px);
}

@media (max-width: 620px) {
  .cart-fab {
    min-width: 0;
    left: 12px;
    right: 12px;
    transform: none;
    max-width: none;
  }
  .cart-fab:active { transform: scale(.97); }
  @keyframes cart-fab-in {
    from { opacity: 0; transform: translateY(20px) scale(.94); }
    to   { opacity: 1; transform: translateY(0) scale(1); }
  }
  @keyframes cart-fab-bump {
    0%, 100% { transform: scale(1); }
    30%      { transform: scale(1.055); }
    60%      { transform: scale(.985); }
  }
}

@media (prefers-reduced-motion: reduce) {
  .cart-fab, .cart-fab.bump { animation: none !important; }
}