/* ============================================================================
   Jollibee Rewards prototype — Simulator layout (mobile)
   Everything that is NOT the design system: the phone frame, faux status bar,
   bottom tab bar, screen switcher, and the Jollibee screen patterns (points
   card, status-icon activity rows, reward catalog cards, voucher code, scan
   viewfinder, confirm modal, launcher). fds-* components stay in components.css.
   Layout dims use raw px (allowed); COLOR is always a --c-* token.
   ========================================================================== */

/* ---- Phone frame ----------------------------------------------------------- */
/* The app page locks to the viewport — the DOCUMENT never scrolls (only the
   screens inside the phone do), so transitions can't scroll the page. The
   launcher (index.html) has no .app-shell class, so it scrolls normally. */
body.app-shell { height: 100vh; height: 100dvh; overflow: hidden; }
.app { height: 100%; display: flex; align-items: center; justify-content: center; padding: var(--space-24); background: var(--c-canvas); overflow: hidden; }
.phone {
  position: relative; width: 390px; max-width: 100%;
  height: 100%; max-height: 844px;
  display: flex; flex-direction: column; overflow: hidden;
  background: var(--c-surface); border-radius: 44px;
  box-shadow: 0 24px 70px rgba(8,9,28,0.28), 0 0 0 10px #0c0d1a, 0 0 0 11px #2a2c3a;
}
/* Status bar overlays the screen so the app color bleeds edge-to-edge under it
   (real-phone behavior). Foreground follows --sb-fg (white on brand screens). */
.phone__statusbar {
  position: absolute; top: 0; left: 0; right: 0; z-index: 6; height: 36px;
  display: flex; align-items: center; justify-content: space-between;
  padding: 0 22px; background: transparent; color: var(--sb-fg, var(--c-text));
  font-family: var(--font-display); font-size: 0.8125rem; font-weight: 700; pointer-events: none;
}
.phone__glyphs { display: flex; align-items: center; gap: 5px; }
.phone__glyphs i { display: block; background: var(--sb-fg, var(--c-text)); border-radius: 1px; }
.phone__sig { width: 15px; height: 10px; -webkit-mask: repeating-linear-gradient(90deg,#000 0 2px,transparent 2px 4px); mask: repeating-linear-gradient(90deg,#000 0 2px,transparent 2px 4px); }
.phone__wifi { width: 13px; height: 10px; clip-path: polygon(50% 100%, 0 30%, 100% 30%); }
.phone__batt { width: 20px; height: 10px; border-radius: 2px; }
/* Stage: screens are absolutely positioned inside; only the stage animates
   (status bar + tab bar are siblings and stay put). */
.phone__body { flex: 1; min-height: 0; position: relative; overflow: hidden; background: var(--c-canvas); }

/* SPA screens — each is a fixed-height flex column: pinned header (flex-none) + ONE
   scrolling body + optional pinned footer. No position:sticky anywhere, so during a
   transform transition the whole screen slides as one rigid block (headers never lag). */
.screen { position: absolute; inset: 0; display: none; flex-direction: column; overflow: hidden; background: var(--c-canvas); }
.screen.is-active { display: flex; }
.screen__scroll { flex: 1; min-height: 0; overflow-y: auto; overflow-x: hidden; -webkit-overflow-scrolling: touch; padding: 0 var(--space-16) var(--space-32); }
.screen__scroll::-webkit-scrollbar { width: 0; }

/* Transitions: push (forward), pop (back), fade (tab/root switch) */
.screen.scr-anim { will-change: transform, opacity; }
.screen.enter-push { animation: scrInRight 0.28s cubic-bezier(0.22, 0.8, 0.28, 1) both; }
.screen.leave-pop  { animation: scrOutRight 0.28s cubic-bezier(0.22, 0.8, 0.28, 1) both; }
.screen.enter-fade { animation: scrFade 0.2s ease both; }
@keyframes scrInRight { from { transform: translateX(100%); } to { transform: translateX(0); } }
@keyframes scrOutRight { from { transform: translateX(0); } to { transform: translateX(100%); } }
@keyframes scrFade { from { opacity: 0; } to { opacity: 1; } }
@media (prefers-reduced-motion: reduce) { .screen.scr-anim { animation-duration: 0.001s; } }

/* Title-over-subtitle always STACK (DS card header: title 14/600 → desc 12/400,
   as separate blocks). These are inline <span>s in markup, so force block. */
.scan-cta__title, .scan-cta__sub,
.promo__title, .promo__sub,
.txn__title, .txn__meta,
.reward__name, .reward__desc,
.vrow__name, .vrow__meta { display: block; }

/* ---- App bar (screen header) ---------------------------------------------- */
.appbar { flex: 0 0 auto; display: flex; align-items: center; gap: var(--space-12); padding: calc(36px + var(--space-6)) var(--space-16) var(--space-12); background: var(--c-surface); }
.appbar__title { font-family: var(--font-display); font-weight: 700; font-size: 1.0625rem; flex: 1; min-width: 0; }
.appbar__btn { width: 36px; height: 36px; border-radius: var(--radius-full); border: none; background: transparent; color: var(--c-text); display: inline-flex; align-items: center; justify-content: center; cursor: pointer; flex: 0 0 auto; }
.appbar__btn:hover { background: var(--c-surface-hover); }
.appbar--brand { background: var(--c-gradient-brand); color: #fff; }
.appbar--brand .appbar__title, .appbar--brand .appbar__btn { color: #fff; }
.appbar--brand .appbar__btn:hover { background: rgba(255,255,255,0.18); }

/* home greeting header (brand) */
/* Neutral header (Talino-style): color lives in ONE hero card below, not the header.
   The wordmark carries the brand; the status bar reads dark over the light canvas. */
.home-head { flex: 0 0 auto; background: transparent; color: var(--c-text); padding: calc(36px + var(--space-8)) var(--space-16) var(--space-8); }
.home-head__row { display: flex; align-items: center; justify-content: space-between; gap: var(--space-12); }
.home-head__brand { display: flex; align-items: center; gap: var(--space-8); font-family: var(--font-brand); font-weight: 700; font-size: 1.1875rem; letter-spacing: -0.01em; color: var(--c-accent-strong); }
/* Logo mark = the bee head in a golden badge (Jollibee's own badge treatment). The badge is a
   fixed circle, so it centers cleanly in any lockup — no per-pixel alignment needed. */
.fmark-badge { display: inline-flex; align-items: center; justify-content: center; border-radius: 50%; background: var(--c-brand-yellow); box-shadow: 0 2px 8px rgba(0,0,0,0.12); flex: 0 0 auto; }
.home-head__brand [data-icon], .launch__brand [data-icon] { display: inline-flex; align-items: center; }
.home-head__sfx { color: var(--c-text-2); }
/* Greeting on ONE line, prominent (baseline-aligned). */
.home-head__greet { margin-top: var(--space-16); display: flex; align-items: baseline; gap: var(--space-6); }
.home-head__hi { font-family: var(--font-display); font-size: 1.25rem; font-weight: 500; color: var(--c-text); }
.home-head__name { font-family: var(--font-display); font-size: 1.25rem; font-weight: 700; line-height: 1.1; color: var(--c-text); }
.home-head__bell { width: 40px; height: 40px; border-radius: var(--radius-full); background: var(--c-subtle); color: var(--c-text); display: inline-flex; align-items: center; justify-content: center; border: none; cursor: pointer; position: relative; }
.home-head__dot { position: absolute; top: 9px; right: 10px; width: 8px; height: 8px; border-radius: var(--radius-full); background: var(--c-accent); border: 2px solid var(--c-canvas); }

/* ---- Points card ----------------------------------------------------------- */
/* The ONE hero color moment — a single RED brand card on the neutral canvas. */
.points-card { background: var(--c-gradient-brand); color: #fff; border-radius: var(--radius-2xl); padding: var(--space-16); box-shadow: 0 10px 26px rgba(228,0,43,0.22); }
.points-card__top { display: flex; align-items: center; justify-content: space-between; font-family: var(--font-display); font-weight: 600; font-size: 0.8125rem; }
.points-card__label { opacity: 0.95; display: inline-flex; align-items: center; gap: var(--space-6); }
.points-card__qr { display: inline-flex; align-items: center; gap: var(--space-4); background: rgba(255,255,255,0.22); border-radius: var(--radius-full); padding: 4px 10px; font-size: 0.6875rem; }
.points-card__value { font-family: var(--font-brand); font-weight: 700; font-size: 2.25rem; line-height: 1.05; margin-top: var(--space-8); letter-spacing: -0.01em; }
.points-card__unit { font-family: var(--font-display); font-size: 0.9375rem; font-weight: 600; opacity: 0.92; }
.points-card__pending { display: inline-flex; align-items: center; gap: var(--space-6); background: rgba(255,255,255,0.20); border-radius: var(--radius-full); padding: 4px 10px; font-family: var(--font-display); font-size: 0.75rem; font-weight: 600; margin-top: var(--space-8); }
.points-card__pending .ficon { width: 14px; height: 14px; }
/* Primary action lives INSIDE the hero card (SFC+ "Pay with points" pattern) —
   a white pill on the red card, so scanning is seamless, not a separate card. */
.points-card__cta { display: flex; align-items: center; justify-content: center; gap: var(--space-8); width: 100%; margin-top: var(--space-12); background: #fff; color: var(--c-accent-strong); border: none; border-radius: var(--radius-full); padding: var(--space-8); font-family: var(--font-display); font-weight: 700; font-size: 0.9375rem; cursor: pointer; box-shadow: 0 2px 8px rgba(0,0,0,0.12); }
.points-card__cta .ficon { width: 20px; height: 20px; }

/* ---- Section + generic ----------------------------------------------------- */
.sec { margin-top: var(--space-16); }
.sec__head { display: flex; align-items: center; justify-content: space-between; margin-bottom: var(--space-12); }
.sec__title { font-family: var(--font-display); font-weight: 600; font-size: 1.0625rem; }
.sec__link { font-family: var(--font-display); font-weight: 600; font-size: 0.8125rem; color: var(--c-accent-strong); background: none; border: none; cursor: pointer; }
.stack { display: flex; flex-direction: column; gap: var(--space-12); }
.stack--8 { gap: var(--space-8); }
.row { display: flex; align-items: center; }
.gap-8 { gap: var(--space-8); } .gap-12 { gap: var(--space-12); }
.spacer { flex: 1; }
.muted { color: var(--c-text-2); }
.center { text-align: center; }

/* quick actions grid on home */
.qa { display: grid; grid-template-columns: repeat(4, 1fr); gap: var(--space-8); }
.qa__item { display: flex; flex-direction: column; align-items: center; gap: var(--space-8); padding: var(--space-8) var(--space-4); border-radius: var(--radius-lg); border: none; background: transparent; cursor: pointer; }
/* Sparse, VARIED secondary tints (like Talino's pastel chips) — soft bg + colored glyph. */
/* Chips use Jollibee's official benefit-tile colors as soft tints (faint bg + tile glyph). */
.qa__ic { width: 46px; height: 46px; border-radius: var(--radius-full); display: inline-flex; align-items: center; justify-content: center; background: color-mix(in srgb, var(--c-tile-red) 12%, var(--c-surface)); color: var(--c-tile-red); }
.qa__ic--teal { background: color-mix(in srgb, var(--c-tile-teal) 14%, var(--c-surface)); color: color-mix(in srgb, var(--c-tile-teal) 80%, #000); }
.qa__ic--gold { background: color-mix(in srgb, var(--c-tile-gold) 20%, var(--c-surface)); color: color-mix(in srgb, var(--c-tile-gold) 58%, #000); }
.qa__ic--orange { background: color-mix(in srgb, var(--c-tile-orange) 14%, var(--c-surface)); color: var(--c-tile-orange); }
.qa__ic .ficon { width: 22px; height: 22px; }
.qa__label { font-family: var(--font-display); font-weight: 600; font-size: 0.75rem; color: var(--c-text-2); text-align: center; }

/* big scan CTA (home hero action) */
.scan-cta { display: flex; align-items: center; gap: var(--space-12); width: 100%; padding: var(--space-16); border: 1px solid var(--c-border); border-radius: var(--radius-xl); background: var(--c-surface-raised); color: var(--c-text); cursor: pointer; text-align: left; box-shadow: 0 1px 3px rgba(0,0,0,0.05); }
.scan-cta__ic { width: 48px; height: 48px; border-radius: var(--radius-lg); background: var(--c-gradient-brand); color: #fff; display: inline-flex; align-items: center; justify-content: center; flex: 0 0 auto; }
.scan-cta__ic .ficon { width: 26px; height: 26px; }
.scan-cta__title { font-family: var(--font-display); font-weight: 700; font-size: 0.9375rem; }
.scan-cta__sub { font-family: var(--font-text); font-size: 0.75rem; color: var(--c-text-2); margin-top: 1px; }

/* promo banner */
/* ---- Promo banner (celebratory — a marketing accent, meant to pop) --------- */
/* Yellow banner (Jollibee's secondary) — on-brand + distinct from the red hero. Compact. */
.banner { position: relative; overflow: hidden; border-radius: var(--radius-xl); background: var(--c-brand-yellow); color: var(--c-on-yellow); padding: var(--space-12) var(--space-16); box-shadow: 0 8px 20px rgba(255,199,44,0.30); }
.banner__deco { position: absolute; right: -10px; top: -14px; color: color-mix(in srgb, var(--c-brand-yellow-strong) 26%, transparent); pointer-events: none; transform: rotate(-12deg); }
.banner__deco .ficon { width: 90px; height: 90px; }
.banner__eyebrow { display: inline-flex; align-items: center; gap: var(--space-4); font-family: var(--font-display); font-weight: 700; font-size: 0.6875rem; letter-spacing: 0.08em; text-transform: uppercase; color: var(--c-accent-strong); position: relative; }
.banner__eyebrow .ficon { width: 13px; height: 13px; }
.banner__title { font-family: var(--font-brand); font-weight: 700; font-size: 1.1875rem; line-height: 1.12; margin-top: var(--space-4); position: relative; }
.banner__sub { font-family: var(--font-text); font-size: 0.8125rem; color: color-mix(in srgb, var(--c-on-yellow) 80%, transparent); margin-top: var(--space-4); position: relative; }
.banner__cta { display: inline-flex; align-items: center; gap: var(--space-6); margin-top: var(--space-12); background: var(--c-accent-solid); color: #fff; border: none; border-radius: var(--radius-full); padding: var(--space-8) var(--space-16); font-family: var(--font-display); font-weight: 700; font-size: 0.8125rem; cursor: pointer; position: relative; }
.banner__cta .ficon { width: 16px; height: 16px; }
.banner-dots { display: flex; justify-content: center; align-items: center; gap: 6px; margin-top: var(--space-12); }
.banner-dots i { display: block; width: 6px; height: 6px; border-radius: var(--radius-full); background: var(--c-border-strong); }
.banner-dots i.is-active { width: 18px; border-radius: 3px; background: var(--c-accent); }
.banner-dots i { cursor: pointer; }

/* Promo carousel — horizontal scroll-snap; each slide is one full-width banner. */
.carousel { overflow-x: auto; overflow-y: hidden; scroll-snap-type: x mandatory; -webkit-overflow-scrolling: touch; scrollbar-width: none; }
.carousel::-webkit-scrollbar { display: none; }
.carousel__track { display: flex; }
.carousel__slide { flex: 0 0 100%; scroll-snap-align: start; display: flex; }
.carousel__slide > * { width: 100%; }
.banner--img { padding: 0; border: none; background: none; overflow: hidden; cursor: pointer; }
.banner__img { display: block; width: 100%; height: 100%; object-fit: cover; object-position: center; }

/* ---- Activity / transaction rows (status-icon list — SFC+ cue) ------------- */
.txn-list { display: flex; flex-direction: column; }
.txn-group-label { font-family: var(--font-display); font-weight: 700; font-size: 0.6875rem; letter-spacing: 0.06em; text-transform: uppercase; color: var(--c-muted); background: var(--c-subtle); padding: var(--space-8) var(--space-16); border-radius: var(--radius-md); margin: var(--space-16) 0 var(--space-8); }
.txn { display: flex; align-items: center; gap: var(--space-12); padding: var(--space-12) var(--space-4); border-bottom: 1px solid var(--c-border); }
.txn:last-child { border-bottom: none; }
.txn__ic { width: 36px; height: 36px; border-radius: var(--radius-full); display: inline-flex; align-items: center; justify-content: center; flex: 0 0 auto; }
.txn__ic .ficon { width: 20px; height: 20px; }
/* Status semantics: pending=orange · earned=green · redeemed=red · rejected=maroon */
.txn__ic--pending { background: var(--c-warning-bg); color: var(--c-warning); }
.txn__ic--earned { background: var(--c-success-bg); color: var(--c-success); }
.txn__ic--redeemed { background: var(--c-accent-faint); color: var(--c-accent-strong); }
.txn__ic--rejected { background: var(--c-error-bg); color: var(--c-error); }
.txn__body { flex: 1; min-width: 0; }
.txn__title { font-family: var(--font-display); font-weight: 600; font-size: 0.875rem; }
.txn__meta { font-family: var(--font-text); font-size: 0.75rem; color: var(--c-muted); margin-top: 1px; }
.txn__amt { font-family: var(--font-display); font-weight: 700; font-size: 0.9375rem; flex: 0 0 auto; }
.txn__amt--earned { color: var(--c-success-text); }
.txn__amt--pending { color: var(--c-warning-text); }
.txn__amt--redeemed { color: var(--c-accent-strong); }
.txn__amt--rejected { color: var(--c-muted); text-decoration: line-through; }
.list-note { font-family: var(--font-text); font-size: 0.75rem; color: var(--c-text-2); padding: var(--space-8) var(--space-4); }
.list-end { display: flex; flex-direction: column; align-items: center; gap: var(--space-6); color: var(--c-text-subtle); padding: var(--space-24) 0; font-family: var(--font-display); font-size: 0.8125rem; }

/* ---- Reward catalog cards -------------------------------------------------- */
.reward { display: flex; gap: var(--space-12); padding: var(--space-12); border: 1px solid var(--c-border); border-radius: var(--radius-xl); background: var(--c-surface); }
.reward--rec { border-color: var(--c-brand-yellow); box-shadow: 0 0 0 1px var(--c-brand-yellow); }
.reward__thumb { width: 76px; height: 76px; border-radius: var(--radius-lg); background: var(--c-gradient-brand); color: #fff; display: inline-flex; align-items: center; justify-content: center; flex: 0 0 auto; overflow: hidden; }
.reward__thumb .ficon { width: 34px; height: 34px; }
.reward__thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
.reward__body { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: var(--space-4); }
.reward__name { font-family: var(--font-display); font-weight: 700; font-size: 0.9375rem; }
.reward__desc { font-family: var(--font-text); font-size: 0.75rem; color: var(--c-text-2); }
.reward__foot { display: flex; align-items: center; justify-content: space-between; gap: var(--space-8); margin-top: auto; }
.reward__cost { font-family: var(--font-display); font-weight: 700; font-size: 0.875rem; color: var(--c-accent-strong); display: inline-flex; align-items: center; gap: var(--space-4); }
.reward__go { display: inline-flex; color: var(--c-muted); }
.reward__lock { display: inline-flex; align-items: center; gap: var(--space-4); font-family: var(--font-display); font-weight: 700; font-size: 0.6875rem; color: var(--c-muted); }
.reward__rec-tag { display: inline-flex; align-items: center; gap: var(--space-4); font-family: var(--font-display); font-weight: 700; font-size: 0.6875rem; color: var(--c-brand-yellow-strong); }
.reward__rec-tag .ficon { width: 13px; height: 13px; }

/* ---- Voucher rows + code --------------------------------------------------- */
/* Wallet: member-since line + total colors matched to the transaction list */
.wallet-since { font-family: var(--font-text); font-size: 0.8125rem; color: var(--c-text-2); margin-top: var(--space-16); }
.kv__v--earn { color: var(--c-success-text); }
.kv__v--redeem { color: var(--c-accent-strong); }

/* Voucher cards — big vertical cards (image on top); tapping opens the QR. */
.vcard { display: block; width: 100%; text-align: left; cursor: pointer; background: var(--c-surface); border: 1px solid var(--c-border); border-radius: var(--radius-2xl); overflow: hidden; box-shadow: 0 2px 10px rgba(8,9,28,0.06); -webkit-tap-highlight-color: transparent; transition: transform 0.1s ease; }
.vcard:active { transform: scale(0.98); }
.vcard__media { display: flex; align-items: center; justify-content: center; aspect-ratio: 4 / 3; background: var(--c-gradient-brand); color: #fff; }
.vcard__media .ficon { width: 44px; height: 44px; }
.vcard__media img { width: 100%; height: 100%; object-fit: cover; display: block; }
.vcard__body { display: block; padding: var(--space-12); }
.vcard__name { display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; overflow: hidden; min-height: 2.6em; font-family: var(--font-display); font-weight: 700; font-size: 0.9375rem; line-height: 1.3; }
.vcard__meta { display: block; font-family: var(--font-text); font-size: 0.75rem; color: var(--c-text-2); margin-top: var(--space-4); }

.vrow { display: flex; align-items: center; gap: var(--space-12); padding: var(--space-12); background: var(--c-surface); border: 1px solid var(--c-border); border-radius: var(--radius-lg); }
.vrow__ic { width: 40px; height: 40px; border-radius: var(--radius-md); background: var(--c-accent-faint); color: var(--c-accent-strong); display: inline-flex; align-items: center; justify-content: center; flex: 0 0 auto; }
.vrow__body { flex: 1; min-width: 0; }
.vrow__name { font-family: var(--font-display); font-weight: 600; font-size: 0.875rem; }
.vrow__meta { font-family: var(--font-text); font-size: 0.6875rem; color: var(--c-muted); margin-top: 1px; }
.vcode { text-align: center; padding: var(--space-16); }
.vcode__box { display: inline-block; font-family: var(--font-mono); font-weight: 700; font-size: 1.375rem; letter-spacing: 0.08em; color: var(--c-accent-strong); border: 2px solid var(--c-accent); border-radius: var(--radius-md); padding: var(--space-8) var(--space-24); background: var(--c-surface); }
.vcode__hint { font-family: var(--font-text); font-size: 0.8125rem; color: var(--c-text-2); margin: 0 auto var(--space-12); max-width: 30ch; }
.qr-box { width: 180px; height: 180px; margin: 0 auto; border-radius: var(--radius-lg); background: var(--c-surface); border: 1px solid var(--c-border); display: flex; align-items: center; justify-content: center; color: var(--c-text); }
.qr-box .ficon { width: 150px; height: 150px; }
.qr-box img { width: 150px; height: 150px; display: block; border-radius: var(--radius-md); }
/* A real QR carries its own four-module quiet zone, so the SVG IS the white plate
   and fills the box — boxing it further would pad the padding and leave the code
   looking half-size. `border-radius: inherit` clips the plate to the box's corners. */
.qr-box svg { width: 100%; height: 100%; display: block; border-radius: inherit; }

/* Member QR (earning instrument) — the voucher QR's sibling, deliberately NOT its
   twin: crew scan both, so this one wears a gold ring and a tag naming what a scan
   does. Everything else is .vcode / .qr-box, unchanged. */
.qr-sub { font-family: var(--font-text); font-size: 0.8125rem; color: var(--c-muted); margin-top: 2px; }
.qr-box--member { position: relative; width: 200px; height: 200px; border: 3px solid var(--c-brand-yellow); }
/* No size override here: .qr-box svg above already fills the ring. */
.qr-box__tag { position: absolute; top: 0; left: 50%; transform: translate(-50%, -50%); white-space: nowrap;
  background: var(--c-brand-yellow); color: var(--c-on-yellow); border-radius: var(--radius-full);
  padding: 3px 12px; font-family: var(--font-display); font-weight: 700; font-size: 0.6875rem;
  letter-spacing: 0.06em; text-transform: uppercase; }
/* The code in plain digits — the third fallback, when neither scanner works and
   crew key it in by hand. */
.qr-num { margin-top: var(--space-16); }
.qr-num__label { display: block; font-family: var(--font-display); font-weight: 600;
  font-size: 0.6875rem; letter-spacing: 0.07em; text-transform: uppercase; color: var(--c-muted); }
.qr-num__val { display: block; font-family: var(--font-mono); font-weight: 700; font-size: 1.0625rem;
  letter-spacing: 0.16em; color: var(--c-text); margin-top: 2px; }
.qr-acts { display: flex; flex-wrap: wrap; gap: var(--space-8); justify-content: center;
  margin-top: var(--space-16); }
.qr-saved { display: none; }
.qr-saved.is-on { display: flex; }

/* Full-screen code. The counter view: the code, big, and nothing else competing
   with it — a bigger target scans faster across a counter. */
.codefs { position: absolute; inset: 0; z-index: 21; display: none; flex-direction: column;
  background: var(--c-surface); padding: calc(36px + var(--space-8)) var(--space-16) var(--space-24); }
.codefs.is-open { display: flex; }
.codefs__close { align-self: flex-end; flex: 0 0 auto; width: 40px; height: 40px; border: none;
  border-radius: var(--radius-full); background: var(--c-surface-hover); color: var(--c-text);
  display: inline-flex; align-items: center; justify-content: center; cursor: pointer; }
.codefs__stage { flex: 1; min-height: 0; display: flex; flex-direction: column; align-items: center;
  justify-content: center; gap: var(--space-16); color: var(--c-text); }
.codefs__stage svg { max-width: 94%; height: auto; }   /* unit-module viewBox, so any uniform scale is safe */
.codefs__num { font-family: var(--font-mono); font-weight: 700; font-size: 1rem; letter-spacing: 0.16em; }

/* A linear barcode needs length, and a phone only has it vertically — so the
   barcode and its digits turn together, as the SFC+ pattern does. */
.bcode { display: block; }
/* Column before the turn, so after it the digits sit BESIDE the bars rather than
   stacked underneath — the way the reference reads at a counter. */
.bc-rot { display: flex; flex-direction: column; align-items: center; gap: var(--space-12);
  transform: rotate(90deg); }
.bc-rot .bcode { width: 480px; height: 170px; }
.bc-num { font-family: var(--font-mono); font-weight: 700; font-size: 1.5rem;
  letter-spacing: 0.06em; white-space: nowrap; color: var(--c-text); }

/* Earn chooser — the two channels as siblings, each stating its timing. Lives in a
   .modal-backdrop/.modal like the redeem sheet; only the rows are net-new. */
.earn-opt { display: flex; align-items: center; gap: var(--space-12); width: 100%; text-align: left;
  background: var(--c-surface-inset); border: 1px solid var(--c-border); border-radius: var(--radius-xl);
  padding: var(--space-12); cursor: pointer; }
.earn-opt + .earn-opt { margin-top: var(--space-8); }
.earn-opt:active { transform: scale(0.98); }
.earn-opt__ic { width: 46px; height: 46px; border-radius: var(--radius-full); flex: 0 0 auto;
  display: inline-flex; align-items: center; justify-content: center;
  background: color-mix(in srgb, var(--c-tile-red) 12%, var(--c-surface)); color: var(--c-tile-red); }
.earn-opt__ic--gold { background: color-mix(in srgb, var(--c-brand-yellow) 22%, var(--c-surface));
  color: var(--c-brand-yellow-strong); }
.earn-opt__ic .ficon { width: 22px; height: 22px; }
.earn-opt__t { display: block; font-family: var(--font-display); font-weight: 700; font-size: 0.9375rem; }
.earn-opt__s { display: block; font-family: var(--font-text); font-size: 0.75rem; color: var(--c-muted); margin-top: 1px; }

/* The chip that opens the Member QR, on the points card. */
.points-card__qr { border: none; cursor: pointer; color: var(--c-text-on-accent);
  font-family: var(--font-display); font-weight: 700; }
.points-card__qr:active { transform: scale(0.96); }

/* ---- Scan viewfinder ------------------------------------------------------- */
.scan-view { position: relative; flex: 1; min-height: 0; overflow: hidden; background: #0c0d1a; display: flex; flex-direction: column; align-items: center; justify-content: center; padding: var(--space-24); color: #fff; }
/* Real device camera feed (behind the frame). Fades in when getUserMedia succeeds;
   on file:// / denied it stays hidden and the receipt mock shows instead. */
.scan-cam { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; opacity: 0; transition: opacity 0.4s ease; z-index: 0; }
.scan-cam.is-live { opacity: 1; }
.scan-view.cam-live .scan-receipt-mock { display: none; }
.scan-frame { position: relative; z-index: 1; width: 78%; max-width: 260px; aspect-ratio: 3 / 4; border-radius: var(--radius-lg); }
.scan-frame::before, .scan-frame::after, .scan-frame > i::before, .scan-frame > i::after { content: ""; position: absolute; width: 30px; height: 30px; border: 3px solid var(--c-brand-yellow); }
.scan-frame::before { top: 0; left: 0; border-right: none; border-bottom: none; border-radius: 8px 0 0 0; }
.scan-frame::after { top: 0; right: 0; border-left: none; border-bottom: none; border-radius: 0 8px 0 0; }
.scan-frame > i::before { bottom: 0; left: 0; border-right: none; border-top: none; border-radius: 0 0 0 8px; }
.scan-frame > i::after { bottom: 0; right: 0; border-left: none; border-top: none; border-radius: 0 0 8px 0; }
.scan-frame__line { position: absolute; left: 8%; right: 8%; top: 8%; height: 2px; background: var(--c-brand-yellow); box-shadow: 0 0 12px var(--c-brand-yellow); opacity: 0.9; animation: scanline 2.4s ease-in-out infinite; }
@keyframes scanline { 0%, 100% { top: 8%; } 50% { top: 88%; } }
.scan-receipt-mock { position: absolute; inset: 12% 18%; background: repeating-linear-gradient(transparent 0 10px, rgba(255,255,255,0.14) 10px 11px); border-radius: 4px; z-index: 1; }
.scan-hint { z-index: 1; font-family: var(--font-text); font-size: 0.875rem; text-align: center; opacity: 0.9; max-width: 30ch; margin: 0 auto; }
.scan-actions { position: absolute; z-index: 2; bottom: var(--space-24); left: 0; right: 0; display: flex; flex-direction: column; align-items: center; gap: var(--space-12); }
.scan-shutter { width: 68px; height: 68px; border-radius: var(--radius-full); background: #fff; border: 4px solid rgba(255,255,255,0.4); cursor: pointer; }
.scan-shutter:active { transform: scale(0.94); }
.scan-close { position: absolute; top: calc(36px + var(--space-8)); left: var(--space-16); z-index: 3; width: 40px; height: 40px; border-radius: var(--radius-full); background: rgba(255,255,255,0.16); color: #fff; border: none; display: inline-flex; align-items: center; justify-content: center; cursor: pointer; }
.scan-title { position: absolute; top: calc(36px + var(--space-12)); left: 0; right: 0; text-align: center; color: #fff; font-family: var(--font-display); font-weight: 700; font-size: 0.9375rem; z-index: 2; }

/* ---- OCR processing screen ------------------------------------------------- */
.proc { flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: var(--space-16); padding: var(--space-48) var(--space-24); text-align: center; }
.proc__spinner { width: 56px; height: 56px; border-radius: var(--radius-full); border: 4px solid var(--c-accent-faint); border-top-color: var(--c-accent); animation: spin 0.8s linear infinite; }
@keyframes spin { to { transform: rotate(360deg); } }
.proc__title { font-family: var(--font-display); font-weight: 700; font-size: 1.125rem; }
.proc__steps { display: flex; flex-direction: column; gap: var(--space-12); margin-top: var(--space-8); text-align: left; }
.proc__step { display: flex; align-items: center; gap: var(--space-8); font-family: var(--font-text); font-size: 0.875rem; color: var(--c-text-subtle); }
.proc__step::before { content: ""; width: 16px; height: 16px; border-radius: var(--radius-full); border: 2px solid var(--c-border-strong); flex: 0 0 auto; transition: background 0.2s ease, border-color 0.2s ease; }
.proc__step.is-active { color: var(--c-text); }
.proc__step.is-done { color: var(--c-text-2); }
.proc__step.is-done::before { background: var(--c-success); border-color: var(--c-success); }

/* ---- Receipt review fields ------------------------------------------------- */
.kv { display: flex; align-items: center; justify-content: space-between; gap: var(--space-12); padding: var(--space-12) 0; border-bottom: 1px solid var(--c-border); }
.kv:last-child { border-bottom: none; }
.kv__k { font-family: var(--font-text); font-size: 0.8125rem; color: var(--c-text-2); }
.kv__v { font-family: var(--font-display); font-weight: 600; font-size: 0.875rem; text-align: right; }
.earn-badge { display: flex; align-items: center; justify-content: center; gap: var(--space-8); background: color-mix(in srgb, var(--c-brand-yellow) 16%, var(--c-surface)); color: var(--c-text); border: 1px solid color-mix(in srgb, var(--c-brand-yellow) 55%, transparent); border-radius: var(--radius-lg); padding: var(--space-12) var(--space-16); font-family: var(--font-display); font-weight: 700; font-size: 1rem; }
.earn-badge .ficon { color: var(--c-brand-yellow-strong); }
.earn-badge__txt { white-space: nowrap; }
.earn-badge b { font-weight: 800; }

/* ---- Big status confirmation (validating / credited) ----------------------- */
.status-hero { flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: center; text-align: center; padding: var(--space-24); gap: var(--space-16); }
.status-hero__ic { width: 84px; height: 84px; border-radius: var(--radius-full); display: inline-flex; align-items: center; justify-content: center; }
.status-hero__ic .ficon { width: 44px; height: 44px; }
.status-hero__ic--pending { background: var(--c-warning-bg); color: var(--c-warning); }
.status-hero__ic--done { background: var(--c-success-bg); color: var(--c-success); }
.status-hero__ic--rejected { background: var(--c-error-bg); color: var(--c-error); }
.status-hero__title { font-family: var(--font-display); font-weight: 800; font-size: 1.5rem; }
.status-hero__sub { font-family: var(--font-text); font-size: 0.9375rem; color: var(--c-text-2); max-width: 32ch; }

/* ---- Sticky footer CTA ----------------------------------------------------- */
.screen__footer { flex: 0 0 auto; padding: var(--space-12) var(--space-16) var(--space-16); background: var(--c-surface); border-top: 1px solid var(--c-border); }

/* ---- Confirm modal --------------------------------------------------------- */
.modal-backdrop { position: absolute; inset: 0; background: rgba(8,9,28,0.5); z-index: 20; display: none; align-items: flex-end; }
.modal-backdrop.is-open { display: flex; }
.modal { width: 100%; background: var(--c-surface); border-radius: var(--radius-2xl) var(--radius-2xl) 0 0; padding: var(--space-24) var(--space-16) var(--space-16); }
.modal__title { font-family: var(--font-display); font-weight: 800; font-size: 1.25rem; margin-bottom: var(--space-8); }
.modal__body { font-family: var(--font-text); font-size: 0.875rem; color: var(--c-text-2); line-height: 1.55; }

/* ---- Wallet stacked cards -------------------------------------------------- */
.wcard { border-radius: var(--radius-xl); padding: var(--space-16); color: #fff; }
.wcard__top { display: flex; align-items: center; justify-content: space-between; font-family: var(--font-display); font-weight: 700; font-size: 1rem; }
.wcard--points { background: var(--c-gradient-points); }
.wcard--vouchers { background: var(--c-gradient-brand); }
.wcard__hint { font-family: var(--font-text); font-size: 0.75rem; opacity: 0.9; }

/* ---- Stepper qty ----------------------------------------------------------- */
.qty { display: flex; align-items: center; justify-content: center; gap: var(--space-16); }
.qty__btn { width: 44px; height: 44px; border-radius: var(--radius-md); border: 1px solid var(--c-border-strong); background: var(--c-surface); color: var(--c-text); font-size: 1.25rem; cursor: pointer; display: inline-flex; align-items: center; justify-content: center; }
.qty__val { font-family: var(--font-display); font-weight: 800; font-size: 1.5rem; min-width: 2ch; text-align: center; }

/* ---- Bottom tab bar -------------------------------------------------------- */
.tabbar { flex: 0 0 auto; display: flex; align-items: flex-end; justify-content: space-around; padding: var(--space-8) var(--space-8) var(--space-12); background: var(--c-surface); border-top: 1px solid var(--c-border); z-index: 5; }
.tabbar__item { display: flex; flex-direction: column; align-items: center; gap: 3px; border: none; background: none; color: var(--c-muted); font-family: var(--font-display); font-weight: 600; font-size: 10px; cursor: pointer; padding: var(--space-4) var(--space-8); }
.tabbar__item .ficon { width: 22px; height: 22px; }
.tabbar__item.is-active { color: var(--c-accent); }
.tabbar__fab { width: 54px; height: 54px; border-radius: var(--radius-full); background: var(--c-gradient-brand); color: #fff; display: inline-flex; align-items: center; justify-content: center; margin-top: -24px; box-shadow: 0 8px 18px rgba(228,0,43,0.4); border: 3px solid var(--c-surface); cursor: pointer; }
.tabbar__fab .ficon { width: 26px; height: 26px; }
.tabbar__fab-label { font-family: var(--font-display); font-weight: 700; font-size: 10px; color: var(--c-accent); margin-top: 2px; }

/* ---- Launcher (index.html) ------------------------------------------------- */
.launch { max-width: 940px; margin: 0 auto; padding: var(--space-48) var(--space-24) var(--space-64); }
.launch__brand { display: flex; align-items: center; gap: var(--space-12); }
.launch__title { font-family: var(--font-display); font-size: 2.25rem; font-weight: 800; letter-spacing: -0.02em; }
.launch__sub { color: var(--c-text-2); font-size: 1.0625rem; margin-top: var(--space-12); max-width: 70ch; }
.launch__section { font-family: var(--font-display); font-size: 0.6875rem; font-weight: 700; letter-spacing: 0.12em; text-transform: uppercase; color: var(--c-muted); margin: var(--space-48) 0 var(--space-16); }
.launch__grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--space-16); }
@media (max-width: 767px) { .launch__grid { grid-template-columns: 1fr; } }
.launch__card { display: flex; flex-direction: column; gap: var(--space-8); padding: var(--space-24); border-radius: var(--radius-2xl); border: 1px solid var(--c-border); background: var(--c-surface); text-decoration: none; color: inherit; transition: border-color .15s ease, box-shadow .15s ease, transform .15s ease; }
.launch__card:hover { border-color: var(--c-accent); box-shadow: 0 8px 28px rgba(8,9,28,0.08); transform: translateY(-1px); }
.launch__card-top { display: flex; align-items: center; gap: var(--space-12); }
.launch__card-icon { width: 40px; height: 40px; border-radius: var(--radius-lg); background: var(--c-accent-faint); color: var(--c-accent-strong); display: inline-flex; align-items: center; justify-content: center; flex: 0 0 auto; }
.launch__card-title { font-family: var(--font-display); font-size: 1.0625rem; font-weight: 700; }
.launch__card-desc { color: var(--c-text-2); font-size: 0.875rem; }
.launch__card-meta { display: flex; flex-wrap: wrap; gap: var(--space-6); margin-top: var(--space-4); }
.fineprint { color: var(--c-text-2); font-size: 0.8125rem; line-height: 1.6; }

/* ---- Scan: upload fallback (when there's no live camera) -------------------- */
/* .fds-btn sets display:inline-flex, which beats [hidden]'s display:none — so a
   hidden button still rendered. One rule, once, for every hidden element. */
[hidden] { display: none !important; }

/* With a parts strip in the action stack, the centred frame collided with the
   hint. Only shifts when there are parts, so the one-frame case is untouched. */
.scan-view.has-parts { justify-content: flex-start; padding-top: calc(36px + var(--space-48)); }

/* Receipt parts — thumbnails of the frames captured so far, on the camera's dark
   surface (rgba here matches the rest of .scan-view, which is photographic, not
   themed). Removable, because editing a misread is out of Phase 1: a bad frame has
   to be droppable at capture time or it is stuck. */
.parts { position: relative; z-index: 2; display: flex; align-items: center; gap: var(--space-12); }
.part { position: relative; width: 46px; height: 60px; border-radius: var(--radius-md);
  overflow: visible; flex: 0 0 auto; }
.part img { width: 100%; height: 100%; object-fit: cover; display: block;
  border-radius: var(--radius-md); border: 2px solid rgba(255,255,255,0.55); }
.part__n { position: absolute; bottom: 2px; left: 2px; right: 2px; border-radius: 3px;
  background: rgba(0,0,0,0.55); color: #fff; font-family: var(--font-display);
  font-weight: 700; font-size: 10px; text-align: center; }
.part__x { position: absolute; top: -7px; right: -7px; width: 20px; height: 20px; padding: 0;
  border: none; border-radius: var(--radius-full); background: var(--c-surface);
  color: var(--c-text); display: inline-flex; align-items: center; justify-content: center;
  cursor: pointer; box-shadow: 0 1px 4px rgba(0,0,0,0.4); }
.part--add { display: inline-flex; align-items: center; justify-content: center;
  width: 46px; height: 60px; border-radius: var(--radius-md); cursor: pointer;
  border: 2px dashed rgba(255,255,255,0.5); background: rgba(255,255,255,0.08); color: #fff; }
.scan-shutter:disabled { opacity: 0.4; cursor: not-allowed; }
/* Review: how many frames the read came from. */
.rev-parts { font-family: var(--font-display); font-weight: 600; font-size: 0.75rem;
  color: var(--c-accent-strong); margin-top: var(--space-8); }

.scan-upload { z-index: 2; background: rgba(255,255,255,0.14); color: #fff; border: 1px solid rgba(255,255,255,0.28); border-radius: var(--radius-full); padding: var(--space-6) var(--space-16); font-family: var(--font-display); font-weight: 600; font-size: 0.8125rem; cursor: pointer; -webkit-backdrop-filter: blur(4px); backdrop-filter: blur(4px); }
.scan-upload:hover { background: rgba(255,255,255,0.24); }
.scan-upload:active { transform: scale(0.97); }

/* ---- Rewards: locked (can't afford yet) ------------------------------------ */
.reward--locked .reward__thumb { filter: grayscale(0.5); opacity: 0.72; }
.reward--locked .reward__cost { color: var(--c-muted); }
.fds-btn:disabled { opacity: 0.5; cursor: not-allowed; box-shadow: none; }

/* ---- Small tactility polish ------------------------------------------------ */
.reward[role="button"] { cursor: pointer; -webkit-tap-highlight-color: transparent; transition: transform 0.1s ease; }
.reward[role="button"]:active { transform: scale(0.98); }
.vrow { transition: background 0.15s ease, border-color 0.15s ease; }
.qa__item, .points-card__cta, .banner__cta { transition: transform 0.12s ease; }
.qa__item:active, .points-card__cta:active, .banner__cta:active { transform: scale(0.97); }
/* Soft sheen on the points hero so the brand red reads a touch richer. */
.points-card { position: relative; overflow: hidden; }
.points-card > * { position: relative; z-index: 1; }
.points-card::after { content: ""; position: absolute; inset: 0; z-index: 0; background: radial-gradient(120% 80% at 85% -10%, rgba(255,255,255,0.22), transparent 60%); pointer-events: none; }

/* ---- Receipt review: header + item lines ----------------------------------- */
.rev-intro { margin-top: var(--space-16); }
.rev-intro__title { display: flex; align-items: center; gap: var(--space-8); font-family: var(--font-display); font-weight: 700; font-size: 1.0625rem; line-height: 1.3; }
.rev-intro__title .ficon { color: var(--c-accent); flex: 0 0 auto; }
.rev-intro__sub { font-family: var(--font-text); font-size: 0.875rem; color: var(--c-text-2); margin-top: var(--space-8); line-height: 1.6; max-width: 42ch; }
.sec__note { font-family: var(--font-text); font-size: 0.75rem; color: var(--c-text-2); }
.kv__v--amt { font-weight: 700; }
.kv__v--addr { max-width: 62%; line-height: 1.4; }
.ritem { display: grid; grid-template-columns: 1.5rem 1fr auto; align-items: baseline; column-gap: var(--space-8); font-family: var(--font-text); font-size: 0.875rem; color: var(--c-text); }
.ritem__qty { color: var(--c-muted); font-variant-numeric: tabular-nums; }
.ritem__name { min-width: 0; font-weight: 600; }
.ritem__price { text-align: right; color: var(--c-text); font-weight: 600; font-variant-numeric: tabular-nums; white-space: nowrap; }
.ritem--mod { font-size: 0.8125rem; color: var(--c-muted); }
.ritem--mod .ritem__name { padding-left: 12px; font-weight: 400; }

/* ---- Full-bleed on real mobile viewports ----------------------------------- */
@media (max-width: 560px) {
  .app { padding: 0; }
  .phone { width: 100%; height: 100vh; height: 100dvh; border-radius: 0; box-shadow: none; }
  /* Full-bleed on a real phone: the device already shows the OS status bar, so the
     simulated one would double up. Hide it and reclaim its 36px reservation. The
     status bar stays on the framed desktop view (there it reads as phone chrome). */
  .phone__statusbar { display: none; }
  .appbar { padding-top: var(--space-12); }
  .home-head { padding-top: var(--space-16); }
  .scan-close { top: var(--space-12); }
  .scan-title { top: var(--space-16); }
}

/* A voucher the till has already consumed. Dimmed rather than recoloured — it is
   still the member's record, just spent. */
.vcard--used { opacity: .55; }
/* A spent voucher’s code, dimmed for the same reason: it is still the member’s
   record, and it is no longer worth scanning. */
.qr-box--used { opacity: .45; }

/* One small pulse when the balance changes — the card acknowledging that something
   landed. Kept short and shallow on purpose: any bigger and it competes with the
   number it exists to draw attention to. */
@keyframes jr-bump { 0% { transform: none; } 35% { transform: scale(1.025); } 100% { transform: none; } }
.points-card.is-bump { animation: jr-bump 420ms ease-out; }
@media (prefers-reduced-motion: reduce) { .points-card.is-bump { animation: none; } }

/* Capture guidance sits with the CONTROLS, not over the viewfinder — a camera screen
   needs its frame, and a three-step list read once does not earn permanent space.
   The steps live in a sheet behind "How to scan a long receipt". */
.scan-how { background: none; border: 0; padding: 2px 8px; cursor: pointer;
  font-family: var(--font-text); font-size: 0.8125rem; color: var(--c-surface);
  opacity: 0.75; text-decoration: underline; text-underline-offset: 2px; }
.scan-how:active { opacity: 1; }
.howto { margin: 0 0 var(--space-12); padding-left: 1.15rem; font-family: var(--font-text);
  font-size: 0.875rem; line-height: 1.55; color: var(--c-text-2); text-align: left; }
.howto li + li { margin-top: 4px; }

/* A Review field that did not read. Every field is required, so this is a blocker,
   not a nicety — it says which photo to take next. */
.kv__v--missing { color: var(--c-warning-text); font-weight: 600; }

/* The recommendation appears and disappears with the API's personalization flag,
   which can flip from another device mid-demo — so it animates instead of popping,
   and the catalog below slides with it. max-height is set in JS because an `auto`
   height cannot be transitioned. */
.rec-slot { overflow: hidden; max-height: 0; opacity: 0; transform: translateY(-8px);
  transition: max-height 340ms ease, opacity 240ms ease, transform 340ms ease; }
.rec-slot.is-in { opacity: 1; transform: none; }
@media (prefers-reduced-motion: reduce) { .rec-slot { transition: none; } }

/* Freshly inserted recommendation content fades up. A CSS animation rather than a
   class toggle, so it runs on insert with no JS choreography. */
.rec-in { animation: rec-in 260ms ease-out both; }
@keyframes rec-in { from { opacity: 0; transform: translateY(4px); } to { opacity: 1; transform: none; } }

/* The "working it out" state the slot opens on, before the pick lands. */
.rec-load { display: flex; align-items: center; gap: var(--space-12);
  background: var(--c-surface); border: 1px solid var(--c-border); border-radius: var(--radius-lg);
  padding: var(--space-16); font-family: var(--font-text); font-size: 0.875rem; color: var(--c-text-2); }
.rec-spin { flex: 0 0 auto; width: 18px; height: 18px; border-radius: var(--radius-full);
  border: 2px solid var(--c-border); border-top-color: var(--c-accent);
  animation: rec-spin 720ms linear infinite; }
@keyframes rec-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) {
  .rec-in { animation: none; }
  .rec-spin { animation: none; border-top-color: var(--c-accent); }
}

/* ══════════════════════════════════════════════════════════════════════════
   Consent and preferences (item 7)
   A consent gate is an affirmative act, so it uses a checkbox rather than the
   switch the preferences rows use — a switch reads as a setting already in
   force, which is the wrong thing to say at the moment of asking. Foundry has no
   checkbox, so this one is the app's own and is not named fds-*.
   ══════════════════════════════════════════════════════════════════════════ */
.consent-lede {
  margin: var(--space-8) 0 var(--space-16); color: var(--c-text-2);
  font-size: 0.9375rem; line-height: 1.5;
}
.consent-card {
  display: block; background: var(--c-surface); border: 1px solid var(--c-border);
  border-radius: var(--radius-lg); padding: var(--space-16); margin-bottom: var(--space-12);
  cursor: pointer;
}
.consent-card__top { display: flex; align-items: center; gap: var(--space-8); margin-bottom: var(--space-8); }
.consent-card__title { font-family: var(--font-display); font-weight: 700; font-size: 1rem; color: var(--c-text); }
.consent-card__need {
  margin-left: auto; font-size: 0.75rem; font-weight: 600; letter-spacing: .02em;
  padding: 2px 8px; border-radius: var(--radius-full);
  background: var(--c-subtle); color: var(--c-muted);
}
/* The one the member cannot decline is marked as such rather than left to be
   discovered by a disabled button. */
.consent-card__need--must { background: var(--c-accent-faint); color: var(--c-accent-text); }
.consent-card__body { margin: 0; color: var(--c-text-2); font-size: 0.875rem; line-height: 1.55; }
.consent-card__links { display: flex; flex-wrap: wrap; gap: var(--space-12); margin-top: var(--space-8); }
.consent-card__links a {
  font-size: 0.875rem; font-weight: 600; color: var(--c-accent-text); text-decoration: underline;
}
.consent-card__check {
  display: flex; align-items: center; gap: var(--space-8); margin-top: var(--space-12);
  padding-top: var(--space-12); border-top: 1px solid var(--c-border);
  font-size: 0.9375rem; font-weight: 600; color: var(--c-text);
}
.consent-foot { margin: var(--space-8) 0 0; text-align: center; font-size: 0.75rem; color: var(--c-muted); }

.cbox { position: relative; display: inline-flex; flex: 0 0 auto; }
.cbox input { position: absolute; opacity: 0; width: 0; height: 0; }
.cbox__box {
  width: 22px; height: 22px; border-radius: var(--radius-sm);
  border: 2px solid var(--c-border-strong); background: var(--c-surface);
  display: inline-flex; align-items: center; justify-content: center;
  color: transparent; transition: background .12s ease, border-color .12s ease;
}
.cbox input:checked + .cbox__box { background: var(--c-accent); border-color: var(--c-accent); color: #fff; }
.cbox input:focus-visible + .cbox__box { outline: 2px solid var(--c-accent); outline-offset: 2px; }

.pref-card { padding: var(--space-4) var(--space-16); }
.pref-row { padding: var(--space-12) 0; }
.pref-row + .pref-row { border-top: 1px solid var(--c-border); }
.pref-row__text { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.pref-row__label { font-weight: 600; font-size: 0.9375rem; color: var(--c-text); }
.pref-row__help { font-size: 0.8125rem; color: var(--c-text-2); line-height: 1.45; }
.pref-said {
  margin: var(--space-8) 0 0; font-size: 0.875rem; font-weight: 600;
  color: var(--c-success-text); line-height: 1.45;
}
.pref-note { margin: var(--space-8) 0 0; font-size: 0.8125rem; color: var(--c-warning-text); }
.pref-rec { padding: var(--space-12) 0; font-size: 0.875rem; color: var(--c-text-2); line-height: 1.55; }
.pref-rec + .pref-rec { border-top: 1px solid var(--c-border); }
.pref-rec b { color: var(--c-text); }
