/* ==========================================================================
   Jackpot Music Game — site.css
   The theme's entire visual layer. One stylesheet, no @import, no CDN,
   no hosted font service (Hard Rule 2 / F1).

   Reading order:
     1. Tokens            — every colour, family and measure, once.
     2. Fonts             — four self-hosted woff2 files.
     3. Base elements     — reset, document, typography scale.
     4. Utilities         — wrap, eyebrow, visually-hidden, skip link.
     5. Buttons           — the one interactive vocabulary.
     6. V1  Header        — sticky bar + the no-JS nav disclosure.
     7. V13 Action bar    — the mobile sticky bottom bar.
     8. V14 Footer        — link columns + legal strip.
     9. Page patterns     — V2-V12, V15 (added in phases 3-5).
    10. Reduced motion    — the global kill switch, last so it wins.

   Breakpoints are the three §2.4 unifies the comps' four ragged ones to,
   written desktop-first to match the comps' idiom:
     lg  >= 1024px   base rules
     md   768-1023   @media (max-width: 1023px)
     sm  <=  767px   @media (max-width:  767px)
     xs  <=  400px   verify-only; no rules should be needed
   ========================================================================== */

/* ---------- 1. Tokens ---------------------------------------------------- */

:root {
  /* Colour — the block both comps share, extracted once. */
  --ink: #1B1033;
  --ink-2: #251743;
  --surface: #2E1C55;
  --purple: #462E8D;
  --purple-lt: #A991F0;
  --green: #4B9E46;
  --green-br: #6BD45F;
  --amber: #F5B841;
  --paper: #FDF6E8;
  --paper-dim: #E8DFCB;
  --line: rgba(253, 246, 232, .14);

  /* Literals the comps inlined, promoted to tokens (§2.1). */
  --on-green: #0F2A0C;
  --green-hover: #7FE472;
  --purple-hover: #553BA5;
  --ink-on-paper: #241536;
  --body-on-paper: #4C3A63;
  --eyebrow-on-paper: #7B5AC4;
  --store-ink: #141018;

  /* §2.1.1 contrast floor. The comps use .44-.6 alpha for muted text,
     which measures 3.3-4.7:1 on --ink and fails WCAG AA. Every one of
     those uses this token instead: .72 alpha measures ~9:1 on --ink and
     ~7.8:1 on --surface. Deliberate, recorded deviation (F11). */
  --paper-muted: rgba(253, 246, 232, .72);

  /* §2.1.1 extended. The comps' player panel is #fff and rgba(#fff,.9)
     on --green, which measures 3.35:1 and 3.1:1 — the heading scrapes
     large-text AA, the body fails outright. This darker green is the
     panel ground ONLY (--green still paints borders and accents): white
     on it measures 5.04:1 and --paper measures 4.69:1. See BUILD_LOG
     phase 2 for the arithmetic. */
  --green-panel: #3A7D36;

  /* Type */
  --display: "Bricolage Grotesque", Georgia, serif;
  --body: "Figtree", system-ui, -apple-system, sans-serif;
  --mono: "DM Mono", ui-monospace, monospace;

  --h1: clamp(38px, 6.4vw, 74px);          /* home; overridden per page */
  --h2: clamp(30px, 4.4vw, 50px);          /* home */
  --h2-compact: clamp(26px, 3.4vw, 38px);  /* /app and /privacy */
  --h3: 20px;

  /* Space and measure */
  --wrap: 1180px;
  --pad: clamp(20px, 5vw, 48px);
  --sec-pad: clamp(60px, 8vw, 104px);          /* home sections */
  --sec-pad-compact: clamp(52px, 7vw, 88px);   /* /app and /privacy */
  --header-h: 72px;
  --bar-h: 64px;

  /* Radii (§2.3) */
  --r-pill: 999px;
  --r-panel: 22px;
  --r-split: 20px;
  --r-ticket: 18px;
  --r-card: 16px;
  --r-store: 14px;
  --r-row: 12px;
}

/* The compact scale is a variant of the same scale, not a second one. */
.page-app,
.page-privacy {
  --h1: clamp(36px, 5.6vw, 64px);
  --h2: var(--h2-compact);
  --sec-pad: var(--sec-pad-compact);
}

/* ---------- 2. Fonts ----------------------------------------------------- */
/* Self-hosted, latin-subset, weight-subset at source. The bake rewrites
   these url() references to the same fingerprinted paths base.html
   preloads through {% asset %}, so the preload and the @font-face are
   byte-identical URLs and the font is in cache at first paint. */

@font-face {
  font-family: "Bricolage Grotesque";
  src: url(/assets/fonts/bricolagegrotesque-variable-latin.a79fdb52d4.woff2) format("woff2");
  font-weight: 700 800;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "Figtree";
  src: url(/assets/fonts/figtree-variable-latin.4ba7d3d096.woff2) format("woff2");
  font-weight: 400 700;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "DM Mono";
  src: url(/assets/fonts/dmmono-400-latin.e1896b13b2.woff2) format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "DM Mono";
  src: url(/assets/fonts/dmmono-500-latin.9964608a84.woff2) format("woff2");
  font-weight: 500;
  font-style: normal;
  font-display: swap;
}

/* ---------- 3. Base elements --------------------------------------------- */

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

html {
  scroll-behavior: smooth;
  /* The header is sticky, so an anchor jump would otherwise land the
     target underneath it. The comps omit this and #how / #jackpot / #faq
     land behind the bar; corrected here. */
  scroll-padding-top: calc(var(--header-h) + 12px);
}

body {
  margin: 0;
  background: var(--ink);
  color: var(--paper);
  font-family: var(--body);
  /* 16.5px floor: the comps drop to 16px below 560px, which is under the
     legibility floor this build sets for mobile (§2.2). */
  font-size: clamp(16.5px, 1.2vw, 17px);
  line-height: 1.65;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

img,
svg { max-width: 100%; display: block; }

a { color: inherit; }

/* Editor content can contain a token longer than its column — a URL, a
   run-on word — and an unbreakable token is the classic cause of
   horizontal scroll on a phone. Measured: a 53-character word in a FAQ
   summary pushed 133px of page overflow at 390px. `break-word` breaks
   only when a line cannot otherwise fit, so ordinary prose is unchanged. */
h1, h2, h3, p, li, summary, .song, .artist, .fact b, .ticket .amt, .card-cat {
  overflow-wrap: break-word;
}

h1, h2, h3 {
  font-family: var(--display);
  font-weight: 800;
  line-height: 1.03;
  letter-spacing: -.02em;
  margin: 0;
}
h1 { font-size: var(--h1); }
h2 { font-size: var(--h2); }
h3 {
  font-size: var(--h3);
  font-weight: 700;
  letter-spacing: -.01em;
  line-height: 1.2;
}

/* ---------- 4. Utilities -------------------------------------------------- */

.wrap {
  max-width: var(--wrap);
  margin-inline: auto;
  /* The safe-area insets are folded in here rather than into --pad so a
     notch in landscape never sits over content (§2.7). */
  padding-inline:
    calc(var(--pad) + env(safe-area-inset-left))
    calc(var(--pad) + env(safe-area-inset-right));
}

.eyebrow {
  font-family: var(--mono);
  font-size: 12px;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--purple-lt);
  margin: 0 0 18px;
}

/* Present to assistive tech and to the bake's no-script text extractor,
   absent from the visual design. Used for the brand name beside the logo
   mark — see base.html. */
.vh {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0; border: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* Empty-state defence. A slot with no content renders as an empty
   element, so clearing a section heading in the admin leaves
   `<h2></h2>` behind — invisible on screen but a real empty heading in
   the accessibility tree, and the "stray heading" §10D asks not to
   have. One rule covers every heading and eyebrow on the site,
   including any added later, and `display: none` removes them from the
   accessibility tree rather than merely hiding them. `:where()` keeps
   the specificity at zero so it never fights a real rule. */
:where(h1, h2, h3, .eyebrow, .card-cat, .card-rd, .ticket .lbl, .qr-panel .lbl):empty {
  display: none;
}

.skip { position: absolute; left: -9999px; }
.skip:focus {
  left: 12px; top: 12px; z-index: 99;
  display: inline-flex; align-items: center; min-height: 44px;
  background: var(--paper); color: var(--ink);
  padding: 10px 16px; border-radius: 8px;
  font-weight: 700; text-decoration: none;
}

:focus-visible {
  outline: 3px solid var(--amber);
  outline-offset: 3px;
  border-radius: 4px;
}

/* ---------- 5. Buttons ---------------------------------------------------- */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 9px;
  font-family: var(--body);
  font-weight: 700;
  font-size: 16px;
  line-height: 1.2;
  text-decoration: none;
  padding: 14px 26px;
  min-height: 48px;
  border-radius: var(--r-pill);
  border: 2px solid transparent;
  cursor: pointer;
  transition: transform .16s, background .16s, border-color .16s;
}

/* The comps' lift is a pointer affordance. On touch it never fires and
   would only add latency, so it is gated on a real hover device and
   :active carries the feedback everywhere (§2.7). */
@media (hover: hover) and (pointer: fine) {
  .btn:hover { transform: translateY(-2px); }
  .btn-go:hover { background: var(--green-hover); }
  .btn-ghost:hover { border-color: var(--paper); }
  .btn-dark:hover { background: var(--ink-2); }
}
.btn:active { transform: translateY(1px); }
.btn-go:active { background: var(--green-hover); }
.btn-ghost:active { border-color: var(--paper); background: rgba(253, 246, 232, .1); }
.btn-dark:active { background: var(--ink-2); }

.btn-go { background: var(--green-br); color: var(--on-green); }
.btn-ghost { border-color: rgba(253, 246, 232, .32); color: var(--paper); }
/* R1 §7 · the player panel's button. It was a solid paper pill, which
   appears nowhere else on the site — paper is text here, and buttons are
   green-bright or outlined. --purple was the client's suggestion but sits
   at ~3:1 against the panel green at a similar hue and goes quiet; --ink
   is already the site's ground colour rather than a new introduction, and
   it inverts correctly against the venue panel beside it (light button on
   a dark panel, dark button on a light one). */
.btn-dark { background: var(--ink); color: var(--paper); }
.btn-sm { padding: 10px 20px; font-size: 15px; min-height: 44px; }

/* ---------- 6. V1 — sticky header ---------------------------------------- */

.nav {
  position: sticky;
  top: 0;
  z-index: 50;
  background: rgba(27, 16, 51, .86);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
  border-bottom: 1px solid var(--line);
}

.nav-in {
  display: flex;
  align-items: center;
  gap: clamp(16px, 2.4vw, 32px);
  min-height: var(--header-h);
}

.brand {
  display: flex;
  align-items: center;
  text-decoration: none;
  flex-shrink: 0;
  /* The mark is 34px tall, but the link is interactive and every
     interactive element clears 44px (§2.7). The extra height is hit
     area inside the 72px bar, so nothing moves visually. */
  min-height: 44px;
}
.brand img { height: 34px; width: auto; }

.nav-main { margin-left: auto; min-width: 0; }
.nav-main ul {
  display: flex;
  flex-wrap: nowrap;
  align-items: center;
  /* Six links (three real pages + three section anchors) plus two CTA
     pills have to hold at exactly 1024px, which is why the gap is fluid
     rather than the comps' fixed 28px. */
  gap: clamp(14px, 1.6vw, 26px);
  font-size: 15px;
  font-weight: 500;
  list-style: none;
  margin: 0;
  padding: 0;
}
.nav-main a {
  display: inline-flex;
  align-items: center;
  min-height: 44px;   /* hit area only; the bar is 72px so nothing shifts */
  text-decoration: none;
  color: var(--paper-dim);
  white-space: nowrap;
  transition: color .18s;
}
.nav-main a[aria-current="page"] { color: var(--paper); }
@media (hover: hover) and (pointer: fine) {
  .nav-main a:hover { color: var(--paper); }
}

.nav-cta { display: flex; gap: 10px; flex-shrink: 0; }

/* The disclosure. Native details/summary: real markup, opens on tap with
   no script, keyboard operable for free. It is the ONLY navigation below
   1024px, which is why it can never depend on JavaScript (§2.7).

   It is a SIBLING of the bar row, so the panel is in normal flow beneath
   the bar and opening it pushes the page down. The summary is lifted
   into the bar by absolute positioning against the sticky header (which
   is a positioned element, so it is the containing block), and the bar
   reserves exactly the summary's width on its right so nothing overlaps.
   Hence the fixed --toggle-w: it makes that reservation exact rather
   than a guess. */
:root { --toggle-w: 106px; }

.nav-menu { display: none; }

.nav-toggle {
  position: absolute;
  top: 0;
  right: calc(var(--pad) + env(safe-area-inset-right));
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 9px;
  width: var(--toggle-w);
  height: var(--header-h);
  font-family: var(--mono);
  font-size: 12px;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--paper);
  cursor: pointer;
  list-style: none;
  user-select: none;
}
.nav-toggle::-webkit-details-marker { display: none; }

/* The two-bar glyph becomes an X when open — a state cue that survives
   with motion switched off and needs no script. */
.nav-toggle::after {
  content: "";
  width: 15px;
  height: 9px;
  border-top: 2px solid currentColor;
  border-bottom: 2px solid currentColor;
  transition: transform .2s;
}
.nav-menu[open] .nav-toggle::after {
  height: 0;
  border-bottom: 0;
  transform: rotate(45deg);
}

.nav-panel {
  border-top: 1px solid var(--line);
  padding: 6px 0 22px;
  max-height: calc(100dvh - var(--header-h) - 12px);
  overflow-y: auto;
  overscroll-behavior: contain;
}
.nav-panel-list {
  list-style: none;
  margin: 0;
  padding: 0;
}
.nav-panel-list + .nav-panel-list { border-top: 1px solid var(--line); }
.nav-panel-list li + li { border-top: 1px solid var(--line); }
.nav-panel-list a {
  display: flex;
  align-items: center;
  min-height: 52px;
  padding: 6px 2px;
  text-decoration: none;
  color: var(--paper-dim);
  font-size: 16.5px;
  font-weight: 600;
}
.nav-panel-list a[aria-current="page"] { color: var(--paper); }
.nav-panel-list a:active { background: rgba(253, 246, 232, .06); }
.nav-panel-cta { width: 100%; margin-top: 18px; }

/* ---------- 7. V13 — sticky bottom action bar (sm only) ------------------- */
/* Derived, not comp-provided. The comps strand the primary action in the
   top-right corner, which on a phone is the furthest point from a thumb.
   Always present rather than scroll-triggered, because scroll-triggering
   needs script and this must work without it. */

.actionbar { display: none; }

/* ---------- 8. V14 — footer ---------------------------------------------- */

.foot {
  border-top: 1px solid var(--line);
  padding: 52px 0 40px;
  font-size: 15px;
}
.foot-in {
  display: flex;
  flex-wrap: wrap;
  gap: 32px;
  justify-content: space-between;
  align-items: flex-start;
}
.foot-brand { max-width: 30ch; }
/* width:auto is load-bearing, not decorative. The <img> carries the
   asset's intrinsic width="225" height="68" (so it can never shift
   layout), and setting only `height` here would leave the attribute
   width in force — rendering the mark 225px wide by 32px tall, i.e.
   squashed to roughly a third of its aspect ratio. The header mark had
   this; the footer one did not. */
.foot-brand img { height: 32px; width: auto; margin-bottom: 14px; }
.foot-tagline {
  margin: 0;
  color: var(--paper-muted);
  max-width: 28ch;
  font-size: 14.5px;
}
.foot a { color: var(--paper-dim); text-decoration: none; }
@media (hover: hover) and (pointer: fine) {
  .foot a:hover { color: var(--paper); text-decoration: underline; }
}
.foot-nav { display: flex; gap: 44px; flex-wrap: wrap; }
.foot-nav ul {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 9px;
}
.foot-nav h2 {
  font-family: var(--mono);
  font-size: 11px;
  font-weight: 500;
  letter-spacing: .13em;
  text-transform: uppercase;
  color: var(--paper-muted);
  display: block;
  margin: 0 0 13px;
  line-height: 1.4;
}
.legal {
  margin: 38px 0 0;
  padding-top: 22px;
  border-top: 1px solid var(--line);
  font-size: 13px;
  color: var(--paper-muted);
  display: flex;
  flex-wrap: wrap;
  gap: 8px 22px;
  list-style: none;
  padding-inline: 0;
}

/* ---------- 9. Page patterns --------------------------------------------- */

/* Sections. Richtext slots arrive as <p> elements, so the patterns style
   `.x p` rather than the wrapper — never restyle richtext per instance. */
/* padding-block, NOT the `padding: X 0` shorthand. Most sections carry
   both classes (`<section class="sec wrap">`), and the shorthand sets
   padding-inline to 0 — which lands after `.wrap` in this stylesheet and
   silently strips the gutters, running body text edge to edge on a
   phone. */
.sec { padding-block: var(--sec-pad); }

/* V5 · Section head.
   R1 §2: the measure now lives on `.sec-head-main`, not on `.sec-head`.
   That is the whole fix. The heading stays at 34ch (36ch on /app) —
   widening it would only have made long headings wider without filling
   the right-hand gutter the client kept annotating. Instead the intro,
   which already existed directly beneath the heading in the same narrow
   column, moves into a second column beside it. */
.sec-head { margin-bottom: var(--sec-head-gap, clamp(34px, 4.5vw, 56px)); }
.sec-head-main { max-width: var(--sec-head-measure, 34ch); }
.sec-head p {
  color: var(--paper-dim);
  font-size: var(--sec-head-p-size, 18px);
  margin: 16px 0 0;
  max-width: var(--sec-head-p-measure, 44ch);
}

/* Desktop only. Below 1024px these are plain blocks and the head stacks
   exactly as it did before R1 — heading, then intro, one column. */
@media (min-width: 1024px) {
  /* R2 · proportions and alignment, after the client reported the heads
     still reading wrong.

     The bug was the measure. `.sec-head-main` capped at 34ch resolves `ch`
     against the BODY font (17px, ~370px), but the heading renders at 50px
     — so "34ch" was never 34 characters of heading, it was TWELVE. That
     forced 4- and 5-line headings (the why-venues heading ran to five) and
     left up to 234px of dead space beside them. Display type wants 20-35
     characters per line.

     So: the column IS the measure here (no arbitrary cap on top of it),
     and the split is 1.25fr/.75fr rather than even — a display heading
     needs materially more room than the deck beside it, and an even split
     was giving the heading the same width as a paragraph at half its size.

     `align-items: end` sets the deck on the heading's last line. Column
     heights will never match exactly; bottom-aligned that reads as
     deliberate editorial setting, top-aligned it reads as a paragraph that
     ran out. This is a considered departure from R1's "top-aligned". */
  .sec-head-split {
    display: grid;
    grid-template-columns: 1.25fr .75fr;
    gap: clamp(32px, 4vw, 64px);
    align-items: end;
  }
  .sec-head-split .sec-head-main { max-width: none; }
  .sec-head-split .sec-head-aside p {
    /* .lede size; the column carries the measure, same as the heading */
    font-size: clamp(17px, 1.9vw, 20px);
    max-width: none;
    margin-top: 0;
  }
  /* A figure in the deck column pushes the text back to the top — an image
     bottom-aligned against a heading looks dropped rather than placed. */
  .sec-head-split:has(.sec-head-figure) { align-items: start; }
  /* A head with no intro has no right column to fill; centring it beats
     leaving an empty one, which would only move the gap (R1 §2). */
  .sec-head-centred { text-align: center; }
  /* Third and last instance of the same unit bug. /app sets
     `--sec-head-measure: 36ch`, which on the wrapper resolves against the
     17px body font: 16 characters of heading at 1440, against 31 at 900.
     Narrower on the bigger screen. The measure moves to the h2, where the
     number means what it says, and the wrapper just centres. */
  .sec-head-centred .sec-head-main { max-width: none; margin-inline: auto; }
  .sec-head-centred h2 { max-width: 30ch; margin-inline: auto; }

  /* R2b · a head with NEITHER a deck nor a centring instruction spans the
     wrap. It still needs a measure, but the cap has to sit on the h2, not
     on the wrapper: `ch` resolves against the element it is written on,
     and the wrapper inherits the 17px body font while the h2 renders at
     50px. That mismatch is the exact bug R2 fixed above, so do not move
     this cap back up to `.sec-head-main`. 30ch here is 30 characters of
     HEADING, comfortably inside the 20-35 display range. */
  .sec-head:not(.sec-head-split):not(.sec-head-centred) .sec-head-main { max-width: none; }
  .sec-head:not(.sec-head-split):not(.sec-head-centred) h2 { max-width: 30ch; }
}

/* R1 §3 · the optional section image, beneath the intro in the same
   column. Matches the value cards below it: 16px radius, --line border. */
.sec-head-figure { margin-top: 22px; }
.sec-head-figure :where(img, picture) {
  width: 100%;
  height: auto;
  border-radius: var(--r-card);
  border: 1px solid var(--line);
  display: block;
}

/* V2 · Hero split.
   The two washes are decorative and pointer-events:none. `overflow:hidden`
   on the section is what keeps their negative insets from producing
   horizontal scroll — do not remove it. */
.hero {
  position: relative;
  overflow: hidden;
  padding: var(--hero-pad, clamp(48px, 7vw, 86px) 0 clamp(56px, 7vw, 90px));
}
/* R1 §4 · the same twin washes now also carry the home jackpot band. The
   selectors are shared rather than duplicated so the two can never drift
   apart — the band is meant to read as the hero's treatment, not as a
   second, similar-looking one. */
.hero::before,
.band-ink::before {
  content: "";
  position: absolute;
  inset: -40% 40% 30% -20%;
  background: radial-gradient(closest-side, rgba(70, 46, 141, .55), transparent 72%);
  pointer-events: none;
}
.hero::after,
.band-ink::after {
  content: "";
  position: absolute;
  right: -10%; top: -25%;
  width: 60%; height: 80%;
  background: radial-gradient(closest-side, rgba(75, 158, 70, .22), transparent 70%);
  pointer-events: none;
}
/* Per-page variation goes through custom properties, never through a
   more specific selector: `.page-app .hero-grid` would outrank the
   `.hero-grid { grid-template-columns: 1fr }` in the md media block and
   silently keep /app two-column on a tablet. A variable changes the
   value without changing who wins. */
.hero-grid {
  position: relative;
  display: grid;
  grid-template-columns: var(--hero-cols, 1.05fr .95fr);
  gap: var(--hero-gap, clamp(36px, 5vw, 72px));
  align-items: center;
}
.page-app {
  --hero-cols: 1.1fr .9fr;                       /* §3 V2 */
  --hero-gap: clamp(34px, 5vw, 68px);
  --hero-pad: clamp(46px, 6.5vw, 80px) 0 clamp(46px, 6vw, 72px);
  --sec-head-measure: 36ch;
  --sec-head-p-measure: 46ch;
  --sec-head-p-size: 17.5px;
  --sec-head-gap: clamp(28px, 4vw, 46px);
}
.hero-lede p {
  font-size: clamp(17px, 1.9vw, 20px);
  color: var(--paper-dim);
  max-width: 34em;
  margin: 22px 0 0;
}
.hero-cta { display: flex; flex-wrap: wrap; gap: 14px; margin-top: 32px; }
.hero-note p {
  font-family: var(--mono);
  font-size: 13px;
  color: var(--paper-muted);
  margin: 22px 0 0;
}

/* V3 · Signature song card.
   MOTION CONTRACT (§2.5): the FINISHED state is the base state, and each
   animation plays FROM the unfinished state TOWARD it with fill-mode
   `both`. Switch animation off and the card still reads correctly — three
   found, one live, two to come — with no JavaScript and no fallback
   branch. Do not restructure this into "start empty, animate in". */
.card {
  background: linear-gradient(180deg, var(--surface), var(--ink-2));
  border: 1px solid rgba(169, 145, 240, .34);
  border-radius: var(--r-panel);
  padding: 22px;
  box-shadow: 0 34px 70px -28px rgba(0, 0, 0, .85), 0 0 0 1px rgba(0, 0, 0, .25);
  transform: rotate(-1.1deg);
}
.card-top {
  display: flex; align-items: center; justify-content: space-between;
  gap: 12px; padding-bottom: 16px; border-bottom: 1px solid var(--line);
}
.card-cat { font-family: var(--display); font-weight: 700; font-size: 19px; }
.card-rd {
  font-family: var(--mono); font-size: 11px; letter-spacing: .12em;
  text-transform: uppercase; color: var(--amber);
  border: 1px solid rgba(245, 184, 65, .45);
  padding: 4px 10px; border-radius: var(--r-pill); white-space: nowrap;
}
.rows { list-style: none; margin: 14px 0 0; padding: 0; display: flex; flex-direction: column; gap: 7px; }
.row {
  display: flex; align-items: center; gap: 13px;
  padding: 11px 13px; border-radius: var(--r-row);
  background: rgba(253, 246, 232, .045);
  border: 1px solid transparent;
  min-height: 44px;
}
.tick {
  flex-shrink: 0; width: 23px; height: 23px; border-radius: 50%;
  border: 2px solid rgba(169, 145, 240, .55);
  display: grid; place-items: center;
}
.tick svg { width: 12px; height: 12px; opacity: 0; transform: scale(.4); }
.song { display: block; position: relative; width: fit-content; font-size: 15px; font-weight: 600; line-height: 1.25; }
.artist { display: block; font-family: var(--mono); font-size: 11.5px; color: var(--paper-muted); margin-top: 2px; }

.row.done .tick { background: var(--green-br); border-color: var(--green-br); animation: tickIn .38s ease both; }
.row.done .tick svg { opacity: 1; transform: scale(1); animation: markIn .38s ease both; }
.row.done .song { color: var(--paper-muted); }
.row.done .song::after {
  content: ""; position: absolute; left: 0; right: 0; top: 53%;
  height: 2px; border-radius: 2px; background: rgba(107, 212, 95, .8);
  transform: scaleX(1); transform-origin: left;
  animation: strike .38s ease both;
}
.row:nth-child(1) .tick, .row:nth-child(1) .tick svg, .row:nth-child(1) .song::after { animation-delay: .35s; }
.row:nth-child(2) .tick, .row:nth-child(2) .tick svg, .row:nth-child(2) .song::after { animation-delay: .95s; }
.row:nth-child(3) .tick, .row:nth-child(3) .tick svg, .row:nth-child(3) .song::after { animation-delay: 1.55s; }

.row.live { background: rgba(245, 184, 65, .12); border-color: rgba(245, 184, 65, .42); animation: liveIn .4s ease 2.15s both; }
.row.live .tick { border-color: var(--amber); animation: liveTick .4s ease 2.15s both, pulse 1.5s ease-in-out 2.55s infinite; }

@keyframes tickIn { from { background: transparent; border-color: rgba(169,145,240,.55); } to { background: var(--green-br); border-color: var(--green-br); } }
@keyframes markIn { from { opacity: 0; transform: scale(.4); } to { opacity: 1; transform: scale(1); } }
@keyframes strike { from { transform: scaleX(0); } to { transform: scaleX(1); } }
@keyframes liveIn { from { background: rgba(253,246,232,.045); border-color: transparent; } to { background: rgba(245,184,65,.12); border-color: rgba(245,184,65,.42); } }
@keyframes liveTick { from { border-color: rgba(169,145,240,.55); } to { border-color: var(--amber); } }
@keyframes pulse { 0%, 100% { box-shadow: 0 0 0 0 rgba(245,184,65,.5); } 50% { box-shadow: 0 0 0 7px rgba(245,184,65,0); } }

.card-foot {
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  margin-top: 16px; padding-top: 15px; border-top: 1px solid var(--line);
  font-family: var(--mono); font-size: 12px; color: var(--paper-muted);
}
/* The digits stack in one slot and hand off on the same clock as the rows.
   The last digit is the base state, so with motion off the counter reads
   its finished value rather than blank.

   The LAST digit stays in normal flow and the earlier three are laid over
   it. That is what fixes the alignment: the comps' version took every
   digit out of flow inside a fixed `em` box
   (`width:.62em; height:1.1em; vertical-align:text-bottom`), leaving no
   real glyph to sit on the baseline, so the figure rendered visibly below
   the "of 6 checked" beside it. Now one actual digit sizes the box and
   sets the baseline, and since the face is monospaced the three overlaid
   digits land exactly on top of it. The in-flow digit is also the finished
   value, which is the reduced-motion base state. */
/* line-height:1 is part of the fix, not tidying. Inheriting the body's
   1.65 makes this inline-block's box taller than the line's strut, which
   both nudges the whole line's baseline and leaves half-leading above the
   in-flow digit — so the three overlaid digits (at top:0) would sit ~1.3px
   above the final one and jump as the count lands. At line-height:1 the
   box is the glyph, and all four digits coincide exactly. */
.num { position: relative; display: inline-block; line-height: 1; }
.num i { font-style: normal; }
.num i:nth-child(1),
.num i:nth-child(2),
.num i:nth-child(3) { position: absolute; left: 0; top: 0; opacity: 0; }
.num i:nth-child(1) { animation: digFirst .35s linear both; }
.num i:nth-child(2) { animation: dig .6s linear .35s both; }
.num i:nth-child(3) { animation: dig .6s linear .95s both; }
.num i:nth-child(4) { opacity: 1; animation: digLast .01s linear 1.55s both; }
@keyframes digFirst { 0%, 99% { opacity: 1; } 100% { opacity: 0; } }
@keyframes dig { 0% { opacity: 0; } 1%, 99% { opacity: 1; } 100% { opacity: 0; } }
@keyframes digLast { from { opacity: 0; } to { opacity: 1; } }

.now { display: flex; align-items: center; gap: 8px; color: var(--amber); }
.bars { display: flex; align-items: flex-end; gap: 2px; height: 13px; }
.bars i { width: 3px; background: var(--amber); border-radius: 1px; animation: eq .9s ease-in-out infinite; }
.bars i:nth-child(1) { height: 40%; animation-delay: 0s; }
.bars i:nth-child(2) { height: 95%; animation-delay: .18s; }
.bars i:nth-child(3) { height: 60%; animation-delay: .36s; }
.bars i:nth-child(4) { height: 80%; animation-delay: .12s; }
@keyframes eq { 0%, 100% { transform: scaleY(.42); } 50% { transform: scaleY(1); } }

/* V4 · Facts strip */
.facts { border-block: 1px solid var(--line); background: rgba(253, 246, 232, .03); }
/* margin-inline MUST stay `auto` — this element carries `.wrap` too, and a
   `margin: 0` shorthand here silently overrides the centring `.wrap` sets,
   which left the whole facts strip flush against the left edge of the page
   while every other wrap was centred. Same trap as the `padding` shorthand
   on `.sec`: never use a box shorthand on an element that also carries
   `.wrap`. */
.facts-in {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 2px;
  list-style: none;
  margin-block: 0;
  margin-inline: auto;
  padding-block: 0;
}
.fact { padding: 26px 22px; text-align: center; }
.fact b {
  display: block; font-family: var(--display); font-size: 26px;
  font-weight: 800; color: var(--green-br); letter-spacing: -.01em; line-height: 1.1;
}
.fact span {
  font-family: var(--mono); font-size: 11.5px; letter-spacing: .09em;
  text-transform: uppercase; color: var(--paper-muted); display: block; margin-top: 5px;
}

/* V6 · Numbered steps triad. The numeral is a CSS counter on an --ink chip
   sitting astride the rule, so adding or removing a step renumbers itself. */
.steps { display: grid; grid-template-columns: repeat(3, 1fr); gap: clamp(20px, 3vw, 38px); counter-reset: s; list-style: none; margin: 0; padding: 0; }
.step { position: relative; padding-top: 26px; border-top: 2px solid var(--purple); }
.step::before {
  counter-increment: s; content: "0" counter(s);
  position: absolute; top: -15px; left: 0;
  background: var(--ink); padding-right: 12px;
  font-family: var(--mono); font-size: 13px; color: var(--purple-lt); letter-spacing: .08em;
}
.step h3 { margin-bottom: 9px; }
/* The measure cap matters at TABLET, not desktop. At lg the steps are
   3-up and each column is ~33 characters wide, so 64ch never binds; at
   md they go 1-up and the paragraph would otherwise run the full
   content width — measured at 77 characters, past the 75 ceiling and
   the "no full-width text at tablet" rule. */
.step p { color: var(--paper-dim); font-size: 16px; margin: 0; max-width: 64ch; }

/* V7 · Inverted band. R2e: there is now ONE treatment, not two. The cream
   `.band` is gone — R1 moved home's jackpot band off it, R2e moved /app's
   venue notice off it, and a rule with no users does not ship. It is in
   git if the cream ground is ever wanted back. */

/* R1 §4 · the dark variant, home's jackpot band. The client asked both
   "does this need to be a different colour?" and "give this box a white
   border?" — a white border on cream is about 1.05:1, i.e. invisible, so
   both notes pointed at the same answer: stop making this band cream.
   The hero's washes (declared above) supply the depth that stops the page
   becoming one long flat dark run. */
.band-ink { background: var(--ink-2); color: var(--paper); position: relative; overflow: hidden; }
.band-ink .eyebrow { color: var(--purple-lt); }
.band-ink h2 { color: var(--paper); }
.band-ink p { color: var(--paper-muted); font-size: 18px; margin: 20px 0 0; }
.band-ink .band-grid { position: relative; }   /* above the washes */
.band-grid { display: grid; grid-template-columns: 1.15fr .85fr; gap: clamp(32px, 5vw, 64px); align-items: center; }
/* The /app variant of the band: heading block left, one green button
   right. `position: relative` for the same reason .band-grid has it —
   the washes are positioned pseudo-elements and would otherwise paint
   OVER static in-flow content, tinting the heading and body. */
.band-ink .band-row { position: relative; }
.band-row { display: flex; flex-wrap: wrap; align-items: center; gap: clamp(20px, 4vw, 46px); justify-content: space-between; }
.band-row h2 { max-width: 18ch; }
.band-row p { max-width: 52ch; font-size: 17px; margin: 14px 0 0; }
.band-row .btn { flex-shrink: 0; }

/* R2f · centred at desktop. `justify-content: space-between` on two children
   of very different mass — a 567px text block and a 148px pill — pushed them
   to opposite ends of a 1180px row and left 369px of nothing in the middle,
   with the button marooned against the right edge. There is no third element
   to fill that gap and inventing one would be filler, so the row stops
   pretending to be two columns: this is a call to action, and a call to
   action centres.

   Desktop only. Below 1024 the md block already stacks this left-aligned,
   which is right for a narrow column — centred text gets hard to read past
   a couple of lines, and on a phone this body is six.

   margin-left/right, NOT the `margin-inline` shorthand next to an existing
   `margin: 14px 0 0` longhand set: `.band-row p` sets all four sides, so a
   shorthand here would silently reset the 14px top offset. */
@media (min-width: 1024px) {
  .band-row {
    flex-direction: column;
    align-items: center;
    text-align: center;
    gap: 34px;
  }
  .band-row h2,
  .band-row p { margin-left: auto; margin-right: auto; }
}

/* V8 · Ticket panel */
/* R1 §5 · restyled. It read as an empty ticket: a shape built to hold a
   number, holding the phrase "Growing daily". And its bottom line, "Your
   share of the payout: $0", scanned at a glance as *you get nothing* on
   the one section selling the jackpot as a benefit. Purple ground, paper
   dashes and a green figure make it read as money rather than a warning.
   The dashed border and 18px radius stay — that is the ticket metaphor. */
.ticket {
  background: var(--purple); color: var(--paper);
  border-radius: var(--r-ticket); padding: 32px;
  border: 2px dashed var(--paper); text-align: center;
}
.ticket .lbl { font-family: var(--mono); font-size: 11.5px; letter-spacing: .14em; text-transform: uppercase; color: var(--paper-muted); }
.ticket .amt {
  font-family: var(--display); font-weight: 800;
  font-size: clamp(42px, 6vw, 62px); line-height: 1;
  margin: 12px 0 6px; color: var(--green-br);
}
.ticket .sub { font-size: 14.5px; color: var(--paper-muted); margin: 0; }
.ticket hr { border: 0; border-top: 1px dashed rgba(253, 246, 232, .38); margin: 20px 0; }
.ticket .cost { font-family: var(--mono); font-size: 13px; color: var(--green-br); }

/* V9 · Value card grid */
.vals { display: grid; grid-template-columns: repeat(2, 1fr); gap: clamp(18px, 2.6vw, 30px); list-style: none; margin: 0; padding: 0; }
.val { background: rgba(253, 246, 232, .045); border: 1px solid var(--line); border-radius: var(--r-card); padding: 28px; }
.val h3 { color: var(--green-br); margin-bottom: 9px; }
.val p { margin: 0; color: var(--paper-dim); font-size: 16px; }

/* V10 · FAQ disclosure list. Native details/summary: works with script
   off, keyboard operable for free, and the platform emits the FAQPage
   JSON-LD from the faq-role group. Never replace with a scripted
   accordion. */
/* R2 · the list spans the wrap, so its left AND right edges line up with
   the two-column head above it. At 790px under a 1084px head it ended
   294px short of the head's right edge, which is what read as misaligned
   — the list looked like it belonged to a different grid. The answers
   still cap at 64ch below, so widening the rows costs no legibility; it
   only makes the whole row a target and squares the section off. */
.faq { max-width: none; }
.faq details { border-bottom: 1px solid var(--line); }
.faq summary {
  cursor: pointer; list-style: none;
  padding: 22px 44px 22px 0; position: relative;
  font-family: var(--display); font-weight: 700; font-size: 19px; letter-spacing: -.01em;
  line-height: 1.3;
}
.faq summary::-webkit-details-marker { display: none; }
.faq summary::after {
  content: "+"; position: absolute; right: 8px; top: 50%;
  translate: 0 -50%;
  font-family: var(--mono); font-size: 24px; line-height: 1;
  color: var(--purple-lt); transition: rotate .22s;
}
.faq details[open] summary::after { rotate: 45deg; }
.faq details p { margin: 0 0 24px; color: var(--paper-dim); font-size: 16.5px; max-width: 64ch; }

/* V15 · Prose column (/privacy).
   Derived, not comp-provided — there is no long-form page in either
   comp. It introduces NO new visual language: the eyebrow, heading
   scale, body colour and measure are all existing tokens, composed the
   way the FAQ answers already compose them. */
.prose { max-width: 720px; }
.prose .updated {
  font-family: var(--mono);
  font-size: 12px;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--paper-muted);
  margin: 18px 0 0;
}
.prose-lede p {
  font-size: clamp(17px, 1.9vw, 19px);
  color: var(--paper-dim);
  margin: 22px 0 0;
  max-width: 62ch;
}
.prose section { margin-top: clamp(40px, 5vw, 56px); }
.prose h2 { margin-bottom: 14px; }
.prose p { color: var(--paper-dim); font-size: 16.5px; margin: 0 0 16px; max-width: 64ch; }
.prose p:last-child { margin-bottom: 0; }

/* V12 · Store buttons and QR panel (/app).
   These are the page's primary action, so they get the most generous
   targets on the site. A store button whose URL slot is empty renders as
   a non-interactive <span> with the same face — never an <a href="">,
   which would silently link to the current page (§6). */
.stores { display: flex; flex-wrap: wrap; gap: 14px; margin-bottom: 26px; }
.store {
  display: flex; align-items: center; gap: 13px;
  text-decoration: none;
  background: var(--paper); color: var(--store-ink);
  padding: 13px 24px 13px 20px;
  border-radius: var(--r-store);
  min-height: 52px;
  transition: transform .16s, box-shadow .16s;
}
@media (hover: hover) and (pointer: fine) {
  .store:hover { transform: translateY(-2px); box-shadow: 0 14px 30px -14px rgba(0, 0, 0, .7); }
}
.store:active { transform: translateY(1px); }
/* The unlinked state: same face, no affordance, no pointer. */
.store-off { cursor: default; opacity: .82; }
.store img { width: 26px; height: 26px; flex-shrink: 0; }
.store span { display: block; line-height: 1.2; }
.store .s1 {
  font-family: var(--mono); font-size: 10.5px; letter-spacing: .08em;
  text-transform: uppercase; opacity: .66;
}
.store .s2 { font-family: var(--display); font-weight: 700; font-size: 18px; letter-spacing: -.01em; }
.freebar { font-family: var(--mono); font-size: 13px; color: var(--paper-muted); margin: 0; }

.qr-panel {
  background: linear-gradient(180deg, var(--surface), var(--ink-2));
  border: 1px solid rgba(169, 145, 240, .32);
  border-radius: var(--r-panel);
  padding: 28px; text-align: center;
  box-shadow: 0 34px 70px -28px rgba(0, 0, 0, .85);
}
.qr-panel .lbl {
  font-family: var(--mono); font-size: 11px; letter-spacing: .14em;
  text-transform: uppercase; color: var(--amber);
  display: block; margin-bottom: 18px;
}
.qr-box { background: var(--paper); border-radius: var(--r-store); padding: 14px; width: 210px; margin: 0 auto; }
/* The platform emits the <picture>; the theme only sizes it. A stretched
   QR does not scan, so the aspect ratio is pinned square. */
.qr-box img, .qr-box picture { width: 100%; height: auto; aspect-ratio: 1 / 1; display: block; }
.qr-panel p { font-size: 14.5px; color: var(--paper-muted); margin: 18px 0 0; }

/* V11 · Split CTA panels */
.split { display: grid; grid-template-columns: 1fr 1fr; gap: clamp(18px, 2.6vw, 28px); }
.panel { border-radius: var(--r-split); padding: clamp(28px, 4vw, 44px); }
.panel h3 { font-family: var(--display); font-size: clamp(24px, 3vw, 32px); font-weight: 800; margin-bottom: 12px; line-height: 1.1; }
.panel p { font-size: 16.5px; margin: 0 0 26px; }
.p-venue { background: linear-gradient(150deg, var(--purple), var(--surface)); border: 1px solid rgba(169, 145, 240, .34); }
.p-venue p { color: rgba(253, 246, 232, .82); }
/* --green-panel, not --green: see the token block. White here is 5.04:1
   and --paper is 4.69:1; on the comps' --green they were 3.35:1 and
   3.1:1, i.e. a fail. */
.p-play { background: var(--green-panel); border: 1px solid rgba(107, 212, 95, .5); }
.p-play h3 { color: #fff; }
.p-play p { color: var(--paper); }

/* ==========================================================================
   Breakpoint: md — 768px to 1023px
   Heroes stack, steps go 1-up, the inline links collapse into the
   disclosure, both header CTAs stay in the bar.
   ========================================================================== */

@media (max-width: 1023px) {
  /* R2c-b · THE MEASURE IS RELEASED FOR THE WHOLE RANGE BELOW THE SPLIT,
     not just for phones. This declaration used to live in the sm block
     (max-width: 767px) while the two-column head needs min-width: 1024px,
     so the band from 768 to 1023 got neither: `.sec-head-main` kept its
     base `max-width: 34ch`, which resolves against the 17px body font on
     the wrapper and pinned headings to about 360px. Measured at 900px
     that was FOURTEEN characters per line, and the why-venues heading ran
     to four lines. Same unit bug R2 fixed at desktop, surviving in the
     gap between two breakpoints that did not meet.

     The rule is: below 1024 the container is the measure, capped on the
     H2 in the h2's own units — never on the wrapper. Without the cap,
     /app's heads hit 41 characters per line at 900px, wider than the
     20-35 display range and wider than the same heading at 1440. At 390
     the container is narrower than 30ch, so the cap is a no-op there and
     the phone layout is untouched. */
  .sec-head-main { max-width: none; }
  .sec-head h2 { max-width: 30ch; }

  :root { --h3: 19px; }

  .nav-main { display: none; }
  .nav-menu { display: block; }
  /* Reserve the summary's column on the right of the bar row. */
  .nav-in {
    padding-right: calc(var(--pad) + env(safe-area-inset-right) + var(--toggle-w) + 10px);
  }
  .nav-cta { margin-left: auto; }
  /* At md both CTAs are still in the bar, so the panel's copy of the
     secondary CTA would be a duplicate control. */
  .nav-panel-cta { display: none; }

  /* Tablet is a touch device, so the footer link rows take the same
     44px floor mobile gets — not just below 768px. */
  .foot-nav ul { gap: 0; }
  .foot-nav a { display: flex; align-items: center; min-height: 44px; }

  /* V2/V7 · heroes and the paper band stack to one column. The signature
     panel is capped and centred so it does not stretch into a letterbox. */
  .hero-grid, .band-grid { grid-template-columns: 1fr; }
  .hero-panel { max-width: 420px; margin-inline: auto; width: 100%; }

  /* V3 · the rotation goes: at this width it reads as a mistake rather
     than a flourish, and it is the main horizontal-overflow risk. */
  .card { transform: none; margin-top: 12px; }

  /* V6 · three columns of long prose would break the measure. */
  .steps { grid-template-columns: 1fr; gap: 30px; }

  /* V4 · 2x2, not a snap rail: four items of two or three words read
     better as a block than as a scroller that hides half of them. */
  .facts-in { grid-template-columns: 1fr 1fr; }

  /* V8 */
  .ticket { max-width: 460px; margin-inline: auto; width: 100%; }

  /* V11 · venue panel first. */
  .split { grid-template-columns: 1fr; }

  /* V12 · QR panel below the text, capped and centred. */
  .qr-panel { max-width: 340px; margin-inline: auto; width: 100%; }

  /* V7 (app variant) · the notice's button moves below the text,
     left-aligned rather than stretched. */
  .band-row { flex-direction: column; align-items: flex-start; }

  /* R1 §3 · the head has stacked, so the image sits below it. Capped and
     left-aligned rather than stretched across the full tablet width. */
  .sec-head-figure { max-width: 480px; }
}

/* ==========================================================================
   Breakpoint: sm — 767px and below
   Single column everywhere, header reduces to logo + disclosure, the
   sticky bottom action bar appears.
   ========================================================================== */

@media (max-width: 767px) {
  :root { --header-h: 60px; }

  .nav-cta { display: none; }
  .nav-panel-cta { display: flex; }

  .brand img { height: 30px; }

  /* The action bar. 64px plus the home-indicator inset, a hairline and a
     blur so content scrolling under it stays legible. */
  .actionbar {
    display: flex;
    gap: 8px;
    position: fixed;
    left: 0; right: 0; bottom: 0;
    z-index: 60;
    padding: 8px calc(var(--pad) + env(safe-area-inset-left))
             calc(8px + env(safe-area-inset-bottom))
             calc(var(--pad) + env(safe-area-inset-right));
    min-height: calc(var(--bar-h) + env(safe-area-inset-bottom));
    background: rgba(27, 16, 51, .94);
    backdrop-filter: blur(12px);
    -webkit-backdrop-filter: blur(12px);
    border-top: 1px solid var(--line);
  }
  .actionbar .btn {
    flex: 1 1 0;
    min-height: 48px;
    padding: 12px 14px;
    font-size: 15px;
  }

  /* The bar is fixed, so it overlays whatever is last in the document —
     and that is the FOOTER, not main. Padding main (as §2.7 words it)
     would leave the legal strip underneath, which is the exact failure
     §2.7 asks to check for. The clearance goes on the last element. */
  .foot { padding-bottom: calc(40px + var(--bar-h) + env(safe-area-inset-bottom)); }

  .foot-in { flex-direction: column; gap: 30px; }
  .foot-nav { flex-direction: column; gap: 26px; }
  .foot-nav ul { gap: 0; }
  .foot-nav a {
    display: flex;
    align-items: center;
    min-height: 44px;
  }
  /* The legal strip stacks rather than wrapping, so a licence number
     never fragments across a line break. */
  .legal { flex-direction: column; gap: 8px; }

  /* ---- page patterns at sm ---- */

  /* V5 · the intro is capped tighter so lines do not run long on a narrow
     column. The heading measure is released in the md block above, not
     here — see the note there. */
  .sec-head { margin-bottom: clamp(28px, 6vw, 34px); }
  .sec-head p { max-width: 38ch; font-size: 16.5px; }

  /* R1 §3 · full width on a phone. */
  .sec-head-figure { margin-top: 18px; }

  /* V2 · action row buttons go full width and stack. */
  .hero-cta { gap: 10px; }
  .hero-cta .btn { width: 100%; }
  .hero-panel { max-width: 340px; }
  /* The washes are scaled back so they do not flatten the text contrast
     on a small screen. */
  .hero::before { inset: -20% 20% 45% -30%; }
  .hero::after { width: 80%; height: 50%; right: -25%; top: -12%; }

  /* V3 · rows stay >= 44px so six of them still feel like a list, not a
     stack of slivers. Rows are content: none of them are dropped. */
  .card { padding: 18px; }

  /* V4 · stays 2x2; figure and caption step down a size. */
  .fact { padding: 20px 12px; }
  .fact b { font-size: 22px; }
  .fact span { font-size: 11px; letter-spacing: .07em; }

  /* V8 */
  .ticket { padding: 24px; }

  /* V9 */
  .vals { grid-template-columns: 1fr; gap: 14px; }
  .val { padding: 22px; }

  /* V10 · the whole row is the tap target, not just the words. */
  .faq summary { padding: 20px 44px 20px 0; font-size: 17.5px; min-height: 44px; }
  .faq details p { font-size: 16px; }

  /* V11 */
  .panel { padding: 24px; }
  .panel .btn { width: 100%; }

  /* V12 · the store buttons are this page's primary action, so on a
     phone they stack full width with the most generous targets on the
     site. The QR box scales to 180px, which is what `sizes` promises. */
  .stores { flex-direction: column; gap: 10px; }
  .store { width: 100%; min-height: 56px; }
  .qr-panel { max-width: 300px; padding: 22px; }
  .qr-box { width: 180px; }
  .qr-panel p { font-size: 13.5px; }

  /* V7 (app variant) */
  .band-row .btn { width: 100%; }

  /* V15 · full width within the gutters, spacing at the clamp floor. */
  .prose { max-width: none; }
  .prose section { margin-top: clamp(34px, 8vw, 40px); }
}

/* ==========================================================================
   10. Reduced motion — last, so it wins over everything above.
   Nothing on this site conveys information through motion alone, so
   switching all of it off loses nothing.
   ========================================================================== */

@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation: none !important;
    transition: none !important;
  }
  html { scroll-behavior: auto; }
  .btn:active { transform: none; }
}
