/** Shopify CDN: Minification failed

Line 6117:13 Expected identifier but found whitespace
Line 6117:14 Unexpected "repeating-linear-gradient("

**/
/* ============================================================
   Self-hosted fonts
   ------------------------------------------------------------
   These were on fonts.googleapis.com. That cost two serial round
   trips on two third-party origins before a single glyph file was
   even requested: the browser had to fetch Google's stylesheet,
   parse it, and only then learn the woff2 URLs on fonts.gstatic.com.
   On a phone at ~150ms RTT that is several hundred milliseconds of
   fallback text; on desktop wifi it hides in the noise, which is
   exactly why the phone felt so much slower than the desktop.

   Served from the theme's own assets folder now, so they ride the
   cdn.shopify.com connection that theme.css itself arrived on --
   already open, already warm. The URLs are relative on purpose:
   theme.css and the woff2 files sit in the same directory, so this
   resolves without needing Liquid (a .css asset is served static).

   Same five latin files Google was serving, byte for byte, so
   nothing about the type changes. Inter is declared twice against
   one file exactly as Google's own stylesheet does.
   ============================================================ */
@font-face {
  font-family: 'Archivo Black'; font-style: normal; font-weight: 400;
  font-display: swap; src: url('archivo-black-400.woff2') format('woff2');
}
@font-face {
  font-family: 'Bebas Neue'; font-style: normal; font-weight: 400;
  font-display: swap; src: url('bebas-neue-400.woff2') format('woff2');
}
@font-face {
  font-family: 'Inter'; font-style: normal; font-weight: 400;
  font-display: swap; src: url('inter-var.woff2') format('woff2');
}
@font-face {
  font-family: 'Inter'; font-style: normal; font-weight: 600;
  font-display: swap; src: url('inter-var.woff2') format('woff2');
}
@font-face {
  font-family: 'Space Mono'; font-style: normal; font-weight: 400;
  font-display: swap; src: url('space-mono-400.woff2') format('woff2');
}
@font-face {
  font-family: 'Space Mono'; font-style: normal; font-weight: 700;
  font-display: swap; src: url('space-mono-700.woff2') format('woff2');
}

/* ============================================================
   ORELSAN TOUR 2026 — Street Press Hip Hop Theme
   ============================================================ */

/* --- Design Tokens --- */
:root {
  --bg: #0A0A0A;
  --bg-soft: #0F0F0F;
  --bg-raised: #111111;
  --bg-input: #141414;
  --border: #262626;          /* hairlines and dividers */
  --border-card: #606060;     /* component boundaries: 3.15:1 on --bg, 3.00:1 on --bg-raised */
  --border-strong: #FFFFFF;
  --text-primary: #FFFFFF;
  --text-secondary: #E2E2E2;
  --text-muted: #9A9A9A;
  --accent: #E8B923;
  --accent-deep: #C99A12;
  --accent-soft: #2E2410;
  /* The upper tier's own colour. Deep teal rather than another green: three
     candidates were put on the live map side by side, and a darker green read
     as the accent dimmed -- too close to the map's own dimming to trust --
     while a steel blue separated best but brought a hue this site does not
     otherwise use. Teal is unmistakably a different section and still sits in
     the green family. White labels clear 4.9:1 on it, better than on --accent. */
  --tier-upper: #1F6F8B;
  /* The second seated tier: a mint that is clearly lighter than the upper
     ring and clearly cooler than the gold, so the three seated bands read
     apart at a glance. A darker sea green was tried first and sat too close
     to --tier-upper once the two were side by side on the plan. */
  --tier-mid: #46B5D1;
  /* Sold out reads as absent rather than as another price band. */
  --tier-sold: #3A3A3A;
  --danger: #FF1E56;
  --danger-soft: #2E0D16;   /* the danger equivalent of --accent-soft */
  --shadow: 4px 4px 0px #E8B923;
  --shadow-hover: 6px 6px 0px #E8B923;
  --radius: 0px;
  --font-display: 'Bebas Neue', sans-serif;
  --font-body: 'Inter', sans-serif;
  --font-mono: 'Space Mono', monospace;
  --max-width: 1280px;
  --section-pad: 80px;
}

/* --- Reset --- */
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
html { scroll-behavior: smooth; }
/* Anchor targets sit below the sticky header (79px desktop / 71px mobile),
   otherwise every #link lands with its heading hidden underneath it. */
section[id] { scroll-margin-top: 96px; }
body {
  font-family: var(--font-body);
  background: var(--bg);
  color: var(--text-primary);
  font-size: 17px;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  color-scheme: dark;
}
img { max-width: 100%; display: block; }
a { color: inherit; text-decoration: none; }
button { cursor: pointer; font-family: inherit; border: none; background: none; }
ul { list-style: none; }

/* --- Typography --- */
h1, h2, h3, h4, h5 {
  font-family: var(--font-display);
  line-height: 1;
  letter-spacing: 0.02em;
}
.label-mono {
  font-family: var(--font-mono);
  font-size: 12px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.12em;
}

/* --- Layout --- */
.container {
  max-width: var(--max-width);
  margin: 0 auto;
  padding: 0 24px;
}
.section { padding: var(--section-pad) 0; }
.section--soft { background: var(--bg-soft); }
.section--white { background: var(--bg-raised); }
.section--dark {
  background: var(--border-strong);
  color: #FFFFFF;
}

/* --- Buttons --- */
.btn {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 14px 28px;
  font-family: var(--font-display);
  font-size: 20px;
  letter-spacing: 0.04em;
  border-radius: var(--radius);
  border: 2px solid var(--accent);
  transition: all 0.15s ease;
  cursor: pointer;
  white-space: nowrap;
}
.btn--primary {
  background: var(--accent);
  color: #000000;
}
.btn--primary:hover {
  background: var(--accent-deep);
  border-color: var(--accent-deep);
}
.btn--secondary {
  background: transparent;
  color: var(--text-primary);
  border-color: var(--border-strong);
}
.btn--secondary:hover {
  background: var(--border-strong);
  color: #000000;
}
.btn--dark {
  background: #000000;
  color: var(--accent);
  border-color: #000000;
}
.btn--dark:hover {
  background: var(--bg-raised);
}
.btn--full { width: 100%; justify-content: center; }
.btn:disabled, .btn[disabled] {
  opacity: 0.5;
  cursor: not-allowed;
  box-shadow: none;
  transform: none;
}

/* --- Cards --- */
.card {
  background: var(--bg-raised);
  border: 2px solid var(--border-card);
  border-radius: var(--radius);
  transition: border-color 0.15s ease;
}
.card:hover { border-color: var(--accent); }
.card--flat:hover { border-color: var(--border-card); }

/* --- Badges / Tags --- */
.badge {
  display: inline-block;
  padding: 3px 10px;
  background: var(--accent);
  color: #000000;
  font-family: var(--font-mono);
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  border-radius: 0;
}
.badge--gold {
  background: var(--accent);
  color: #000000;
}
.badge--danger {
  background: var(--danger);
  color: #FFFFFF;
}
.badge--float {
  position: absolute;
  top: 16px;
  left: 16px;
  z-index: 2;
}

/* --- Inputs --- */
.input {
  width: 100%;
  padding: 12px 16px;
  background: var(--bg-input);
  border: 2px solid var(--border-card);
  border-radius: var(--radius);
  font-family: var(--font-body);
  font-size: 16px;
  color: var(--text-primary);
  transition: border-color 0.2s ease;
  outline: none;
}
.input:focus { border-color: var(--border-strong); }
.input::placeholder { color: var(--text-muted); }

/* --- Divider --- */
.rule { border: none; border-top: 1px solid var(--border-strong); margin: 0; }
.rule--soft { border-top-color: var(--border); }

/* --- Reveal on scroll ---
   An animation, not a transition, and deliberately so. Most of the elements
   carrying .reveal are components with a `transition` of their own --
   .ticket-card, .faq-item, .info-card, .tour-row, .hiw-card -- and each of
   those rules sits later in this file at equal specificity, so it replaced
   the reveal's transition list outright. Measured on the live page before
   this change: tour rows transitioned `transform, background-color` and
   how-it-works cards `background-color` alone, so both arrived with no
   entrance at all; ticket, FAQ and info cards got `opacity` at 0.22s and
   snapped into position because `translate` and `scale` were not in their
   list. `animation` is a separate property from `transition` and cannot be
   overwritten by one, so every reveal now runs the same way.

   It rides `translate`/`scale` rather than `transform`, which the hover
   lifts own -- as separate properties the entrance and the hover compose
   instead of cancelling each other.

   `.reveal.visible` states the resting values itself instead of leaning on
   `animation-fill-mode: forwards`, so an element whose `animation` is later
   replaced by another rule -- .ticket-card.is-flash, when the seat map sends
   you to a card -- stays visible rather than dropping back to opacity 0.
   `backwards` covers the stagger delay, before the animation starts. */
.js-reveal .reveal { opacity: 0; }
.reveal.visible {
  opacity: 1;
  animation: reveal-in var(--reveal-dur) var(--ease-glide) backwards;
}
@keyframes reveal-in {
  from { opacity: 0; translate: 0 30px; scale: 0.98; }
  to   { opacity: 1; translate: none;   scale: none; }
}

/* --- Hero entrance ---
   The hero used to carry .reveal, which meant the first thing an arriving
   visitor saw was an empty black page: .reveal starts at opacity 0 and only
   lifts once the deferred theme.js has parsed and an IntersectionObserver
   has fired, then spends --reveal-dur (0.95s) fading in. Measured on the
   live site, that left the artist name, the poster and the CTA invisible for
   over a second on arrival -- on mobile data, longer. Ad traffic lands here.

   This is a plain CSS animation on the elements themselves, so it begins at
   first paint with no JavaScript involved at all, and it is less than half
   as long. Nothing above the fold can now be delayed by, or lost to, a
   script that is slow or never arrives. */
.hero-img-wrap {
  animation: hero-in 0.4s var(--ease-glide) 0.06s both;
}
@keyframes hero-in {
  from { opacity: 0; translate: 0 16px; }
  to   { opacity: 1; translate: none; }
}

/* --- Watermark --- */
.watermark {
  position: absolute;
  font-family: var(--font-display);
  font-size: clamp(120px, 20vw, 280px);
  color: var(--border-strong);
  opacity: 0.05;
  line-height: 1;
  pointer-events: none;
  user-select: none;
  white-space: nowrap;
}

/* ============================================================
   ANNOUNCEMENT BAR
   ============================================================ */
.announcement-bar {
  background: var(--accent);
  padding: 10px 0;
  overflow: hidden;
  border-bottom: 2px solid var(--accent-deep);
}
.announcement-bar__text {
  color: #000000;
  font-family: var(--font-mono);
  font-size: 12px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.14em;
  text-align: center;
}

/* ============================================================
   HEADER
   ============================================================ */
.site-header {
  position: sticky;
  top: 0;
  z-index: 100;
  background: var(--bg);
  border-bottom: 2px solid var(--border);
}
.site-header__inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 16px 24px;
  max-width: var(--max-width);
  margin: 0 auto;
}
.site-header__logo {
  font-family: var(--font-display);
  font-size: 28px;
  letter-spacing: 0.06em;
  color: var(--text-primary);
}
.site-header__logo:hover { color: var(--accent); }
.site-header__nav { display: flex; align-items: center; gap: 32px; }
.site-header__nav a {
  font-family: var(--font-mono);
  font-weight: 700;
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.15em;
  color: var(--text-muted);
  transition: color 0.15s ease;
}
.site-header__nav a:hover { color: var(--accent); }
.site-header__right { display: flex; align-items: center; gap: 16px; }
.cart-btn {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px 16px;
  background: transparent;
  color: var(--text-primary);
  font-family: var(--font-mono);
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  border: 2px solid var(--border-card);
  transition: all 0.15s ease;
  text-decoration: none;
}
.cart-btn:hover { border-color: var(--accent); color: var(--accent); }
.cart-btn__count {
  background: var(--accent);
  color: #000;
  width: 20px; height: 20px;
  border-radius: 50%;
  font-family: var(--font-mono);
  font-size: 11px;
  display: flex; align-items: center; justify-content: center;
}
/* Fires when the cart count changes, so adding a ticket is visible without
   opening anything. Short and snappy, in keeping with the rest of the motion. */
@keyframes cart-bump {
  0%   { transform: scale(1); }
  35%  { transform: scale(1.28); }
  100% { transform: scale(1); }
}
.cart-btn__count.bump { animation: cart-bump 0.35s ease; }
.cart-btn.flash { border-color: var(--accent); color: var(--accent); }

@media (prefers-reduced-motion: reduce) {
  .cart-btn__count.bump { animation: none; }
}

.menu-btn {
  display: none;
  flex-direction: column;
  gap: 5px;
  padding: 8px;
  cursor: pointer;
}
.menu-btn span {
  display: block;
  width: 24px; height: 2px;
  background: var(--text-primary);
  transition: all 0.15s ease;
}

/* Mobile nav overlay */
.nav-overlay {
  display: none;
  position: fixed;
  inset: 0;
  background: var(--bg);
  z-index: 200;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 20px;
  border: 2px solid var(--border-card);
}
.nav-overlay a {
  font-family: var(--font-display);
  font-size: 30px;
  color: var(--text-primary);
  letter-spacing: 0.04em;
  transition: color 0.15s ease;
}

/* The cart button is an <a> too, so it would otherwise inherit the display
   size above and fill half the screen. */
.nav-overlay a.btn {
  font-size: 15px;
  padding: 12px 24px;
  margin-top: 12px;
}
.nav-overlay a:hover { color: var(--accent); }
.nav-overlay__close {
  position: absolute;
  top: 18px; right: 18px;
  font-size: 26px;
  cursor: pointer;
  background: none; border: none;
  color: var(--text-primary);
  font-family: var(--font-display);
}

/* ============================================================
   HERO
   ============================================================ */
.hero {
  position: relative;
  overflow: hidden;
  background: var(--bg);
  min-height: 90vh;
  display: flex;
  align-items: center;
  border-bottom: 2px solid var(--border);
}
.hero__bg-texture {
  position: absolute;
  inset: 0;
  background-image: repeating-linear-gradient(
    45deg,
    #E8B923 0, #E8B923 1px,
    transparent 0, transparent 8px
  );
  opacity: 0.03;
  pointer-events: none;
}
.hero__inner {
  position: relative;
  z-index: 1;
  width: 100%;
  max-width: var(--max-width);
  margin: 0 auto;
  padding: 80px 24px;
  display: grid;
  grid-template-columns: 3fr 2fr;
  gap: 48px;
  align-items: center;
}
.hero__content { position: relative; }
/* The glow belongs to the poster artwork, not to this heading -- it is baked
   into assets/orelsan-poster.jpg around the wordmark. `.hero-name` and
   `.hero-tour` stay as plain classes so there is something to hang a change
   on if that ever moves back into CSS. */

.hero__meta {
  font-family: var(--font-mono);
  font-size: 12px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.14em;
  color: var(--text-muted);
  margin-bottom: 16px;
}
.hero__artist {
  font-family: var(--font-display);
  font-size: clamp(80px, 12vw, 140px);
  line-height: 0.9;
  color: var(--text-primary);
  margin-bottom: 12px;
}
.hero__tour {
  font-family: var(--font-display);
  font-size: clamp(28px, 4vw, 48px);
  color: var(--accent);
  margin-bottom: 32px;
}
.hero__actions {
  display: flex;
  gap: 16px;
  flex-wrap: wrap;
  margin-top: 40px;
}
.hero__image-wrap {
  position: relative;
  aspect-ratio: 1;
  border: 2px solid var(--border-card);
  border-radius: var(--radius);
  overflow: hidden;
  background: var(--bg-raised);
}
.hero__image-wrap img {
  width: 100%; height: 100%;
  object-fit: cover;
}
.hero__image-placeholder {
  width: 100%; height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--bg-raised);
}
.hero__image-placeholder span {
  font-family: var(--font-display);
  font-size: 64px;
  color: var(--border);
}
.hero__live-badge {
  position: absolute;
  top: 16px;
  left: 16px;
  background: var(--accent);
  color: #000;
  font-family: var(--font-mono);
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  padding: 4px 10px;
  border: 1px solid var(--accent-deep);
}
.hero__selling-badge {
  position: absolute;
  bottom: 16px;
  right: 16px;
  background: transparent;
  color: var(--accent);
  font-family: var(--font-mono);
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  padding: 4px 10px;
  border: 1px solid var(--accent);
}
.hero__meta-row {
  display: flex;
  gap: 32px;
  flex-wrap: wrap;
  border-top: 1px solid var(--border);
  border-bottom: 1px solid var(--border);
  padding: 16px 0;
  margin: 24px 0;
}
.hero__meta-item .hero__meta-label {
  font-family: var(--font-mono);
  font-size: 10px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.15em;
  color: var(--text-muted);
  margin-bottom: 4px;
}
.hero__meta-item .hero__meta-value {
  font-size: 16px;
  font-weight: 600;
  color: var(--text-primary);
}
.hero__trust {
  display: flex;
  gap: 24px;
  flex-wrap: wrap;
  margin-top: 16px;
}
.hero__trust span {
  font-family: var(--font-mono);
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--text-muted);
}
.hero__trust span::before { content: '+ '; color: var(--accent); }

/* ============================================================
   COUNTDOWN
   ============================================================ */
.countdown-section {
  background: var(--bg-soft);
  padding: 60px 0;
  border-top: 2px solid var(--border);
  border-bottom: 2px solid var(--border);
}
.countdown-section__inner {
  max-width: var(--max-width);
  margin: 0 auto;
  padding: 0 24px;
  text-align: center;
}
.countdown-section__label {
  font-family: var(--font-mono);
  font-size: 12px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.14em;
  color: var(--text-muted);
  margin-bottom: 24px;
}
.countdown {
  display: flex;
  justify-content: center;
  gap: 16px;
  flex-wrap: wrap;
}
.countdown__unit {
  background: var(--bg-raised);
  border: 2px solid var(--border-card);
  border-radius: var(--radius);
  padding: 20px 28px;
  min-width: 100px;
  text-align: center;
}
.countdown__number {
  font-family: var(--font-display);
  font-size: 64px;
  line-height: 1;
  color: var(--accent);
  display: block;
}
.countdown__label {
  font-family: var(--font-mono);
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  color: var(--text-muted);
  margin-top: 4px;
  display: block;
}
.countdown--expired {
  font-family: var(--font-display);
  font-size: 48px;
  color: var(--accent);
}

/* ============================================================
   TICKET TIERS
   ============================================================ */
.ticket-tiers { background: var(--bg); }
.ticket-tiers__header {
  text-align: center;
  margin-bottom: 48px;
}
.ticket-tiers__title {
  font-family: var(--font-display);
  font-size: clamp(40px, 6vw, 64px);
  color: var(--text-primary);
}
.ticket-tiers__grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 2px;
  background: var(--border);
}
.ticket-card__top {
  background: #000000;
  padding: 14px 24px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  border-bottom: 2px solid var(--border);
}
.ticket-card__tier {
  font-family: var(--font-mono);
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.2em;
  color: var(--text-muted);
}
.ticket-card__body { padding: 24px; flex: 1; display: flex; flex-direction: column; gap: 16px; }
.ticket-card__price {
  font-family: var(--font-display);
  font-size: 64px;
  line-height: 1;
  color: var(--accent);
}
.ticket-card__price-sub {
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--text-muted);
  letter-spacing: 0.08em;
  margin-top: 4px;
}
.ticket-card__perks {
  display: flex;
  flex-direction: column;
  gap: 8px;
  flex: 1;
}
.ticket-card__perk {
  font-family: var(--font-body);
  font-size: 15px;
  color: var(--text-secondary);
  display: flex;
  gap: 10px;
}
.ticket-card__perk::before {
  content: '✦';
  color: var(--accent);
  flex-shrink: 0;
}
.ticket-card__footer { padding: 0 24px 24px; }

/* ============================================================
   EVENT INFO
   ============================================================ */
.event-info { background: var(--bg-soft); }
.event-info__title {
  font-family: var(--font-display);
  font-size: clamp(40px, 6vw, 64px);
  margin-bottom: 40px;
}
.event-info__grid {
  display: grid;
  grid-template-columns: 2fr 1fr;
  gap: 2px;
  background: var(--border);
}
.info-card__item { margin-bottom: 24px; }
.info-card__item:last-child { margin-bottom: 0; }
.info-card__label {
  font-family: var(--font-mono);
  font-size: 10px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.15em;
  color: var(--text-muted);
  margin-bottom: 4px;
}
.info-card__value {
  font-family: var(--font-display);
  font-size: 36px;
  color: var(--text-primary);
  line-height: 1.1;
}

/* ============================================================
   SUPPORT ACTS
   ============================================================ */
.support-acts { background: var(--bg); }
.support-acts__title {
  font-family: var(--font-display);
  font-size: clamp(40px, 6vw, 64px);
  margin-bottom: 40px;
}
.support-acts__grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  gap: 2px;
  background: var(--border);
}
.act-card {
  background: var(--bg-raised);
  border: 2px solid var(--border-card);
  border-top: 4px solid var(--accent);
  border-radius: var(--radius);
  padding: 28px;
  transition: background 0.15s ease;
}
.act-card:hover { background: #141414; }
.act-card__slot {
  font-family: var(--font-mono);
  font-size: 10px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.15em;
  color: var(--text-muted);
  margin-bottom: 8px;
}
.act-card__name {
  font-family: var(--font-display);
  font-size: 48px;
  color: var(--text-primary);
  margin-bottom: 8px;
}
.act-card__bio {
  font-family: var(--font-body);
  font-size: 14px;
  color: var(--text-muted);
}

/* ============================================================
   TOUR DATES
   ============================================================ */
.tour-dates { background: var(--bg-soft); }
.tour-dates__title {
  font-family: var(--font-display);
  font-size: clamp(40px, 6vw, 64px);
  margin-bottom: 40px;
}
.tour-dates__list { display: flex; flex-direction: column; }
.tour-date-row {
  display: flex;
  align-items: center;
  gap: 24px;
  padding: 20px 0;
  border-bottom: 1px solid var(--border);
  transition: background 0.15s ease;
}
.tour-date-row:first-child { border-top: 1px solid var(--border); }
.tour-date-row__date {
  font-family: var(--font-display);
  font-size: 40px;
  min-width: 160px;
  color: var(--accent);
}
.tour-date-row__info { flex: 1; }
.tour-date-row__city {
  font-family: var(--font-body);
  font-weight: 600;
  font-size: 18px;
  color: var(--text-primary);
}
.tour-date-row__venue {
  font-family: var(--font-body);
  font-size: 14px;
  color: var(--text-muted);
}
.tour-date-row__status { margin-left: auto; }
.tour-date-row__link {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 8px 16px;
  background: transparent;
  color: var(--accent);
  font-family: var(--font-mono);
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  border: 2px solid var(--accent);
  transition: all 0.15s ease;
  text-decoration: none;
}
.tour-date-row__link:hover { background: var(--accent); color: #000; }

/* ============================================================
   HOW IT WORKS
   ============================================================ */
.how-it-works { background: var(--bg); }
.how-it-works__title {
  font-family: var(--font-display);
  font-size: clamp(40px, 6vw, 64px);
  margin-bottom: 48px;
  text-align: center;
}
.how-it-works__steps-wrapper {
  display: grid;
  grid-template-columns: 1fr auto 1fr auto 1fr;
  align-items: stretch;
  gap: 0;
  background: var(--border);
}
.step-card {
  background: var(--bg-raised);
  border: 2px solid var(--border-card);
  border-radius: var(--radius);
  padding: 32px;
  position: relative;
  overflow: hidden;
}
.step-card__number {
  font-family: var(--font-display);
  font-size: 120px;
  color: var(--accent);
  opacity: 0.08;
  position: absolute;
  top: -16px;
  right: 16px;
  line-height: 1;
  pointer-events: none;
}
.step-card__title {
  font-family: var(--font-display);
  font-size: 40px;
  color: var(--text-primary);
  margin-bottom: 12px;
  position: relative;
}
.step-card__body {
  font-family: var(--font-body);
  font-size: 15px;
  color: var(--text-muted);
  position: relative;
  line-height: 1.6;
}
.step-connector {
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0 16px;
  font-family: var(--font-display);
  font-size: 40px;
  color: var(--accent);
  background: var(--bg);
}

/* ============================================================
   CTA
   ============================================================ */
.cta-section__texture {
  position: absolute;
  inset: 0;
  background-image: repeating-linear-gradient(
    45deg,
    #000000 0, #000000 1px,
    transparent 0, transparent 8px
  );
  opacity: 0.06;
  pointer-events: none;
}
.cta-section__inner {
  position: relative;
  z-index: 1;
  max-width: 800px;
  margin: 0 auto;
  padding: 0 24px;
}
.cta-section__title {
  font-family: var(--font-display);
  font-size: clamp(64px, 10vw, 120px);
  color: #000000;
  line-height: 0.9;
  margin-bottom: 24px;
}
.cta-section__subtitle {
  font-family: var(--font-body);
  font-size: 20px;
  color: rgba(0,0,0,0.7);
  margin-bottom: 40px;
}
.cta-section .watermark {
  color: #000000;
  opacity: 0.06;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
}

/* ============================================================
   FAQ
   ============================================================ */
.faq-section__title {
  font-family: var(--font-display);
  font-size: clamp(40px, 6vw, 64px);
  margin-bottom: 40px;
}
.faq-list { display: flex; flex-direction: column; gap: 2px; }
.faq-item__question {
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 20px 24px;
  text-align: left;
  background: none;
  border: none;
  cursor: pointer;
  font-family: var(--font-display);
  font-size: 22px;
  color: var(--text-primary);
  transition: background 0.15s ease;
}
.faq-item__question:hover { background: #141414; }
.faq-item__icon {
  font-family: var(--font-display);
  font-size: 28px;
  color: var(--accent);
  flex-shrink: 0;
  transition: transform 0.2s ease;
}
.faq-item.open .faq-item__icon { transform: rotate(45deg); }
.faq-item__answer {
  max-height: 0;
  overflow: hidden;
  transition: max-height 0.3s ease;
}
.faq-item__answer-inner {
  padding: 0 24px 20px;
  font-family: var(--font-body);
  font-size: 16px;
  color: var(--text-muted);
  line-height: 1.7;
}
.faq-item__answer-inner a { color: var(--accent); text-decoration: underline; text-underline-offset: 3px; }

/* ============================================================
   FOOTER
   ============================================================ */
.site-footer {
  background: var(--bg);
  border-top: 2px solid var(--border);
  padding: 60px 0 32px;
}
.site-footer__inner {
  max-width: var(--max-width);
  margin: 0 auto;
  padding: 0 24px;
}
.site-footer__top {
  display: grid;
  grid-template-columns: 1fr 1fr 1fr;
  gap: 48px;
  margin-bottom: 48px;
}
.site-footer__logo {
  font-family: var(--font-display);
  font-size: 40px;
  color: var(--text-primary);
  display: block;
  margin-bottom: 12px;
}
.site-footer__tagline {
  font-family: var(--font-mono);
  font-size: 10px;
  color: var(--text-muted);
  text-transform: uppercase;
  letter-spacing: 0.1em;
}
.site-footer__nav-title {
  font-family: var(--font-mono);
  font-size: 10px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.15em;
  color: var(--text-muted);
  margin-bottom: 16px;
}
.site-footer__nav-links { display: flex; flex-direction: column; gap: 10px; }
.site-footer__nav-links a {
  font-family: var(--font-body);
  font-size: 15px;
  color: var(--text-secondary);
  transition: color 0.15s ease;
}
.site-footer__nav-links a:hover { color: var(--accent); }
.site-footer__newsletter-form {
  display: flex;
  gap: 8px;
  margin-top: 16px;
}
.site-footer__newsletter-form .input { flex: 1; }
.site-footer__socials {
  display: flex;
  gap: 8px;
  margin-top: 16px;
}
.social-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 40px; height: 40px;
  background: var(--bg-raised);
  border: 2px solid var(--border-card);
  border-radius: var(--radius);
  font-family: var(--font-mono);
  font-size: 10px;
  font-weight: 700;
  color: var(--text-primary);
  transition: all 0.15s ease;
  text-decoration: none;
}
.social-btn:hover { border-color: var(--accent); color: var(--accent); }
.site-footer__bottom {
  border-top: 1px solid var(--border);
  padding-top: 24px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 12px;
}
.site-footer__legal {
  font-family: var(--font-mono);
  font-size: 10px;
  color: var(--text-muted);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  text-decoration: none;
}
.site-footer__legal:hover { color: var(--accent); }

/* ============================================================
   PRODUCT PAGE
   ============================================================ */
.main-product { padding: var(--section-pad) 0; }
.product-layout {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 48px;
  align-items: start;
}
.product-gallery__main {
  /* Square, to match the product images, which carry the date and section in a
     band across the bottom. The old 3/4 portrait frame cropped a square image
     on both sides and took that band with it. */
  aspect-ratio: 1;
  background: var(--bg-raised);
  border: 2px solid var(--border-card);
  border-radius: var(--radius);
  overflow: hidden;
  display: flex;
  align-items: center;
  justify-content: center;
}
/* `contain`, not `cover`: nothing may be cut off a ticket image. A square
   image fills this frame exactly; anything else letterboxes instead. */
.product-gallery__main img { width: 100%; height: 100%; object-fit: contain; }
.product-info { display: flex; flex-direction: column; gap: 24px; }
.product-info__vendor {
  font-family: var(--font-mono);
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  color: var(--text-muted);
}
.product-info__title {
  font-family: var(--font-display);
  font-size: 56px;
  color: var(--text-primary);
}
.product-info__price {
  font-family: var(--font-display);
  font-size: 48px;
  color: var(--accent);
}
.product-info__desc {
  font-size: 16px;
  color: var(--text-muted);
  line-height: 1.7;
}
.variant-picker { display: flex; flex-direction: column; gap: 8px; }
.variant-picker__label {
  font-family: var(--font-mono);
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--text-muted);
}
.variant-buttons { display: flex; gap: 8px; flex-wrap: wrap; }
.variant-btn {
  padding: 10px 20px;
  background: var(--bg-raised);
  border: 2px solid var(--border-card);
  border-radius: var(--radius);
  font-family: var(--font-body);
  font-size: 15px;
  font-weight: 500;
  color: var(--text-primary);
  cursor: pointer;
  transition: all 0.15s ease;
}
.variant-btn:hover, .variant-btn.active { border-color: var(--accent); }
.variant-btn.active { background: var(--accent); color: #000; }

/* ============================================================
   COLLECTION PAGE
   ============================================================ */
.main-collection { padding: var(--section-pad) 0; }
.collection-header { margin-bottom: 40px; }
.collection-title {
  font-family: var(--font-display);
  font-size: clamp(48px, 8vw, 80px);
  color: var(--text-primary);
}
.product-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 2px;
  background: var(--border);
}
.product-card {
  background: var(--bg-raised);
  border: 2px solid var(--border-card);
  border-radius: var(--radius);
  overflow: hidden;
  transition: border-color 0.15s ease;
}
.product-card:hover { border-color: var(--accent); }
.product-card__image {
  aspect-ratio: 1;
  background: var(--bg-soft);
  overflow: hidden;
}
.product-card__image img { width: 100%; height: 100%; object-fit: contain; }
.product-card__body { padding: 16px; }
.product-card__title {
  font-family: var(--font-display);
  font-size: 24px;
  color: var(--text-primary);
  margin-bottom: 8px;
}
.product-card__price {
  font-family: var(--font-display);
  font-size: 28px;
  color: var(--accent);
}

/* ============================================================
   CART PAGE
   ============================================================ */
.main-cart { padding: var(--section-pad) 0; }
.cart-layout {
  display: grid;
  grid-template-columns: 1fr 380px;
  gap: 40px;
  align-items: start;
}
.cart-title {
  font-family: var(--font-display);
  font-size: 64px;
  margin-bottom: 32px;
  color: var(--text-primary);
}
.cart-item {
  display: flex;
  gap: 20px;
  padding: 20px 0;
  border-bottom: 1px solid var(--border);
}
.cart-item__image {
  width: 80px; height: 80px;
  background: var(--bg-raised);
  border: 2px solid var(--border-card);
  border-radius: var(--radius);
  overflow: hidden;
  flex-shrink: 0;
}
.cart-item__title {
  font-family: var(--font-display);
  font-size: 24px;
  margin-bottom: 4px;
  color: var(--text-primary);
}
.cart-item__price {
  font-family: var(--font-display);
  font-size: 28px;
  color: var(--accent);
}
.cart-summary {
  background: var(--bg-raised);
  border: 2px solid var(--border-card);
  border-radius: var(--radius);
  padding: 32px;
  position: sticky;
  top: 100px;
}
.cart-summary__title {
  font-family: var(--font-display);
  font-size: 32px;
  margin-bottom: 24px;
  color: var(--text-primary);
}
.cart-summary__row {
  display: flex;
  justify-content: space-between;
  padding: 12px 0;
  border-bottom: 1px solid var(--border);
  font-family: var(--font-body);
  font-size: 16px;
  color: var(--text-secondary);
}
.cart-summary__total {
  display: flex;
  justify-content: space-between;
  padding: 16px 0;
  font-family: var(--font-display);
  font-size: 32px;
  color: var(--text-primary);
}

/* ============================================================
   404 PAGE
   ============================================================ */
.main-404 {
  min-height: 60vh;
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  padding: var(--section-pad) 24px;
}
.error-404__number {
  font-family: var(--font-display);
  font-size: clamp(120px, 20vw, 200px);
  color: var(--accent);
  line-height: 1;
}
.error-404__title {
  font-family: var(--font-display);
  font-size: 48px;
  margin-bottom: 16px;
  color: var(--text-primary);
}
.error-404__body {
  font-size: 18px;
  color: var(--text-muted);
  margin-bottom: 32px;
}

/* ============================================================
   RESPONSIVE
   ============================================================ */
@media (max-width: 1024px) {
  .ticket-tiers__grid { grid-template-columns: 1fr; max-width: 480px; margin: 0 auto; }
  .how-it-works__steps-wrapper { grid-template-columns: 1fr; }
  .step-connector { transform: rotate(90deg); }
  .product-layout { grid-template-columns: 1fr; }
  .cart-layout { grid-template-columns: 1fr; }
  .product-grid { grid-template-columns: repeat(2, 1fr); }
}

@media (max-width: 768px) {
  :root { --section-pad: 48px; }
  .site-header__nav { display: none; }
  .menu-btn { display: flex; }
  .hero__inner { grid-template-columns: 1fr; }
  .hero__image-wrap { max-height: 400px; }
  .event-info__grid { grid-template-columns: 1fr; }
  .site-footer__top { grid-template-columns: 1fr; }
  .product-grid { grid-template-columns: 1fr; }
  .hero__actions { flex-direction: column; }
  .hero__actions .btn { width: 100%; justify-content: center; }
  .countdown { gap: 8px; }
  .countdown__unit { padding: 16px 20px; min-width: 80px; }
  .countdown__number { font-size: 48px; }
}

@media (prefers-reduced-motion: reduce) {
  .js-reveal .reveal { opacity: 1; translate: none; scale: none; transform: none; transition: none; }
  .hero-img-wrap { animation: none; }
  .reveal.visible { animation: none; }
  * { transition-duration: 0.01ms !important; animation-duration: 0.01ms !important; }
}

/* ============================================================
   MARQUEE / ANNOUNCEMENT BAR (new)
   ============================================================ */
.marquee-wrap {
  background: var(--accent);
  padding: 10px 0;
  overflow: hidden;
  border-bottom: 2px solid var(--accent-deep);
}
.marquee-track {
  display: flex;
  gap: 0;
  animation: marquee 60s linear infinite;
  white-space: nowrap;
}
.marquee-track span {
  font-family: var(--font-mono);
  font-size: 12px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.14em;
  color: #000;
  padding: 0 40px;
}
.mq-sep { padding: 0 24px !important; opacity: 0.5; letter-spacing: 0 !important; }
@keyframes marquee { from { transform: translateX(0); } to { transform: translateX(-50%); } }

/* ============================================================
   HEADER (new classes)
   ============================================================ */
.header {
  position: sticky;
  top: 0;
  z-index: 100;
  background: var(--bg);
  border-bottom: 2px solid var(--border);
}
.header-inner {
  max-width: var(--max-width);
  margin: 0 auto;
  padding: 16px 24px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
}
.header-logo {
  font-family: var(--font-display);
  font-size: 28px;
  letter-spacing: 0.06em;
  color: var(--text-primary);
}
.header-logo:hover { color: var(--accent); }
.header-nav { display: flex; align-items: center; gap: 32px; }
.header-nav a {
  font-family: var(--font-mono);
  font-weight: 700;
  font-size: 13px;
  text-transform: uppercase;
  letter-spacing: 0.15em;
  color: var(--text-muted);
  transition: color 0.15s;
}
.header-nav a:hover { color: var(--accent); }
.nav-overlay.is-open { display: flex; }

/* ============================================================
   HERO (new layout)
   ============================================================ */
.hero-inner {
  max-width: var(--max-width);
  margin: 0 auto;
  padding: 80px 24px;
  display: grid;
  grid-template-columns: 3fr 2fr;
  gap: 48px;
  align-items: center;
  width: 100%;
}
.hero-img-wrap {
  position: relative;
  background: var(--bg-raised);
  border: 2px solid var(--border-card);
  aspect-ratio: 350 / 524;
  overflow: hidden;
  display: flex;
  align-items: center;
  justify-content: center;
}
.hero-img-wrap img { width: 100%; height: 100%; object-fit: contain; display: block; }
.hero-badge {
  position: absolute;
  top: 14px; left: 14px;
  background: var(--accent);
  color: #000;
  font-family: var(--font-mono);
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  padding: 4px 10px;
  border: 1px solid var(--accent-deep);
}
.hero-badge-br {
  position: absolute;
  bottom: 14px; right: 14px;
  background: transparent;
  color: var(--accent);
  font-family: var(--font-mono);
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  padding: 4px 10px;
  border: 1px solid var(--accent);
}
.hero-trust { display: flex; gap: 24px; margin-top: 20px; flex-wrap: wrap; }
.hero-trust span {
  font-family: var(--font-mono);
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--text-muted);
}
.hero-trust span::before { content: '+ '; color: var(--accent); }

/* ============================================================
   TICKET SECTIONS (new)
   ============================================================ */
.tickets-section {
  background: var(--bg);
  border-bottom: 2px solid var(--border);
  padding: 80px 0;
}
.tickets-section--alt { background: var(--bg-soft); }
.section-title {
  font-family: var(--font-display);
  font-size: clamp(40px, 6vw, 64px);
  color: var(--text-primary);
  margin-bottom: 40px;
}
.ticket-grid {
  display: grid;
  /* Not a fixed three. 9 DÉC sells four tiers and other nights sell three, and
     a hard `repeat(3, ...)` wrapped the fourth card onto a row of its own with
     two empty cells beside it. auto-fit gives each night as many columns as it
     has cards, down to a 230px floor, after which they stack. */
  grid-template-columns: repeat(auto-fit, minmax(230px, 1fr));
  gap: 2px;
  background: var(--border);
}
.ticket-card {
  background: var(--bg-raised);
  border: 2px solid var(--border-card);
  border-radius: var(--radius);
  padding: 32px;
  display: flex;
  flex-direction: column;
  gap: 20px;
  overflow: hidden;
  transition: border-color 0.15s;
}
/* Deliberately no border change on hover -- see the print lift at the end of
   this file. The border and the offset shadow are both accent green, so
   changing both merges them into one thick edge and the shadow stops reading
   as a shadow. The shadow alone is the louder signal anyway. */
.ticket-card--sold-out { opacity: 0.5; }
.ticket-tier-name {
  font-family: var(--font-mono);
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  /* 0.2em read as wide as the badge beside it; 0.15em keeps the tracked look
     and gives the row back about 6px. */
  letter-spacing: 0.15em;
  color: var(--text-muted);
}
.ticket-price {
  font-family: var(--font-display);
  font-size: 64px;
  line-height: 1;
  color: var(--accent);
}
.ticket-price-sub {
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--text-muted);
  letter-spacing: 0.08em;
  margin-top: 4px;
}
.ticket-perks { display: flex; flex-direction: column; gap: 8px; flex: 1; }
.ticket-perk {
  font-size: 15px;
  color: var(--text-secondary);
  display: flex;
  gap: 10px;
}
.ticket-perk::before { content: '✦'; color: var(--accent); flex-shrink: 0; }

/* Strikethrough original price */
.price-original {
  font-family: var(--font-display);
  font-size: 36px;
  color: var(--text-muted);
  position: relative;
  display: inline-block;
  margin-left: 20px;
  vertical-align: middle;
}
.price-original::after {
  content: '';
  position: absolute;
  left: -2px; right: -2px;
  top: 50%;
  height: 2px;
  background: var(--text-muted);
  transform: rotate(-12deg);
}

/* Inline countdown inside ticket sections */
.cd-unit {
  background: var(--bg-raised);
  border: 2px solid var(--border-card);
  text-align: center;
}
.cd-num {
  font-family: var(--font-display);
  font-size: 56px;
  line-height: 1;
  color: var(--accent);
  display: block;
}
.cd-lbl {
  font-family: var(--font-mono);
  font-size: 10px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  color: var(--text-muted);
  margin-top: 4px;
  display: block;
}

/* ============================================================
   EVENT INFO (tabbed new)
   ============================================================ */
.event-info-section {
  background: var(--bg-soft);
  border-bottom: 2px solid var(--border);
  padding: 80px 0;
}
.info-grid {
  display: grid;
  grid-template-columns: 2fr 1fr;
  gap: 2px;
  background: var(--border);
}
.info-card {
  background: var(--bg-raised);
  border: 2px solid var(--border-card);
  border-radius: var(--radius);
  padding: 32px;
}
.info-label {
  font-family: var(--font-mono);
  font-size: 10px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.15em;
  color: var(--text-muted);
  margin-bottom: 6px;
}
.info-value {
  font-family: var(--font-display);
  font-size: 36px;
  color: var(--text-primary);
}
.info-divider { border: none; border-top: 1px solid var(--border); margin: 20px 0; }

/* ============================================================
   TOUR DATES (new classes)
   ============================================================ */
.tour-row {
  display: flex;
  align-items: center;
  gap: 24px;
  padding: 18px 0;
  border-bottom: 1px solid var(--border);
}
.tour-row:first-child { border-top: 1px solid var(--border); }
.tour-date-txt {
  font-family: var(--font-display);
  font-size: 36px;
  color: var(--accent);
  min-width: 130px;
}
.tour-row__end {
  display: flex;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
  justify-content: flex-end;
}
/* A night that is nearly gone wears the danger colour: a bar down its left
   edge and the date itself in red. The bar is an inset shadow rather than a
   border so it costs no width -- the row slides 8px on hover and a border
   would shift every row's content against it. */
.tour-row--hot {
  box-shadow: inset 3px 0 0 var(--danger);
  padding-left: 14px;
}
.tour-row--hot .tour-date-txt { color: var(--danger); }
.tour-city { font-weight: 600; font-size: 17px; color: var(--text-primary); }
.tour-venue-txt { font-size: 13px; color: var(--text-muted); }
.tour-link-btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 7px 14px;
  background: transparent;
  color: var(--accent);
  font-family: var(--font-mono);
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  border: 2px solid var(--accent);
  transition: all 0.15s;
  text-decoration: none;
}
.tour-link-btn:hover { background: var(--accent); color: #000; }

/* ============================================================
   HOW IT WORKS (new classes)
   ============================================================ */
.hiw-section {
  background: var(--bg);
  border-bottom: 2px solid var(--border);
  padding: 80px 0;
}
.hiw-grid {
  display: grid;
  grid-template-columns: 1fr auto 1fr auto 1fr;
  /* stretch, not center: the grid's own --border background is the hairline
     between cells, so a short arrow cell would expose it as a grey block. */
  align-items: stretch;
  gap: 0;
  background: var(--border);
}
.hiw-card {
  background: var(--bg-raised);
  padding: 32px;
  position: relative;
  overflow: hidden;
}
.hiw-num {
  font-family: var(--font-display);
  font-size: 120px;
  color: var(--accent);
  opacity: 0.08;
  position: absolute;
  top: -16px; right: 12px;
  line-height: 1;
  pointer-events: none;
}
.hiw-title {
  font-family: var(--font-display);
  font-size: 36px;
  color: var(--text-primary);
  margin-bottom: 10px;
  position: relative;
}
.hiw-body {
  font-size: 15px;
  color: var(--text-muted);
  position: relative;
  line-height: 1.6;
}
.hiw-arrow {
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0 16px;
  font-family: var(--font-display);
  font-size: 40px;
  color: var(--accent);
  text-align: center;
  background: var(--bg);
}

.info-step-btn {
  font-family: var(--font-display);
  font-size: 22px;
  color: var(--accent);
  background: none;
  border: 2px solid var(--border-card);
  padding: 8px 20px;
  cursor: pointer;
  transition: border-color 0.15s ease, color 0.15s ease;
}
.info-step-btn:hover { border-color: var(--accent); }
.info-step-btn:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
}

/* Footer socials: square buttons, Space Mono two-letter marks. */
.footer-socials {
  display: flex;
  gap: 8px;
  margin-top: 20px;
}
@media (max-width: 860px) {
  /* 40px is under the touch-target floor, and on a phone these are the only
     things to tap in the footer's upper half. */
  .footer-social { width: 44px; height: 44px; }
}
.footer-social {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 40px;
  border: 2px solid var(--border-card);
  font-family: var(--font-mono);
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.06em;
  color: var(--text-secondary);
  text-decoration: none;
  transition: border-color 0.15s ease, color 0.15s ease;
}
/* The glyph, not the box. 20px inside a 40px square leaves the border room to
   read as a frame rather than as a ring squeezed around an icon. `block` kills
   the inline descender gap that would otherwise push it a pixel off centre. */
.social-ico {
  display: block;
  width: 20px;
  height: 20px;
}
.footer-social:hover { border-color: var(--accent); color: var(--accent); }
.footer-social:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
}

/* ============================================================
   CTA (new classes)
   ============================================================ */
.cta-section {
  background: var(--accent);
  padding: 80px 0;
  text-align: center;
  position: relative;
  overflow: hidden;
}
.cta-watermark {
  position: absolute;
  font-family: var(--font-display);
  font-size: 300px;
  color: #000;
  opacity: 0.08;
  top: 50%; left: 50%;
  transform: translate(-50%, -50%);
  white-space: nowrap;
  pointer-events: none;
  line-height: 1;
}
.cta-title {
  font-family: var(--font-display);
  font-size: clamp(64px, 10vw, 120px);
  color: #000;
  line-height: 0.9;
  margin-bottom: 24px;
  position: relative;
}
.cta-sub {
  font-size: 18px;
  color: rgba(0,0,0,0.7);
  margin-bottom: 40px;
  position: relative;
}

/* ============================================================
   FAQ (new classes)
   ============================================================ */
.faq-section {
  background: var(--bg-soft);
  border-bottom: 2px solid var(--border);
  padding: 80px 0;
}
.faq-item {
  background: var(--bg-raised);
  border: 2px solid var(--border-card);
  border-radius: var(--radius);
  margin-bottom: 2px;
  overflow: hidden;
}
.faq-item.open { border-color: var(--accent); }
.faq-q {
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 20px 24px;
  text-align: left;
  background: none;
  border: none;
  cursor: pointer;
  font-family: var(--font-display);
  font-size: 22px;
  color: var(--text-primary);
  transition: background 0.15s;
}
.faq-q:hover { background: #141414; }
.faq-icon {
  font-family: var(--font-display);
  font-size: 28px;
  color: var(--accent);
  flex-shrink: 0;
  transition: transform 0.2s;
}
.faq-item.open .faq-icon { transform: rotate(45deg); }
.faq-body { max-height: 0; overflow: hidden; transition: max-height 0.3s ease; }
.faq-body-inner {
  padding: 0 24px 20px;
  font-size: 16px;
  color: var(--text-muted);
  line-height: 1.7;
}
.faq-body-inner a { color: var(--accent); }

/* ============================================================
   FOOTER (new classes)
   ============================================================ */
.footer {
  background: var(--bg);
  border-top: 2px solid var(--border);
  padding: 60px 0 32px;
}
.footer-top {
  display: grid;
  grid-template-columns: 1fr 1fr 1fr;
  gap: 48px;
  margin-bottom: 48px;
}
.footer-logo {
  font-family: var(--font-display);
  font-size: 40px;
  color: var(--text-primary);
  display: block;
  margin-bottom: 12px;
}
.footer-tagline {
  font-family: var(--font-mono);
  font-size: 10px;
  color: var(--text-muted);
  text-transform: uppercase;
  letter-spacing: 0.1em;
}
.footer-nav-title {
  font-family: var(--font-mono);
  font-size: 10px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.15em;
  color: var(--text-muted);
  margin-bottom: 16px;
}
.footer-links { display: flex; flex-direction: column; gap: 10px; }
.footer-links a { font-size: 15px; color: var(--text-secondary); transition: color 0.15s; }
.footer-links a:hover { color: var(--accent); }
.footer-bottom {
  border-top: 1px solid var(--border);
  padding-top: 24px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 12px;
}
.footer-legal {
  font-family: var(--font-mono);
  font-size: 10px;
  color: var(--text-muted);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  text-decoration: none;
}
.footer-legal:hover { color: var(--accent); }

/* ============================================================
   MY ORDERS SECTION (homepage inline)
   ============================================================ */
.tag {
  display: inline-block;
  padding: 3px 10px;
  background: var(--accent);
  color: #000;
  font-family: var(--font-mono);
  font-size: 10px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  white-space: nowrap;
}
.tag--danger { background: var(--danger); color: #fff; }

/* Scarcity badges are built like the seat map's legend rows -- a tinted panel
   inside a 2px border of the same hue -- so the two of them read as one kind
   of thing and MEILLEUR, which is a label rather than a status, stays the only
   solid chip on the card. The dot is what separates a status from a label. */
.tag--warn,
.tag--scarce {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 3px 8px;
  /* "Presque épuisé" is half again the length of "almost gone", and it has to
     share a 234px row with the tier name. A point smaller and tracked tighter
     than the other chips buys the ~25px that keeps it on one line. */
  font-size: 9px;
  letter-spacing: 0.08em;
}
.tag--warn::before,
.tag--scarce::before {
  content: '';
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: currentColor;
  flex: 0 0 6px;
}
.tag--warn {
  background: var(--accent-soft);
  color: var(--accent);
  box-shadow: inset 0 0 0 2px var(--accent);
}
.tag--scarce {
  background: var(--danger-soft);
  color: var(--danger);
  box-shadow: inset 0 0 0 2px var(--danger);
}
/* Four tiers to a row leaves about 244px inside a card, and "PRESQUE ÉPUISÉ"
   plus a tier name does not fit on one line of that. Letting the row wrap
   drops the badge onto its own line instead of breaking the tier name across
   two, which is what it did before. */
.ticket-card__head {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
}
.ticket-card__tags {
  display: flex;
  align-items: center;
  gap: 6px;
  flex-wrap: wrap;
  justify-content: flex-end;
}

/* ============================================================
   MOBILE RESPONSIVE (updated)
   ============================================================ */
@media (max-width: 768px) {
  .container { padding: 0 16px; }
  .header-inner { padding: 12px 16px; }
  .header-nav { display: none; }
  .menu-btn { display: flex; }

  /* --- Touch targets ---------------------------------------------------
     Twelve controls measured under 40px on a 390px phone: the language
     switch at 29px tall, the cart at 35, the burger at 32, every footer
     link at 22. All clickable, all fiddly. The switcher, cart and burger
     only need a floor, since their padding already centres their contents.
     The footer links had no vertical padding at all, so they take it and
     give up the gap that was standing in for it -- same rhythm down the
     page, three times the area to hit. */
  .lang-btn,
  .cart-btn,
  .menu-btn { min-height: 44px; min-width: 44px; }
  .menu-btn { align-items: center; justify-content: center; }
  .footer-links { gap: 0; }
  .footer-links a { display: block; padding: 11px 0; }

  /* Standalone controls can simply be taller. */
  .seatmap__tab,
  .tour-link-btn,
  [role="tab"] { min-height: 44px; }

  /* Links set in running text cannot be, without opening gaps in the line
     they sit on. They take the height as padding and give it straight back
     as negative margin: the hit area grows to 44px, the layout does not
     move a pixel. */
  .ticket-tier-name--link,
  .footer-bottom a {
    display: inline-block;
    padding: 14px 0;
    margin: -14px 0;
  }
  /* The cart stays visible on mobile — a count that changes where nobody
     can see it is not feedback. Compact, so the bar still fits. */
  .cart-btn { padding: 7px 10px; gap: 6px; font-size: 10px; }
  .cart-btn__count { width: 18px; height: 18px; font-size: 10px; }
  .marquee-wrap { padding: 6px 0; }
  .marquee-track span { font-size: 9px; padding: 0 12px; }
  .hero { min-height: auto; }
  .hero-inner { grid-template-columns: 1fr; gap: 16px; padding: 24px 16px 32px; text-align: center; }
  .hero-inner > div:first-child { order: 2; }
  /* The poster is the hero on a phone -- it arrives first and the copy sits
     under it -- so 200px on a 390px screen left it at 45% of the width with
     dead space either side. Bound by three things instead: a hard ceiling for
     tablets, a share of the width, and a share of the height so a long phone
     does not push the button off the bottom. aspect-ratio honours max-height,
     so the width follows from whichever bound bites first. */
  .hero-img-wrap {
    order: 1;
    max-width: min(420px, 92vw);
    /* Height is the binding constraint on a tall phone, and it was being held
       down to keep the button above the fold. Asked for a bigger poster a
       third time, that trade is settled the other way: the poster gets the
       room and the button sits just below the fold. The cap stays so a short
       phone does not end up with a poster taller than its screen. */
    max-height: 62vh;
    margin: 0 auto;
  }
  .hero-badge { font-size: 8px; padding: 3px 6px; top: 6px; left: 6px; }
  .hero-badge-br { font-size: 8px; padding: 3px 6px; bottom: 6px; right: 6px; }
  .hero-trust { flex-direction: column; gap: 6px; align-items: center; }
  .hero-trust span { font-size: 10px; }
  .tickets-section { padding: 48px 0; }
  .ticket-grid { grid-template-columns: 1fr; }
  .ticket-card { padding: 20px; }
  .ticket-price { font-size: 48px; }
  .price-original { font-size: 24px; margin-left: 10px; }
  .cd-unit { min-width: 52px !important; padding: 8px 10px !important; }
  .cd-num { font-size: 28px !important; }
  .cd-lbl { font-size: 9px; }
  .info-grid { grid-template-columns: 1fr; }
  .tour-row { flex-wrap: wrap; gap: 8px; }
  .tour-date-txt { font-size: 26px; min-width: auto; }
  .hiw-grid { grid-template-columns: 1fr; background: transparent; }
  .hiw-arrow { display: none; }
  .hiw-card { padding: 24px; margin-bottom: 2px; }
  .cta-section { padding: 48px 0; }
  .cta-title { font-size: clamp(44px, 13vw, 72px); }
  .faq-section { padding: 48px 0; }
  .footer { padding: 40px 0 24px; }
  .footer-top { grid-template-columns: 1fr; gap: 28px; margin-bottom: 28px; }
  .footer-bottom { flex-direction: column; align-items: flex-start; gap: 8px; }
  #info-tab-0, #info-tab-1, #info-tab-2 { padding: 8px 10px !important; font-size: 10px !important; }
  .section-title { font-size: clamp(30px, 8vw, 48px); margin-bottom: 24px; }
}

@media (max-width: 420px) {
  .cart-btn { padding: 7px 8px; }
  #lang-switcher button { padding: 7px 9px !important; }
  .ticket-price { font-size: 40px; }
  .tour-date-txt { font-size: 22px; }
  .cd-num { font-size: 24px !important; }
}

/* Visually hidden — available to screen readers only (form labels, etc.) */
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* ── Sticky header ──
   `.header` already carries `position: sticky`, which on its own does nothing
   here: Shopify wraps every section in its own div, and a sticky box can only
   travel inside its parent's box — which is exactly the height of the header.
   The generated wrapper is what has to stick. `:has()` picks out the one
   wrapper holding the header, so the announcement bar above it still scrolls
   away rather than the two stacking on top of each other. */
.shopify-section-group-header-group:has(> .header) {
  position: sticky;
  top: 0;
  z-index: 100;
}

/* ── Quantity stepper ── */
.qty {
  display: inline-flex;
  align-items: stretch;
  border: 2px solid var(--border-strong);
  background: var(--bg-input);
  margin-bottom: 16px;
}
.qty__btn {
  width: 46px;
  font-family: var(--font-display);
  font-size: 24px;
  line-height: 1;
  color: var(--text-primary);
  background: transparent;
  transition: background 0.2s ease, color 0.2s ease;
}
.qty__btn:hover:not(:disabled) { background: var(--accent); color: #0A0A0A; }
.qty__btn:disabled { color: var(--text-muted); cursor: not-allowed; }
.qty__btn:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.qty__input {
  width: 64px;
  padding: 12px 0;
  text-align: center;
  font-family: var(--font-mono);
  font-size: 15px;
  color: var(--text-primary);
  background: transparent;
  border: none;
  border-left: 2px solid var(--border-strong);
  border-right: 2px solid var(--border-strong);
  -moz-appearance: textfield;
}
.qty__input::-webkit-outer-spin-button,
.qty__input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.qty__input:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.qty__label {
  display: block;
  margin-bottom: 8px;
  font-family: var(--font-mono);
  font-size: 12px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  color: var(--text-muted);
}

/* On a tier card the stepper spans the card and takes the button's place once
   the ticket is in the cart. `display: flex` would otherwise beat the browser's
   own `[hidden] { display: none }`, so it has to be turned off explicitly. */
.qty--tier {
  display: flex;
  width: 100%;
}
.qty[hidden] { display: none; }
.qty--tier .qty__input { flex: 1; width: auto; cursor: default; }

/* A card that opens its product should look like it does. */
.ticket-card--clickable { cursor: pointer; }
.ticket-tier-name--link {
  text-decoration: none;
  transition: color 0.2s ease;
}
.ticket-tier-name--link:hover { color: var(--accent); }
.ticket-tier-name--link:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
}

/* ==========================================================================
   Seating map
   The plan is inline SVG, so the blocks are styled here rather than baked into
   the markup: one hover rule covers all 36 of them, and the whole plan follows
   the theme tokens if the palette ever changes.
   ========================================================================== */

.seatmap-section {
  background: var(--bg);
  border-bottom: 2px solid var(--border);
  padding: 80px 0;
}
.seatmap-section--alt { background: var(--bg-soft); }

.seatmap__head {
  display: flex;
  align-items: baseline;
  gap: 24px;
  flex-wrap: wrap;
  margin-bottom: 12px;
}
.seatmap__slug {
  font-family: var(--font-mono);
  font-size: 13px;
  color: var(--accent);
  letter-spacing: 0.12em;
}
.seatmap__intro {
  color: var(--text-secondary);
  max-width: 62ch;
  margin-bottom: 28px;
}

/* Date tabs — same control as the event info tabs, so the page has one idiom. */
.seatmap__tabs {
  display: inline-flex;
  border: 2px solid var(--border-card);
  margin-bottom: 24px;
}
.seatmap__tabs--single { display: none; }
.seatmap__tab {
  /* 10px vertical reproduces the 36px tab height measured off the original
     before it was replaced. The horizontal value is a judgement call: the
     original's tabs measured 80/90/90 wide and these come out 78/86/84,
     which is the closest a rebuilt rule gets without the source to copy. */
  padding: 10px 18px;
  background: transparent;
  border: 0;
  border-right: 2px solid var(--border-card);
  font-family: var(--font-mono);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.12em;
  color: var(--text-muted);
  cursor: pointer;
}
.seatmap__tab:last-child { border-right: 0; }
.seatmap__tab:hover { color: var(--text-primary); }
.seatmap__tab:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.seatmap__tab.is-active { background: var(--accent); color: #000000; }
.seatmap__layout {
  display: grid;
  grid-template-columns: minmax(0, 1.55fr) minmax(280px, 1fr);
  gap: 32px;
  align-items: start;
}

.seatmap__plan {
  background: #000;
  border: 2px solid var(--border-card);
  padding: 16px;
}
.seatmap {
  display: block;
  width: 100%;
  height: auto;
}
/* On a wide screen the plan fits and the viewport does nothing at all. */
.seatmap__hint { display: none; }

/* --- the blocks ---------------------------------------------------------- */

.seatmap__block { cursor: pointer; }
.seatmap__block polygon,
.seatmap__block rect {
  stroke: #000;
  stroke-width: 2;
  transition: fill 0.2s ease, opacity 0.2s ease;
}
.seatmap__block text {
  font-family: var(--font-body);
  font-size: 22px;
  font-weight: 700;
  fill: #FFFFFF;
  text-anchor: middle;
  pointer-events: none;
  user-select: none;
}
.seatmap__block[data-zone-now="gold"] polygon { fill: var(--accent); }
.seatmap__block[data-zone-now="cat1"] polygon { fill: var(--tier-mid); }
.seatmap__block[data-zone-now="upper"] polygon { fill: var(--tier-upper); }
.seatmap__block[data-zone-now="soldout"] polygon { fill: var(--tier-sold); }
.seatmap__block[data-zone-now="soldout"] { cursor: default; }
.seatmap__block[data-zone-now="soldout"] text { fill: #8A8A8A; }
.seatmap__block--floor rect { fill: #FFFFFF; }
.seatmap__block--floor text {
  font-family: var(--font-body);
  font-size: 30px;
  font-weight: 700;
  fill: #0A0A0A;
  letter-spacing: 0.04em;
}

/* Hovering or selecting a zone lights that zone and steps the rest back, so
   the answer to "where am I sitting" is one glance rather than a legend hunt. */
.seatmap.is-focused .seatmap__block { opacity: 0.28; }
.seatmap.is-focused .seatmap__block.is-lit { opacity: 1; }
/* A lit zone is shown by the rest of the hall stepping back, not by outlining
   it. The outline used to be white at 3px, which across 34 outer blocks read
   as a glow round the whole ring and belonged to no other part of the site --
   nothing here outlines in white. The black hairline the blocks already carry
   just gets a little heavier, which reads as the lit zone sharpening while
   everything else falls away. */
.seatmap__block.is-lit polygon,
.seatmap__block.is-lit rect { stroke: #000000; stroke-width: 3; }

.seatmap__stage rect { fill: #FFFFFF; stroke: #000000; stroke-width: 2; }
.seatmap__stage text {
  font-family: var(--font-body);
  font-size: 22px;
  font-weight: 700;
  letter-spacing: 0.06em;
  fill: #0A0A0A;
  text-anchor: middle;
}

/* --- the panel ----------------------------------------------------------- */

.seatmap__panel {
  display: flex;
  flex-direction: column;
  gap: 20px;
  position: sticky;
  top: 96px;
}
.seatmap__readout {
  background: var(--bg-raised);
  border: 2px solid var(--border-card);
  padding: 24px;
}
.seatmap__readout-name {
  font-family: var(--font-display);
  font-size: 38px;
  line-height: 1;
  color: var(--text-primary);
}
.seatmap__readout-note {
  color: var(--text-muted);
  font-size: 14px;
  margin-top: 6px;
  min-height: 20px;
}
.seatmap__readout-price {
  display: flex;
  align-items: baseline;
  gap: 10px;
  margin: 18px 0 20px;
}
.seatmap__from {
  font-family: var(--font-mono);
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  color: var(--text-muted);
}
.seatmap__readout-price strong {
  font-family: var(--font-display);
  font-size: 44px;
  line-height: 1;
  font-weight: 400;
  color: var(--accent);
}
.seatmap__legend {
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: 2px;
  margin: 0;
  padding: 0;
}
.seatmap__legend-btn {
  width: 100%;
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 14px;
  background: var(--bg-input);
  border: 2px solid var(--border-card);
  color: var(--text-primary);
  text-align: left;
  cursor: pointer;
}
.seatmap__legend-static {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 14px;
  background: var(--bg-input);
  border: 2px solid var(--border);
  color: var(--text-muted);
}
.seatmap__legend-btn:hover { border-color: var(--accent); }
.seatmap__legend-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.seatmap__legend-btn[aria-pressed="true"] {
  border-color: var(--accent);
  background: var(--accent-soft);
}
.seatmap__swatch {
  width: 18px;
  height: 18px;
  flex: 0 0 18px;
  border: 2px solid #000;
}
.seatmap__swatch--gold { background: var(--accent); }
.seatmap__swatch--cat1 { background: var(--tier-mid); }
.seatmap__swatch--soldout { background: var(--tier-sold); }
.seatmap__swatch--upper { background: var(--tier-upper); }
.seatmap__swatch--floor { background: #FFFFFF; }
.seatmap__legend-text { flex: 1 1 auto; min-width: 0; }
.seatmap__legend-name {
  display: block;
  font-family: var(--font-mono);
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-primary);
}
.seatmap__legend-note {
  display: block;
  font-size: 12px;
  color: var(--text-muted);
}
.seatmap__legend-price {
  font-family: var(--font-display);
  font-size: 24px;
  color: var(--accent);
}
.seatmap__disclaimer {
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--text-muted);
}

/* The ticket card the map sent you to, so the jump lands somewhere obvious. */
.ticket-card.is-flash {
  animation: seatmap-flash 1.4s ease;
}
@keyframes seatmap-flash {
  0%, 100% { box-shadow: none; }
  15%, 60% { box-shadow: var(--shadow-hover); }
}

/* Four cards across a narrow desktop window leave about 199px inside each
   one, which is 11px short of a tier name plus "presque épuisé". The cards
   give the room back rather than the badge wrapping. */
@media (max-width: 1240px) {
  .ticket-card { padding: 24px; }
  .tag--warn,
  .tag--scarce { padding: 3px 7px; }
}

@media (max-width: 900px) {
  .seatmap__layout { grid-template-columns: 1fr; }
  .seatmap__panel { position: static; }
  .seatmap__block text { font-size: 24px; }
}

/* The map is one SVG scaled to its container, so label size in CSS pixels is
   font-size x (container width / 976 viewBox units). These steps keep every
   block label at roughly 10-15px on screen instead of shrinking with the map.
   The ceiling is set by the narrowest block: the outer ring's side columns are
   69 units wide, so a three-character label like H16 cannot go past about 32
   before it runs out of block to sit in. */
@media (max-width: 600px) {
  /* --- The whole plan, on one screen -----------------------------------
     The drawing was rendering about 301px wide inside the container's
     padding and its own, which put a 30-unit label at 9.2px. Rather than
     let it overflow and be dragged, it takes the width back: -16px cancels
     the container's mobile padding exactly (no vw, so no scrollbar
     surprises), the side borders come off, and the padding drops to 8. The
     drawing gets about 374px of a 390px phone instead of 301.
     min-width:0 stays -- a grid item defaults to min-width:auto and would
     otherwise size the column to the drawing rather than to the screen. */
  .seatmap__plan {
    min-width: 0;
    margin-left: -16px;
    margin-right: -16px;
    border-left: 0;
    border-right: 0;
    padding: 8px;
  }
  .seatmap { width: 100%; }

  /* --- The readout is a second copy of the list below it -----------------
     Name, description and price for the chosen zone, directly above a list
     that carries all three for every zone and outlines the chosen one. On a
     phone that duplicate cost 222px of an 844px screen, so it is clipped and
     the button closes up under the plan.
     Clipped, not display:none -- the readout is aria-live, and removing it
     would leave a screen reader with no answer when a block is tapped. At a
     pixel square it takes no layout space and still speaks. */
  .seatmap__readout { background: none; border: 0; padding: 0; }
  .seatmap__readout > .info-label,
  .seatmap__readout > .seatmap__readout-name,
  .seatmap__readout > .seatmap__readout-note,
  .seatmap__readout > .seatmap__readout-price {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    overflow: hidden;
    clip: rect(0 0 0 0);
    clip-path: inset(50%);
    white-space: nowrap;
    border: 0;
  }

  /* --- The zone list scrolls, the plan stays put ------------------------
     See the note above: 230px shows three rows and half of the fourth, and
     keeps plan + button + list inside one screen so the arena cannot slide
     away while you read down the zones. overscroll-behavior stops the page
     from taking over once the list hits its end. */
  .seatmap__legend {
    max-height: 230px;
    overflow-y: auto;
    overscroll-behavior-y: contain;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: thin;
    scrollbar-color: var(--border-strong) transparent;
  }
  .seatmap__legend::-webkit-scrollbar { width: 4px; }
  .seatmap__legend::-webkit-scrollbar-track { background: transparent; }
  .seatmap__legend::-webkit-scrollbar-thumb { background: var(--border-strong); }

  .seatmap__block text { font-size: 27px; }
  .seatmap__block--floor text { font-size: 34px; }
  .seatmap__stage text { font-size: 24px; }
}
@media (max-width: 430px) {
  /* 32 is the ceiling named above: the outer ring's side columns are 69
     units wide, so a three-character label like H16 starts running out of
     block past it. At the full-width scale that lands near 12px. */
  .seatmap__block text { font-size: 32px; }
  .seatmap__block--floor text { font-size: 40px; }
  .seatmap__stage text { font-size: 28px; }
}

@media (prefers-reduced-motion: reduce) {
  .seatmap__block polygon,
  .seatmap__block rect,
    .seatmap__tab { transition: none; }
  .ticket-card.is-flash { animation: none; }
}

/* ============================================================
   MOTION LAYER
   One place for how the site moves. Everything here is either a
   transition on an existing resting state or a short one-shot
   animation, so nothing loops in the corner of your eye except
   the one hero badge. The global prefers-reduced-motion rule
   further up already flattens every duration; the block at the
   end of this file additionally stops the loop outright.
   ============================================================ */

:root {
  --ease-out: cubic-bezier(0.16, 0.84, 0.44, 1);    /* quick, then settles */
  --ease-press: cubic-bezier(0.34, 1.32, 0.5, 1);   /* releases with a touch of overshoot */
  /* Long, soft deceleration -- most of the distance is covered early and the
     last few pixels take their time, which is what reads as gliding rather
     than sliding. Reserved for the scroll entrances; controls keep the
     shorter --ease-out so they still feel immediate under the finger. */
  --ease-glide: cubic-bezier(0.16, 1, 0.3, 1);
  --reveal-dur: 0.95s;
  --dur-fast: 0.13s;
  --dur: 0.22s;
  --dur-slow: 0.45s;
  --glow-on: 0 6px 22px -6px rgba(103, 192, 95, 0.55);
}

/* --- Anything clickable: lift on hover, sink on press ------------------ */
.btn,
.info-step-btn,
.footer-social,
.seatmap__legend-btn,
.cart-btn,
.tour-link-btn,
.faq-q,
.seatmap__tab,
#btn-fr, #btn-en, #mob-btn-fr, #mob-btn-en {
  position: relative;
  overflow: hidden;
  transition:
    transform var(--dur) var(--ease-press),
    box-shadow var(--dur) var(--ease-out),
    background-color var(--dur-fast) var(--ease-out),
    border-color var(--dur-fast) var(--ease-out),
    color var(--dur-fast) var(--ease-out);
}
.btn:hover,
.info-step-btn:hover,
.footer-social:hover,
.seatmap__legend-btn:hover,
.cart-btn:hover,
.tour-link-btn:hover {
  transform: translateY(-2px);
  box-shadow: var(--glow-on);
}
.btn:active,
.info-step-btn:active,
.footer-social:active,
.seatmap__legend-btn:active,
.cart-btn:active,
.tour-link-btn:active,
.faq-q:active,
.seatmap__tab:active,
#btn-fr:active, #btn-en:active, #mob-btn-fr:active, #mob-btn-en:active,
.is-press {
  transform: translateY(1px) scale(0.985);
  box-shadow: none;
  transition-duration: var(--dur-fast);
}
.btn:disabled:hover,
.btn[disabled]:hover {
  transform: none;
  box-shadow: none;
}

/* --- Click ripple ------------------------------------------------------ */
.ripple {
  position: absolute;
  border-radius: 50%;
  pointer-events: none;
  transform: scale(0);
  background: currentColor;
  opacity: 0.26;
  animation: ripple-out 0.55s var(--ease-out) forwards;
}
.btn--primary .ripple,
#btn-fr .ripple, #mob-btn-fr .ripple { background: #000000; opacity: 0.18; }
@keyframes ripple-out {
  to { transform: scale(2.4); opacity: 0; }
}

/* --- Trailing arrows glide on hover ------------------------------------ */
.arw {
  display: inline-block;
  transition: transform var(--dur) var(--ease-press);
}
a:hover > .arw,
button:hover > .arw,
.tour-row:hover .arw { transform: translateX(5px); }
a:hover > .arw--back,
button:hover > .arw--back { transform: translateX(-5px); }

/* --- Cards lift ------------------------------------------------------- */
.ticket-card,
.card,
.info-card,
.cd-unit,
.faq-item {
  transition:
    transform var(--dur) var(--ease-out),
    border-color var(--dur-fast) var(--ease-out),
    box-shadow var(--dur) var(--ease-out);
}
.card:hover,
.info-card:hover {
  transform: translateY(-3px);
  border-color: var(--accent);
  box-shadow: var(--glow-on);
}
.cd-unit:hover {
  transform: translateY(-2px);
  border-color: var(--accent);
}
.faq-item:hover { border-color: var(--accent); }
/* Both classes named on purpose. `.ticket-card--sold-out:hover` alone is one
   class and one pseudo-class -- exactly the weight of `.ticket-card:hover` --
   so it only won while it happened to sit later in the file. The print lift
   is declared further down, and this has to beat it outright. */
.ticket-card.ticket-card--sold-out:hover {
  transform: none;
  border-color: var(--border-card);
  box-shadow: none;
  z-index: auto;
}

/* --- How it works: the card sits in a grid whose background is the
       hairline, so lifting it would expose grey. Move the watermark
       number instead and warm the surface. --------------------------- */
.hiw-card { transition: background-color var(--dur) var(--ease-out); }
.hiw-card:hover { background: #161616; }
.hiw-num { transition: opacity var(--dur-slow) var(--ease-out), transform var(--dur-slow) var(--ease-out); }
.hiw-card:hover .hiw-num { opacity: 0.2; transform: translateY(4px) scale(1.04); }
.hiw-arrow { transition: transform var(--dur) var(--ease-press); }
.hiw-grid:hover .hiw-arrow { transform: translateX(4px); }

/* --- Header nav: underline wipes in from the left ---------------------- */
.header-nav a {
  position: relative;
  transition: color var(--dur-fast) var(--ease-out);
}
.header-nav a::after {
  content: '';
  position: absolute;
  left: 0;
  bottom: -5px;
  width: 100%;
  height: 2px;
  background: var(--accent);
  transform: scaleX(0);
  transform-origin: left center;
  transition: transform var(--dur) var(--ease-out);
}
.header-nav a:hover::after,
.header-nav a:focus-visible::after { transform: scaleX(1); }
/* The section the header is currently sitting over keeps the underline, in
   the deeper green so it reads as a position and not as a hover. */
.header-nav a.is-current { color: var(--text-secondary); }
.header-nav a.is-current::after {
  transform: scaleX(1);
  background: var(--accent-deep);
}
.header-nav a.is-current:hover::after { background: var(--accent); }

/* --- Tour rows slide toward the ticket button -------------------------- */
.tour-row {
  transition: transform var(--dur) var(--ease-out), background-color var(--dur) var(--ease-out);
}
.tour-row:hover {
  transform: translateX(8px);
  background: #101010;
}
.tour-date-txt { transition: transform var(--dur) var(--ease-press); }
.tour-row:hover .tour-date-txt { transform: scale(1.06); transform-origin: left center; }

/* --- Countdown digits tick as they change ------------------------------ */
.cd-num, .countdown__number { display: inline-block; }
.cd-num.is-tick, .countdown__number.is-tick { animation: digit-tick 0.34s var(--ease-out); }
@keyframes digit-tick {
  0%   { transform: translateY(-42%); opacity: 0; }
  55%  { transform: translateY(2%);   opacity: 1; }
  100% { transform: translateY(0);    opacity: 1; }
}

/* --- FAQ answer fades in with the height ------------------------------- */
.faq-body-inner {
  opacity: 0;
  transform: translateY(-6px);
  transition: opacity var(--dur) var(--ease-out) 0.06s, transform var(--dur) var(--ease-out) 0.06s;
}
.faq-item.open .faq-body-inner { opacity: 1; transform: translateY(0); }
.faq-icon { transition: transform var(--dur) var(--ease-press), color var(--dur-fast) var(--ease-out); }
.faq-q:hover .faq-icon { transform: scale(1.18); }
.faq-item.open .faq-q .faq-icon { transform: rotate(45deg); }
.faq-item.open .faq-q:hover .faq-icon { transform: rotate(45deg) scale(1.18); }

/* --- Seat map: the hovered block rises out of the bowl ----------------- */
.seatmap__block {
  transition: transform var(--dur) var(--ease-out), opacity var(--dur-fast) var(--ease-out);
  transform-box: fill-box;
  transform-origin: center;
}
.seatmap__block:hover { transform: scale(1.03); }
.seatmap__tab:active { transform: scale(0.96); }

/* --- The hero badges breathe, slowly ----------------------------------- */
.hero-badge-br { animation: badge-breathe 3.6s ease-in-out infinite; }
@keyframes badge-breathe {
  0%, 100% { transform: translateY(0);    box-shadow: 0 0 0 0 rgba(103, 192, 95, 0.45); }
  50%      { transform: translateY(-3px); box-shadow: 0 0 0 10px rgba(103, 192, 95, 0); }
}

/* --- Hero arrives in order --------------------------------------------
       Animation, not transition, and no .visible gate: the hero is above the
       fold and is the first thing ad traffic sees, so its entrance has to
       start at first paint rather than wait for theme.js and an
       IntersectionObserver. Delays stagger the headline, tour line, CTA and
       trust row in that order. */
.hero__content > * { animation: hero-child-in 0.4s var(--ease-glide) both; }
.hero__content > *:nth-child(2) { animation-delay: 0.06s; }
.hero__content > *:nth-child(3) { animation-delay: 0.12s; }
.hero__content > *:nth-child(4) { animation-delay: 0.18s; }
.hero__content > *:nth-child(5) { animation-delay: 0.24s; }
@keyframes hero-child-in {
  from { opacity: 0; transform: translateY(16px); }
  to   { opacity: 1; transform: none; }
}

/* The reveal's travel and easing are set with the rule further up the file,
   on `translate`/`scale` so they stay out of the way of the hover lifts. */

/* --- Inputs respond to focus ------------------------------------------- */
.input, input[type="email"], input[type="text"], input[type="number"] {
  transition: border-color var(--dur-fast) var(--ease-out), box-shadow var(--dur) var(--ease-out);
}
.input:focus, input[type="email"]:focus, input[type="text"]:focus, input[type="number"]:focus {
  box-shadow: 0 0 0 3px rgba(103, 192, 95, 0.18);
}

/* --- Add to cart: the success state lands with a pop -------------------- */
[data-add-to-cart].is-added { animation: added-flash 0.5s var(--ease-out); }
@keyframes added-flash {
  0%   { transform: scale(1); }
  35%  { transform: scale(1.035); }
  100% { transform: scale(1); }
}

/* --- Arriving somewhere -------------------------------------------------
       An eased scroll tells you the page moved but not that it finished.
       The section you were sent to lights its own top edge for a beat.
       Inset box-shadow on purpose: no pseudo-element, so no section needs a
       containing block it did not already have. */
.is-arrive { animation: arrive-edge 0.95s var(--ease-out); }
@keyframes arrive-edge {
  0%   { box-shadow: inset 0 3px 0 0 rgba(103, 192, 95, 0); }
  16%  { box-shadow: inset 0 3px 0 0 rgba(103, 192, 95, 1); }
  68%  { box-shadow: inset 0 3px 0 0 rgba(103, 192, 95, 1); }
  100% { box-shadow: inset 0 3px 0 0 rgba(103, 192, 95, 0); }
}

/* --- Ticket insurance, offered in the cart -------------------------------
       An option rather than a line item until it is chosen, so it is built as
       a control: a real checkbox, visually replaced by a square that fills
       with the accent. The input stays in the DOM and focusable rather than
       display:none, or it would drop out of the tab order. */
.cart-insurance {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 20px;
  flex-wrap: wrap;
  margin-top: 2px;
  padding: 18px 20px;
  background: var(--accent-soft);
  border: 2px solid var(--accent);
}
.cart-insurance.is-busy { opacity: 0.55; pointer-events: none; }
.cart-insurance__label {
  display: flex;
  align-items: center;
  gap: 14px;
  cursor: pointer;
  flex: 1 1 260px;
}
.cart-insurance__input {
  position: absolute;
  width: 1px;
  height: 1px;
  opacity: 0;
}
.cart-insurance__box {
  width: 22px;
  height: 22px;
  flex: 0 0 22px;
  border: 2px solid var(--accent);
  background: transparent;
  position: relative;
  transition: background-color var(--dur-fast) var(--ease-out);
}
.cart-insurance__box::after {
  content: '';
  position: absolute;
  left: 5px;
  top: 1px;
  width: 6px;
  height: 12px;
  border: solid #000;
  border-width: 0 3px 3px 0;
  transform: rotate(45deg) scale(0);
  transition: transform var(--dur) var(--ease-press);
}
.cart-insurance__input:checked + .cart-insurance__box { background: var(--accent); }
.cart-insurance__input:checked + .cart-insurance__box::after { transform: rotate(45deg) scale(1); }
.cart-insurance__input:focus-visible + .cart-insurance__box {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
}
.cart-insurance__text { display: flex; flex-direction: column; gap: 3px; }
.cart-insurance__title {
  font-family: var(--font-mono);
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--text-primary);
}
.cart-insurance__note { font-size: 13px; color: var(--text-muted); }
.cart-insurance__price {
  font-family: var(--font-mono);
  font-size: 12px;
  color: var(--text-muted);
  white-space: nowrap;
}
.cart-insurance__price strong {
  font-family: var(--font-display);
  font-size: 26px;
  color: var(--accent);
  margin-left: 10px;
  vertical-align: middle;
}

@media (max-width: 600px) {
  .cart-insurance { align-items: flex-start; }
  .cart-insurance__price { width: 100%; text-align: right; }
}

/* --- The exclusive bundle ------------------------------------------------
       A band under the tier grid rather than a card in it: it is one item, it
       carries five things rather than three, and it costs several times what
       a seat does. Same surface and border weight as a ticket card so it is
       clearly the same family, but accent-bordered and full width. */
.bundle {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 32px;
  flex-wrap: wrap;
  margin-top: 2px;
  padding: 22px 28px;
  background: var(--accent-soft);
  border: 2px solid var(--accent);
}
/* The band already carries an accent border, so the ticket cards' hard accent
   shadow would merge straight into it and read as nothing. It lifts and lights
   instead: the outer glow the rest of the site uses for a raised surface, plus
   a soft inner one so the panel looks backlit rather than merely moved. */
.bundle {
  transition:
    transform var(--dur) var(--ease-out),
    box-shadow var(--dur) var(--ease-out);
}
.bundle:hover {
  transform: translateY(-3px);
  box-shadow: var(--glow-on), inset 0 0 34px -14px var(--accent);
}

.bundle__head {
  display: flex;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
  margin-bottom: 10px;
}
.bundle__name {
  font-family: var(--font-display);
  font-size: 30px;
  line-height: 1;
  color: var(--accent);
  margin-right: 2px;
}
.bundle__perks {
  list-style: none;
  display: flex;
  flex-wrap: wrap;
  gap: 6px 22px;
  margin: 0;
  padding: 0;
}
.bundle__perk {
  display: flex;
  gap: 8px;
  font-size: 14px;
  color: var(--text-secondary);
}
.bundle__perk::before { content: '✦'; color: var(--accent); flex-shrink: 0; }
.bundle__buy {
  display: flex;
  align-items: center;
  gap: 20px;
  flex-wrap: wrap;
}
.bundle__price {
  font-family: var(--font-display);
  font-size: 44px;
  line-height: 1;
  color: var(--accent);
  white-space: nowrap;
}
/* Red only on the pack, and only on the struck price. The tier cards keep
   theirs muted grey -- if every crossed-out number on the page were red the
   pack would not be the one that looks like a deal. The strike itself has to
   be recoloured too; it is a pseudo-element, not text-decoration. */
.bundle__price .price-original {
  font-size: 26px;
  color: var(--danger);
}
.bundle__price .price-original::after { background: var(--danger); }
.bundle__cta { white-space: nowrap; }

@media (max-width: 768px) {
  /* Stacked, it needs more than a tint to stop reading as a fifth ticket: a
     solid cap across the top, a bigger name, and a button that spans the panel
     rather than sitting at card width. */
  .bundle {
    flex-direction: column;
    align-items: stretch;
    gap: 16px;
    padding: 20px;
    border-top-width: 8px;
  }
  .bundle__name { font-size: 38px; }
  .bundle__perks { gap: 6px 18px; }
  .bundle__buy {
    width: 100%;
    justify-content: space-between;
    gap: 14px;
    padding-top: 14px;
    border-top: 2px solid rgba(103, 192, 95, 0.35);
  }
  .bundle__price { font-size: 40px; }
  .bundle__cta { flex: 1 1 auto; text-align: center; justify-content: center; }
}
@media (max-width: 600px) {
  /* Two-up, the list read as four different row shapes with the second column
     landing at a different x each time -- items size to their own text, so
     there was no left edge to run your eye down. One per line gives every
     marker the same edge and makes the perks countable at a glance. */
  .bundle__perks { flex-direction: column; gap: 9px; }
}
@media (max-width: 430px) {
  /* Below this the price and the button stop fitting side by side. */
  .bundle__buy { flex-direction: column; align-items: stretch; }
  .bundle__cta { width: 100%; }
}

/* The eyebrow sits above the section title, so it needs to clear it. */
.tickets-eyebrow { margin-bottom: 12px; }

/* --- Ticket cards: the print lift ----------------------------------------
       Third attempt, and the shortest. The first stacked crop marks, a perk
       walk, a tier-name colour change and a dimming of the rest of the row on
       one hover; the second washed a cursor-tracked stage light across the
       card. Both were rejected as too much for a site that already moves a
       lot.

       This one is a single gesture, and it was already in the design tokens
       waiting to be used: `--shadow: 4px 4px 0px #E8B923`, a hard offset
       shadow with no blur, declared at the top of this file and -- until now
       -- referenced nowhere. It is the screen-printed poster look the rest of
       the theme is built on, and no other hover state on the site uses it.

       The card shifts up and left by exactly half the shadow's offset, so the
       shadow reads as the card peeling off the page rather than as a green
       rectangle appearing behind it. `z-index` lifts it over its neighbours,
       whose 2px grid gap the shadow would otherwise fall behind.

       Everything else is gone: no wash, no crop marks, no perk movement, no
       dimming, no tier-name colour. */
.ticket-card { position: relative; }
.ticket-card:hover {
  transform: translate(-3px, -3px);
  box-shadow: var(--shadow-hover);
  z-index: 1;
}

/* --- The same lift, driven by scroll on a phone --------------------------
   A touch screen has no pointer to hover with, so the card crossing the
   middle of the screen wears exactly the state the mouse would give it:
   the same 3px shift, the same shadow, the same z-index. Nothing new is
   invented for touch -- it is the one gesture, moved onto a different input.

   It eases here where hover snaps. Hover is a decision the hand makes and
   can hold still; this one changes hands every time a card passes, and an
   instant toggle would strobe down the column as you scroll. */
@media (max-width: 768px) {
  .ticket-card {
    transition: border-color 0.15s,
                transform var(--dur) var(--ease-out),
                box-shadow var(--dur) var(--ease-out);
  }
  .ticket-card.is-passing {
    transform: translate(-3px, -3px);
    box-shadow: var(--shadow-hover);
    z-index: 1;
  }
  /* A sold-out card does not lift on desktop either. Both classes named so
     this carries more weight than the rule above rather than relying on
     source order. */
  .ticket-card.ticket-card--sold-out.is-passing {
    transform: none;
    border-color: var(--border-card);
    box-shadow: none;
    z-index: auto;
  }
  .bundle.is-passing {
    transform: translateY(-3px);
    box-shadow: var(--glow-on), inset 0 0 34px -14px var(--accent);
  }
}
@media (prefers-reduced-motion: reduce) {
  .ticket-card.is-passing, .bundle.is-passing { transform: none; }
}

@media (prefers-reduced-motion: reduce) {
  .hero-badge-br { animation: none; }
  .ripple { display: none; }
  .hero__content > * { opacity: 1; transform: none; transition: none; animation: none; }
  .cd-num.is-tick, .countdown__number.is-tick { animation: none; }
  .btn:hover, .ticket-card:hover, .card:hover, .info-card:hover,
  .tour-row:hover, .cd-unit:hover, .seatmap__block:hover,
  .seatmap__legend-btn:hover, .bundle:hover { transform: none; }
  .is-arrive { animation: none; }
  /* The shadow stays -- it is a colour, not a movement -- but the card
     itself holds still; .ticket-card:hover is already in the transform: none
     list above. */
}


/* ============================================================
   CART DRAWER
   The basket used to be a page you had to go and find: adding a
   ticket only flipped the button to "Ajouté" and nudged a small
   count in the header, so nothing on screen told you where to go
   next. This slides in over the page instead.
   ============================================================ */
.cart-drawer {
  position: fixed;
  inset: 0;
  z-index: 300;
}
.cart-drawer[hidden] { display: none; }
.cart-drawer__scrim {
  position: absolute;
  inset: 0;
  background: rgba(0, 0, 0, 0.66);
  opacity: 0;
  transition: opacity var(--dur) var(--ease-out);
}
.cart-drawer__panel {
  position: absolute;
  top: 0;
  right: 0;
  height: 100%;
  width: min(430px, 100%);
  display: flex;
  flex-direction: column;
  background: var(--bg);
  border-left: 2px solid var(--accent);
  translate: 100% 0;
  transition: translate var(--dur-slow) var(--ease-glide);
}
.cart-drawer.is-open .cart-drawer__scrim { opacity: 1; }
.cart-drawer.is-open .cart-drawer__panel { translate: 0 0; }

.cart-drawer__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 20px 20px 16px;
  border-bottom: 1px solid var(--border);
}
.cart-drawer__title {
  font-family: var(--font-display);
  font-size: 32px;
  line-height: 1;
  margin: 0;
  color: var(--text-primary);
}
.cart-drawer__close {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 36px;
  height: 36px;
  background: none;
  border: 1px solid var(--border-card);
  color: var(--text-primary);
  cursor: pointer;
  transition: border-color var(--dur-fast) var(--ease-out), color var(--dur-fast) var(--ease-out);
}
.cart-drawer__close:hover { border-color: var(--accent); color: var(--accent); }

.cart-drawer__body {
  flex: 1;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  padding: 8px 20px 20px;
}
.cart-drawer__empty {
  padding: 56px 0;
  text-align: center;
  font-family: var(--font-display);
  font-size: 26px;
  color: var(--text-muted);
}

.cart-drawer__item {
  display: grid;
  grid-template-columns: 64px 1fr;
  gap: 14px;
  padding: 18px 0;
  border-bottom: 1px solid var(--border);
}
.cart-drawer__thumb {
  width: 64px;
  height: 64px;
  object-fit: cover;
  border: 1px solid var(--border-card);
  background: var(--bg-raised);
}
.cart-drawer__name {
  font-family: var(--font-display);
  font-size: 17px;
  line-height: 1.15;
  color: var(--text-primary);
  margin: 0 0 2px;
}
.cart-drawer__meta {
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-muted);
  margin: 0 0 10px;
}
.cart-drawer__row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
}
.cart-drawer__price {
  font-family: var(--font-display);
  font-size: 20px;
  color: var(--accent);
}

/* The stepper mirrors the one on the ticket cards so the two do not
   feel like different controls for the same job. */
.cart-drawer__qty {
  display: inline-flex;
  align-items: stretch;
  border: 1px solid var(--border-card);
}
.cart-drawer__qty button {
  width: 30px;
  background: none;
  border: none;
  color: var(--text-primary);
  font-family: var(--font-mono);
  font-size: 15px;
  line-height: 1;
  cursor: pointer;
  transition: background-color var(--dur-fast) var(--ease-out);
}
.cart-drawer__qty button:hover { background: var(--accent-soft); }
.cart-drawer__qty button:disabled { opacity: 0.35; cursor: default; background: none; }
.cart-drawer__qty span {
  min-width: 34px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-family: var(--font-mono);
  font-size: 13px;
  color: var(--text-primary);
  border-left: 1px solid var(--border-card);
  border-right: 1px solid var(--border-card);
}
.cart-drawer__remove {
  background: none;
  border: none;
  padding: 0;
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-muted);
  text-decoration: underline;
  cursor: pointer;
  transition: color var(--dur-fast) var(--ease-out);
}
.cart-drawer__remove:hover { color: var(--danger); }

.cart-drawer__foot {
  padding: 18px 20px calc(20px + env(safe-area-inset-bottom, 0px));
  border-top: 1px solid var(--border);
  background: var(--bg-soft);
}
.cart-drawer__foot[hidden] { display: none; }
.cart-drawer__totals {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  margin-bottom: 6px;
}
.cart-drawer__total {
  font-family: var(--font-display);
  font-size: 30px;
  color: var(--text-primary);
}
.cart-drawer__note {
  font-size: 12px;
  color: var(--text-muted);
  margin: 0 0 14px;
}
.cart-drawer__checkout { margin-bottom: 10px; }
.cart-drawer__link {
  display: block;
  text-align: center;
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--text-muted);
  text-decoration: underline;
}
.cart-drawer__link:hover { color: var(--accent); }

.cart-drawer.is-busy .cart-drawer__body { opacity: 0.5; pointer-events: none; }

/* A phone gets the whole screen. A 430px panel on a 390px device would
   otherwise leave a sliver of page down one side that looks like a
   rendering fault rather than a choice. */
@media (max-width: 560px) {
  .cart-drawer__panel { width: 100%; border-left: none; }
  .cart-drawer__title { font-size: 28px; }
}

@media (prefers-reduced-motion: reduce) {
  .cart-drawer__panel { transition: none; }
  .cart-drawer__scrim { transition: none; }
}


/* ============================================================
   HERO v2 -- full-bleed media behind the type
   Supersedes the two-column layout further up this file. The
   poster no longer sits in a bordered box beside the headline;
   it fills the section, and the copy sits on top of it.
   ============================================================ */
.hero {
  position: relative;
  overflow: hidden;
  min-height: min(88vh, 820px);
  display: flex;
  align-items: flex-end;
  border-bottom: 2px solid var(--border);
}
.hero-media {
  position: absolute;
  inset: 0;
  overflow: hidden;
}
.hero-media__still,
.hero-media__video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
/* The still drifts even when there is no video, so the first screen is never
   completely static. Slow enough that it reads as depth rather than movement. */
.hero-media__still {
  animation: hero-drift 26s ease-in-out infinite alternate;
  transform-origin: 60% 45%;
}
@keyframes hero-drift {
  from { scale: 1.06; translate: 0 0; }
  to   { scale: 1.15; translate: -2% -1.5%; }
}
/* The video covers the still once it can paint. Until then the still shows
   through, so a slow connection sees the poster rather than a black box. */
.hero-media__video { z-index: 1; }
.hero-media__veil {
  position: absolute;
  inset: 0;
  z-index: 2;
  pointer-events: none;
}

.hero-inner {
  position: relative;
  z-index: 3;
  display: block;
  width: 100%;
  max-width: var(--max-width);
  margin: 0 auto;
  padding: 120px 24px 64px;
  grid-template-columns: none;
  gap: 0;
}
.hero__content { position: relative; max-width: 900px; }

.hero-eyebrow {
  display: block;
  font-family: var(--font-mono);
  font-size: 12px;
  letter-spacing: 0.28em;
  text-transform: uppercase;
  color: var(--accent);
  margin-bottom: 14px;
}

.hero-name {
  font-size: clamp(64px, 13vw, 168px);
  line-height: 0.86;
  color: #fff;
  margin: 0 0 6px;
  letter-spacing: 0.01em;
}

/* --- Mis-registered print ---------------------------------------------
   Two decorative copies of the name, one cyan and one red, offset behind
   the white original. `isolation: isolate` gives the heading its own
   stacking context so the negative z-index puts them behind the letters
   and nowhere else. The text comes from data-glitch, so assistive tech
   reads the name once, not three times. */
.glitch {
  position: relative;
  isolation: isolate;
}
.glitch::before,
.glitch::after {
  content: attr(data-glitch);
  position: absolute;
  inset: 0;
  pointer-events: none;
  user-select: none;
}
.glitch::before {
  color: #46B5D1;
  z-index: -1;
  animation: glitch-cyan 3.4s steps(1) infinite;
}
.glitch::after {
  color: var(--danger);
  z-index: -2;
  animation: glitch-red 4.1s steps(1) infinite;
}
/* Mostly a steady couple of pixels out -- that is the print look. The burst
   near the end of each cycle is what makes it feel alive rather than blurred. */
@keyframes glitch-cyan {
  0%, 90%, 100% { translate: -3px 0;  opacity: 0.55; }
  91%           { translate: -11px 3px; opacity: 0.95; }
  93%           { translate: 6px -3px;  opacity: 0.8; }
  95%           { translate: -5px 1px;  opacity: 0.85; }
  97%           { translate: -3px 0;    opacity: 0.55; }
}
@keyframes glitch-red {
  0%, 88%, 100% { translate: 3px 0;   opacity: 0.45; }
  89%           { translate: 9px -2px; opacity: 0.85; }
  92%           { translate: -6px 3px; opacity: 0.7; }
  94%           { translate: 4px -1px; opacity: 0.75; }
  96%           { translate: 3px 0;    opacity: 0.45; }
}

.hero-tour {
  font-size: clamp(24px, 4vw, 52px);
  color: var(--accent);
  margin: 0 0 26px;
  line-height: 1;
}
.hero-cta {
  font-size: 20px;
  padding: 16px 38px;
  display: inline-flex;
}
.hero-trust {
  display: flex;
  gap: 24px;
  margin-top: 22px;
  flex-wrap: wrap;
}
.hero-trust span {
  font-family: var(--font-mono);
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--text-secondary);
}
.hero-trust span::before { content: '+ '; color: var(--accent); }

@media (max-width: 768px) {
  .hero {
    min-height: 84vh;
    align-items: flex-end;
  }
  .hero-inner {
    padding: 96px 18px 40px;
    text-align: left;
    grid-template-columns: none;
  }
  .hero-name { line-height: 0.9; }
  .hero-tour { margin-bottom: 20px; }
  .hero-cta { width: 100%; justify-content: center; }
  .hero-trust { gap: 6px 18px; }
  .hero-trust span { font-size: 10px; }
  /* The still is portrait and the section is now short and wide, so anchor
     the crop above centre -- otherwise a phone frames the crowd's feet. */
  .hero-media__still,
  .hero-media__video { object-position: 50% 28%; }
}

@media (prefers-reduced-motion: reduce) {
  .hero-media__still { animation: none; scale: 1.04; }
  .glitch::before, .glitch::after { animation: none; opacity: 0.4; }
}


/* --- Add-ons inside the drawer ---------------------------------------
   The cart page lays each add-on out as a wide row: label and note on
   the left, price on the right. In a 430px drawer that squeezes the
   note to two words a line, so the same markup is restacked here. */
.cart-drawer__addons {
  padding: 0 20px 4px;
  display: grid;
  gap: 8px;
}
.cart-drawer__addons[hidden] { display: none; }
.cart-drawer__addons .cart-insurance {
  display: grid;
  grid-template-columns: 1fr auto;
  align-items: start;
  gap: 10px 12px;
  padding: 12px 14px;
  margin: 0;
}
.cart-drawer__addons .cart-insurance__label { align-items: flex-start; }
.cart-drawer__addons .cart-insurance__title { font-size: 12px; }
.cart-drawer__addons .cart-insurance__note {
  font-size: 11px;
  line-height: 1.35;
  /* Three lines of small print per add-on would push the checkout button off
     a phone screen. The full wording is still on the cart page. */
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.cart-drawer__addons .cart-insurance__price {
  text-align: right;
  font-size: 11px;
  white-space: nowrap;
}
.cart-drawer__addons .cart-insurance__price strong { display: block; font-size: 17px; }


/* --- Hero still: sharpness first, movement second ---------------------
   The first version zoomed the image from 1.06 to 1.15 to get its drift.
   On a 1134px-wide source stretched across a 1920px hero that is already
   a 1.7x upscale, so the animation was adding magnification to a picture
   that had none to spare -- the movement was costing the sharpness.

   The portrait source in a landscape box overflows vertically by roughly
   two thousand pixels once `cover` has done its work. That slack is free.
   Panning `object-position` through it gives a slow vertical tilt with no
   scaling at all, so the image now renders at exactly the size `cover`
   asks for and not a pixel larger. */
.hero-media__still {
  scale: 1;
  translate: none;
  animation: hero-pan 30s ease-in-out infinite alternate;
  object-position: 50% 50%;
  will-change: object-position;
}
/* The travel is bounded below the poster's own artwork. The source has
   "ORELSAN / TOUR 2026" set into the top of the frame, and panning up far
   enough to reveal it put blurred type across the top of the hero, directly
   above the real headline. Everything from about 40% down is crowd and
   stage light, which is what this is meant to be a backdrop of. */
@keyframes hero-pan {
  from { object-position: 50% 47%; }
  to   { object-position: 50% 68%; }
}

/* Fine noise over an upscaled photograph. The eye reads a soft image as out
   of focus, and the same image with grain over it as film -- it buys back
   more apparent sharpness than sharpening filters do, and it costs one
   inline SVG. */
.hero-media__grain {
  position: absolute;
  inset: -50%;
  z-index: 1;
  pointer-events: none;
  opacity: 0.15;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='220' height='220'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='3' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='220' height='220' filter='url(%23n)'/%3E%3C/svg%3E");
  background-size: 220px 220px;
  animation: grain-shift 700ms steps(1) infinite;
}
/* Static grain reads as a dirty screen. Jittering it a few pixels is what
   makes it read as moving film instead. */
@keyframes grain-shift {
  0%   { transform: translate(0, 0); }
  25%  { transform: translate(-6px, 4px); }
  50%  { transform: translate(5px, -7px); }
  75%  { transform: translate(-4px, -3px); }
  100% { transform: translate(6px, 5px); }
}
.hero-media__veil { z-index: 2; }
.hero-media__video { z-index: 0; }

@media (max-width: 768px) {
  /* A phone's hero is nearly the same shape as the poster, so `cover` leaves
     almost no overflow to pan through. A little scale makes the room. The
     source is 1134px wide and a 390px phone at 3x wants 1170, so this is the
     most magnification available before it starts to show. */
  .hero-media__still {
    scale: 1.06;
    object-position: 50% 52%;
    animation: hero-pan-sm 26s ease-in-out infinite alternate;
  }
  @keyframes hero-pan-sm {
    from { object-position: 50% 48%; }
    to   { object-position: 50% 62%; }
  }
  .hero-media__grain { opacity: 0.12; }
}

@media (prefers-reduced-motion: reduce) {
  .hero-media__still { animation: none; object-position: 50% 50%; }
  .hero-media__grain { animation: none; }
}


/* --- Hero sized to the backdrop, not the other way round ---------------
   The section was 818px tall and the image had to stretch to fill it.
   hero-bg.jpg is 1600x490, so a 1905px-wide hero at roughly 620px tall
   asks it to scale about 1.27x instead of the 1.68x the portrait poster
   needed. Same picture, a quarter less magnification. */
.hero { min-height: min(70vh, 640px); }

.hero-media__still {
  /* A little over cover, purely to make room to pan. Any more and the gain
     from the wider source starts being handed back. */
  scale: 1.1;
  object-position: 50% 50%;
  animation: hero-pan 34s ease-in-out infinite alternate;
}
/* The band is far wider than it is tall, so the slack is sideways now --
   which is also the more legible direction of travel behind a headline,
   because nothing drifts up into the type. */
@keyframes hero-pan {
  from { object-position: 36% 50%; }
  to   { object-position: 64% 50%; }
}

.hero-inner { padding: 92px 24px 52px; }

@media (max-width: 768px) {
  .hero { min-height: min(68vh, 560px); }
  .hero-inner { padding: 76px 18px 34px; }
  /* A wide band cropped into a portrait box has enormous horizontal slack --
     roughly 1400px of it -- so the phone gets the pan for free, with the
     image scaled down rather than up. That is why it is sharper here than
     the poster ever was. */
  .hero-media__still {
    scale: 1;
    object-position: 50% 50%;
    animation: hero-pan-sm 30s ease-in-out infinite alternate;
  }
  @keyframes hero-pan-sm {
    from { object-position: 34% 50%; }
    to   { object-position: 66% 50%; }
  }
}

@media (prefers-reduced-motion: reduce) {
  .hero-media__still { animation: none; object-position: 50% 50%; }
}


/* --- Hero back to full height -----------------------------------------
   Shortening the section to suit a thin cropped band made the page look
   squat, and the band itself was the wrong call: the ask was to take the
   lettering off the poster, not to replace the poster. hero-bg.jpg is now
   the whole photograph with the type painted out, so the section goes back
   to the proportions it had. */
.hero { min-height: min(88vh, 820px); }
.hero-inner { padding: 120px 24px 64px; }

.hero-media__still {
  /* Cover already blows a 1134-wide portrait up to span the section. Nothing
     is added on top of that -- the drift comes from the two thousand pixels of
     vertical overflow cropping leaves behind, which costs nothing. */
  scale: 1;
  object-position: 50% 56%;
  animation: hero-pan 34s ease-in-out infinite alternate;
}
/* No lettering left in the frame, so the travel no longer has to dodge it. */
@keyframes hero-pan {
  from { object-position: 50% 48%; }
  to   { object-position: 50% 68%; }
}

@media (max-width: 768px) {
  .hero { min-height: 84vh; }
  .hero-inner { padding: 96px 18px 40px; }
  /* A phone's hero is nearly the poster's own shape, so cover leaves almost no
     overflow. Four percent is enough to pan through and close to the most this
     source can give a 3x screen before it softens. */
  .hero-media__still {
    scale: 1.04;
    object-position: 50% 52%;
    animation: hero-pan-sm 30s ease-in-out infinite alternate;
  }
  @keyframes hero-pan-sm {
    from { object-position: 50% 46%; }
    to   { object-position: 50% 60%; }
  }
}

@media (prefers-reduced-motion: reduce) {
  .hero-media__still { animation: none; object-position: 50% 56%; }
}


/* --- Trust row: icons, and three across on a phone ---------------------
   It was three plain lines with a "+" in front of each. On a phone they
   stacked one under the other and read as a list of terms rather than as
   reassurance. Now each carries a mark of its own and the three sit in a
   row at every width -- an equal-thirds grid rather than a flex wrap, so
   the columns line up instead of breaking wherever the text happens to
   run out. */
.hero-trust {
  display: grid;
  grid-template-columns: repeat(3, auto);
  justify-content: start;
  gap: 26px;
  margin: 26px 0 0;
  padding: 0;
  list-style: none;
}
.hero-trust__item {
  display: flex;
  align-items: center;
  gap: 8px;
  font-family: var(--font-mono);
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--text-secondary);
}
/* The "+" these replace was supplied by a ::before on the label. */
.hero-trust span::before { content: none; }
.hero-trust__icon {
  width: 17px;
  height: 17px;
  flex: none;
  color: var(--accent);
}

@media (max-width: 768px) {
  /* Equal thirds of the screen. The labels are all about the same length,
     so they wrap to two lines together rather than one column running on
     while the others sit short. */
  .hero-trust {
    grid-template-columns: repeat(3, 1fr);
    gap: 10px;
    width: 100%;
    margin-top: 22px;
  }
  .hero-trust__item {
    flex-direction: column;
    align-items: center;
    text-align: center;
    gap: 6px;
    font-size: 9px;
    letter-spacing: 0.06em;
    line-height: 1.3;
  }
  .hero-trust__icon { width: 20px; height: 20px; }
}


/* --- Grain costs more on a phone than it is worth at full size ---------
   The layer is inset -50%, so it is four times the viewport, and it was
   repainting five times a second. That is fine on a desktop GPU and it is
   not fine on a mid-range phone, where it competes with the first paint.
   Smaller layer, slower jitter, and a little less of it. */
@media (max-width: 768px) {
  .hero-media__grain {
    inset: -12%;
    opacity: 0.1;
    animation-duration: 1.4s;
  }
}


/* The busy state covered the item list but not the add-ons, which sit in
   their own container -- so the checkboxes stayed live while the basket was
   mid-flight. */
.cart-drawer.is-busy .cart-drawer__addons { opacity: 0.5; pointer-events: none; }
.cart-insurance__input:disabled + .cart-insurance__box { opacity: 0.5; }


/* --- The half second while the basket is being written ------------------ */
.cart-drawer__item--pending { opacity: 0.6; }
.cart-drawer__skeleton {
  display: block;
  height: 11px;
  margin-bottom: 9px;
  background: linear-gradient(90deg, var(--bg-raised) 0%, var(--border) 50%, var(--bg-raised) 100%);
  background-size: 200% 100%;
  animation: skeleton-sweep 1.1s linear infinite;
}
@keyframes skeleton-sweep {
  from { background-position: 200% 0; }
  to   { background-position: -200% 0; }
}
@media (prefers-reduced-motion: reduce) {
  .cart-drawer__skeleton { animation: none; }
}


/* ============================================================
   MOBILE AUDIT FIXES
   ============================================================ */

/* --- Tour rows stopped fitting on a phone ------------------------------
   The row is one flex line: date, city and venue, then a scarcity chip and
   a button. At 390px the chip squeezed the middle column until "Accor
   Arena" broke across two lines, and only on the dates that carry a chip --
   so the three rows came out different heights, which reads as a fault
   rather than a design.

   A two-line grid instead. The date and the city keep the top line; the
   chip, the price and the button share the second. Nothing competes for
   width, and every row is the same height whether it carries a chip or not. */
.tour-from {
  font-family: var(--font-mono);
  font-size: 12px;
  letter-spacing: 0.04em;
  color: var(--text-secondary);
  white-space: nowrap;
}

@media (max-width: 768px) {
  .tour-row {
    display: grid;
    grid-template-columns: auto 1fr;
    grid-template-areas:
      "date  who"
      "end   end";
    align-items: center;
    column-gap: 14px;
    row-gap: 12px;
    padding: 16px 0;
  }
  /* The date is a <div> too, so the "everything else" selector has to say so
     explicitly -- and :not() carries specificity, which is why simply listing
     .tour-date-txt first was not enough to keep it out of the second column.
     Both landed in the same cell and the city printed over the date. */
  .tour-row > .tour-date-txt { grid-area: date; }
  .tour-row > div:not(.tour-row__end):not(.tour-date-txt) { grid-area: who; }
  .tour-row__end {
    grid-area: end;
    justify-content: flex-start;
    gap: 10px;
  }
  .tour-date-txt { font-size: 28px; min-width: 0; }
  .tour-city { font-size: 16px; }
  .tour-venue-txt { font-size: 12px; white-space: nowrap; }
  .tour-link-btn { margin-left: auto; }
  .tour-from { font-size: 11px; }
}

/* --- The announcement strip was set at 9px ----------------------------
   Small enough that it reads as decoration rather than as the date and the
   venue, which is what it is carrying. */
@media (max-width: 768px) {
  .marquee-track span { font-size: 10px; }
}

/* --- The seat map legend was a scroll box inside a scrolling page ------
   A fixed-height panel with its own scrollbar is awkward on a phone at the
   best of times, and it was cutting the last tier off mid-row, so one of
   four prices was hidden behind a gesture people had no reason to try. It
   is four rows; on mobile they simply stack. */
@media (max-width: 768px) {
  .seatmap__legend {
    max-height: none !important;
    overflow: visible !important;
  }
}


/* One intro for pointers, one for fingers. */
.seatmap__intro--touch { display: none; }
@media (hover: none), (pointer: coarse) {
  .seatmap__intro--pointer { display: none; }
  .seatmap__intro--touch { display: block; }
}


/* ============================================================
   REASSURANCE ROW (under the ticket tiers)
   The hero carries three one-word promises; this is the same three
   ideas at the point of the decision, where each needs a sentence
   rather than a label. Bordered like .info-card so it reads as part
   of the ticket furniture, not as a banner bolted underneath.
   ============================================================ */
.reassure {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 16px;
  margin-top: 40px;
  padding: 0;
  list-style: none;
}
.reassure__item {
  display: flex;
  gap: 14px;
  align-items: flex-start;
  background: var(--bg-raised);
  border: 2px solid var(--border-card);
  border-radius: var(--radius);
  padding: 20px;
}
.reassure__icon {
  width: 26px;
  height: 26px;
  flex: 0 0 26px;
  color: var(--accent);
  margin-top: 2px;
}
.reassure__title {
  font-family: var(--font-mono);
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  color: var(--text-primary);
  margin: 0 0 6px;
}
.reassure__body {
  font-family: var(--font-body);
  font-size: 13px;
  line-height: 1.5;
  color: var(--text-secondary);
  margin: 0;
}
.reassure__link {
  color: var(--accent);
  text-decoration: none;
  border-bottom: 1px solid transparent;
  transition: border-color 0.2s ease;
}
.reassure__link:hover,
.reassure__link:focus-visible { border-bottom-color: var(--accent); }

@media (max-width: 900px) {
  /* Three across leaves each column too narrow for a sentence; two lines of
     two would orphan the third, so they stack. */
  .reassure { grid-template-columns: 1fr; gap: 12px; }
  .reassure__item { padding: 16px; }
}

/* ============================================================
   HERO — THE DEPARTURES BOARD
   The tour is the hero. No photograph: the wordmark is the image,
   so the first screen is text and CSS and paints on frame one.
   Archivo Black lives here and nowhere else -- wide and heavy reads
   arena, where the condensed Bebas of the rest of the theme reads
   poster. One face, one place, so the wordmark stays the loudest
   thing on the page.
   ============================================================ */
:root { --font-hero: 'Archivo Black', 'Bebas Neue', sans-serif; }

.hero-pb {
  background: var(--bg);
  padding: 72px 0 64px;
  border-bottom: 1px solid var(--border);
}
.hero-pb__inner {
  max-width: var(--max-width);
  margin: 0 auto;
  padding: 0 24px;
}
.hero-pb__eyebrow {
  font-family: var(--font-mono);
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--tier-mid);
  margin: 0 0 18px;
}
.hero-pb__name {
  font-family: var(--font-hero);
  /* Fills the measure at any width; the clamp ceiling stops it outgrowing
     the container on very wide screens. */
  font-size: clamp(56px, 13vw, 188px);
  line-height: 0.84;
  letter-spacing: -0.02em;
  color: var(--accent);
  margin: 0;
  text-transform: uppercase;
  overflow-wrap: anywhere;
}
.hero-pb__tour {
  font-family: var(--font-mono);
  font-size: clamp(12px, 1.5vw, 16px);
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--text-primary);
  margin: 18px 0 0;
}

/* --- The board --- */
.board {
  margin-top: 44px;
  border: 2px solid var(--border-card);
  border-radius: var(--radius);
  background: var(--bg-raised);
  overflow: hidden;
}
.board__head,
.board__row {
  display: grid;
  grid-template-columns: minmax(140px, auto) minmax(0, 0.6fr) minmax(0, 1fr) auto;
  gap: 20px;
  align-items: center;
  padding: 0 22px;
}
.board__head {
  font-family: var(--font-mono);
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--text-muted);
  background: var(--bg-soft);
  border-bottom: 2px solid var(--border-card);
  padding-top: 12px;
  padding-bottom: 12px;
  margin: 0;
}
.board__row {
  min-height: 76px;
  text-decoration: none;
  border-bottom: 1px solid var(--border);
  transition: background 0.2s ease;
}
.board__row:last-child { border-bottom: none; }
a.board__row:hover,
a.board__row:focus-visible { background: var(--accent-soft); }
a.board__row:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }

.board__date { display: flex; align-items: center; gap: 10px; }
.board__date-num {
  font-family: var(--font-display);
  font-size: 30px;
  line-height: 1;
  color: var(--text-primary);
}
.board__flag {
  font-family: var(--font-mono);
  font-size: 9px;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  background: var(--accent);
  color: #000;
  padding: 3px 7px;
  border-radius: var(--radius);
}
.board__city {
  font-family: var(--font-display);
  font-size: 26px;
  line-height: 1;
  color: var(--text-primary);
  letter-spacing: 0.02em;
}
.board__venue {
  font-family: var(--font-body);
  font-size: 13px;
  color: var(--text-secondary);
}
.board__end {
  display: flex;
  align-items: center;
  gap: 14px;
  justify-self: end;
}
.board__from {
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--text-muted);
  white-space: nowrap;
}
.board__from b { color: var(--accent); font-weight: 700; }
.board__arrow {
  font-size: 20px;
  color: var(--accent);
  transition: transform 0.2s ease;
}
a.board__row:hover .board__arrow { transform: translateX(4px); }

/* A sold-out night stays on the board. A gap reads as a mistake, and a
   struck date is a scarcity signal doing real work. */
.board__row--gone .board__date-num,
.board__row--gone .board__city { color: var(--text-muted); text-decoration: line-through; }
.board__row--gone .board__venue { color: var(--text-muted); }
.board__gone {
  font-family: var(--font-mono);
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--danger);
  border: 1px solid var(--danger);
  padding: 3px 8px;
  border-radius: var(--radius);
  white-space: nowrap;
}

.hero-pb__foot {
  display: flex;
  align-items: center;
  gap: 28px;
  flex-wrap: wrap;
  margin-top: 32px;
}
.hero-pb__cta { font-size: 18px; padding: 15px 34px; }

/* One orchestrated arrival: the board ticks in a row at a time, the way a
   real departures board flips. Nothing else on the page animates on load. */
@media (prefers-reduced-motion: no-preference) {
  /* Tied to the board scrolling into view rather than to page load. The board
     now sits below the fold, so a load-triggered stagger had always finished
     playing by the time anyone could see it -- the signature motion was being
     spent on an empty screen. */
  .board.reveal .board__row { opacity: 0; transform: translateY(10px); }
  .board.reveal.visible .board__row {
    animation: board-in 0.34s ease forwards;
    animation-delay: calc(var(--row-i, 0) * 80ms);
  }
}
@keyframes board-in {
  to { opacity: 1; transform: none; }
}

@media (max-width: 860px) {
  .hero-pb { padding: 48px 0 44px; }
  .hero-pb__inner { padding: 0 18px; }
  /* Four columns cannot hold a venue name at this width. Date and city take
     the first line, venue and price the second, so every row is the same
     height and nothing wraps mid-word. */
  .board__head { display: none; }
  .board__row {
    grid-template-columns: auto 1fr;
    grid-template-areas:
      "date city"
      "venue end";
    gap: 6px 14px;
    padding: 16px 16px;
    min-height: 0;
  }
  .board__date  { grid-area: date; }
  .board__city  { grid-area: city; font-size: 22px; }
  .board__venue { grid-area: venue; }
  .board__end   { grid-area: end; justify-self: end; }
  .board__date-num { font-size: 24px; }
  .hero-pb__foot { gap: 18px; }
  .hero-pb__cta { width: 100%; justify-content: center; }
}

/* ============================================================
   HERO BACKDROP — three layers, worst case first
   `atmos` is CSS and always runs; the still fades over it; the
   video fades over both, but only once it can actually play.
   A video that never arrives therefore costs nothing on screen,
   which is the failure the reference site was showing live.
   ============================================================ */
.hero-pb { position: relative; overflow: hidden; isolation: isolate; }
.hero-pb__media { position: absolute; inset: 0; z-index: 0; }

/* A 5.8KB still of the hero at 320x180, embedded in the stylesheet itself.

   The poster is only 24KB but it is still a network request, and on a cold
   load it loses that request. Its preload link sits at byte 19,873 of the
   HTML and the six @font-face rules sit at byte 20,177 -- the same TCP
   segment -- so the browser launches 86KB of fonts and this 24KB image
   together and then splits one slow-start connection between them. Measured
   at 190KB/s the poster lands in 0.29s alone and 0.99s against the fonts,
   before the 917KB video takes its share. A phone recording showed the
   result: page fully painted, wordmark already in its display font, and the
   hero still a smear for 1.75 seconds. The fonts won the race and the
   picture lost -- and every one of those fonts is font-display: swap, so
   nothing was waiting on them anyway.

   So the picture no longer races. At 320px wide it is 5.8KB, it travels
   inside the critical CSS, and it paints with the text on the very first
   frame. 320 across a 384px-wide phone, under the veil and the grain, reads
   as the photograph rather than as a placeholder. It was 64x36 and ~900
   bytes, and that is what the recording caught as the blur.

   It sits on the media layer, beneath the veil and the video, so the real
   poster and then the video simply cover it. Nothing has to fade or swap. */
.hero-pb__media {
  background-image: url(data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD//gAQTGF2YzYxLjE5LjEwMAD/2wBDAA4KCw0LCQ4NDA0QDw4RFiQXFhQUFiwgIRokNC43NjMuMjI6QVNGOj1OPjIySGJJTlZYXV5dOEVmbWVabFNbXVn/wgALCAC0AUABAREA/8QAGgAAAgMBAQAAAAAAAAAAAAAAAAECAwQFBv/aAAgBAQAAAAHkOMq5KqLROJZFuMiEknEFLQwgRJNEZKqJIUhzYpFUJkU0paAIuJrlkSargSEOQ3FqrVfgARK9SvmshqlmhGRVAalFzQRlF7qsk4pqd8Z6LI45WFUE1XEklJE10+pXyijJJD0Om6M7GUX1JJVwGgADb6DRDBlXMdcLt9mOMZia0UpRVKNUaExEt516KeWUTlTPbZTBTSkaskq0qUbLMQ3BXdPN0OJuhhe6fKNt8VQ1ZCWvBKhKIa5zz2XRjR09aKMl9EsNF8bLtnMqL6nfmlCAB04LLG41dUpVuXPLPRnltxbtdK5YAWShEaNujLXUur6K7NzzdqhyOZiho21Yu1Zn5ucAlKKk7ZS0aI0dLsMpwPo2C4HI1yDn9Rwp5oAx6rIWZ9VTnf6KwI5LL5hX5hFcZ3GdYQanoslEcoqR0ezYFeazSBHztF9ZVBwhZXJ4txIBoYdXrMIY3vaa4nNRISiVwVS1SaBiYvRbpVleaG25ThxOcAKMRKdefWkiRGQj020zvNU563ojx+IAAwlBZ74jbjNJ3em0RxRKIx07bnwuOJiaaE4kWSU4sOr3JQw2PNCrd0Q5XnmmAgE50phOLkzV3rzze6+oW6518/gtMBCCTpGSHBhP0WqzFw+nHBTq69cuVzgbAAi3UDYAOezraLcVFeGlmm6GOUgWaIgJ1gAxk77bt2rnwjhzlt106qQCvMAD/8QAKhAAAgIBAwQDAQACAgMAAAAAAAECEQMQEiEEIDAxEyJBMhQjBSQzNEL/2gAIAQEAAQUC0fa/DRtKGjaUUV4ELxPtfhXc5FjTXehD8L7X4bLLLLG9MUOct7e5aM/IxNqNqP3WXa/DWtF6RjYuDLPjuWjPxa/tCZuQ+eyy/Cnph6ayOKCMvSY5nxRNtGRaeuyOFyJYWhaPSzcWIX8R99j8nTRuUdGZfbRVmSOyc2mq0gRJz+s1p+6P+PxafvY9cMbMkKk+NWq1Ubk28EYdXkTXUR+L/LheaSlijllE+V6KLY4SjrCxRo/D90fr8iL+ySa7HrH1NbolFaUUQ4nnx/I/8aRkxf8AWcJXgh9cmGA4SRCNu9pJ8aRl9SELlPG4H6WSg0P1AqiycrLLL7ErKPS3CmfVj2m2xpoxStSkhVeRRZeyJE/bMvte5xVw4N5GIpGS4T3G4eWzcKdHz3q+5Lhl8XzZFjdPHciHTtmSCgSpqEI3VueGSbIkpfeU9F728GPFjm9kUmmnlW6HgfdCVDG4ji9en6WeUx4440OO4dwe6zHChE8cZrL0/wAR/wDc/wC6sjhZGKSyQVejHSg6Z6MsNr8HscWhRbPinfwSRGLb+OIlFDjFjwRZ0vSxj2N0ZG5uC+Npp6umZ+n+FqFtJLSxtU028a+llk+Y99MhFpcMhcH8jMktxGX0eQUmWzG3Ka7csSCsrskrU1smuRplm5Ca0fI9w3cNaeii2KFd/Akjgs6TmS7crMLuHb18Knj/AJvSxxiypRNxvEuXFM2I9F8G43G43G4tFo4ODg4ODpo1HVMZL1gfd1buW0+xuo3o3o3I3I3I3o+Q3lOWqRWlllljZZbNxZ0v/rrR6TfEuFCSuL7HydZ/fBx20bTabTaKTRk51vtvW9elW3FpJ09xks+zP5UZ0KSqDt6dfxDvsvS9Pxn52XreuGG/Ktc3uyrP5JSRZg+01DY1p/yF+ar8nRVu0bo3Ockj0SIRVu4vpVwMs63/AMHkQvb8fTP/AHpiZLlKU8c49Qi+GQG0KRZJ0t3HWP8A0+SPg9Fll6Y5bckWJlmfAspKNOPEGZL3cmHJKBHJuTlypcdZPdLWmU/Ai+yyy+5KyEnFxnai9M2FZD0MyP7Fik0RySNzGuJN2u18a32RdeNexEHU75JScpPTL/ekRC0n3S8H/8QALBAAAQIFAwQBAwUBAAAAAAAAAQARAhAgITEwQFESIkFhcTJQkTNCYIGh0f/aAAgBAQAGPwL+AX+zuUXFtTG86fsTx/hfQE8HaUQ2NDLKxfROyfih6BzN5g0ibaLlWxWBymF38q7Musg5ZNcKIg2XKtKyvISuUYeKRTfXh+VZcLohkXuu2xWF6rPcAuRQPiQT6WUfJnhYXarp0xWU7X0LTd8pjX52HK7rKyHUnEsPO1X6n+LpgJiKvsGllOJvH2wpoQ0imKYBOZdwTgvC6CMr2XKcWkGm/g6dgsJ3AT8eVe6wFx8LK6ou4+KekrD09cP0lXpsEBM6RCMwrTENQKelijDwrLGnjWi+KjWIx+6nDfCy4lZetl1ioo1QvhdpWNzDVhWqHxPKzLwsVdX51hCZiTq9BnAfe6hFAXtEUGEp8zgPjdHmm0y4TIxHzR7B3QoMMSZ5lXneX97oGh8RJioR6lkr/qzZZkUIR43dqHGarT97wTL7j//EACUQAAMAAgICAgIDAQEAAAAAAAABESExEEFRYSBxgZEwobHR4f/aAAgBAQABPyFcdToW/knCcIJCRm+H2Mgg28jMdDRDr5j0Lh7OhjFx0Ojv+EuKdy8dlKdSExAaYZcF4XxmwLQh7Fo74SILkWvmvNFwp+BXIw9JFXoMvNnwo1EENK8b0D4rjdi+RDUfM4T9D8SlvQ2omWIN0Z9RksYzZsSo3MQvDwua4IPgo2dhO02loS3xP0+DUdc9crXFNhaxmVPQ3rftCOHqWmQbs2h4NDmsL4kKq0+3CBI4+XQlIluCYbMee3l65fPPXF+eWhdChhoRezsgJMCstrYnksOx+D4hOhE4PTJTBoe+HoMRoe3y3MrAsIPfCGbcpbHF0+xHUbVE4JUbty5RtoYlNkoFFV9kPSy6I6XsvaygJltG8ceGP1htvZn0FNTHFGJeGXWnoTvmMc2LkOpr+xJ7Hr0zwhm3LpIhntQ031zxtFleB281IhoWV0JzymnsUk6cr7HKeWXUqR6T0Qb+DoY5caioOyUFMPYyyTIm5n8FY2H0Zi+wRZGvJkiDDb7DuFhBu/GxM2PfK0Rg9kYe6MUbPPY3TWBdiGCf1MHQYBqm2sCbw58iorqLSuDTgat8vCMse0aEdwZZdmgU/wA/A2WWmPDLrND5diia6I0rV0X4RltFtnUGs0LZYeByjwL8C38NxC9kN0YN7ybZMGH+GaGouxRkkSe9kCcdi9UEMmfZoLsZm29kJB8McsFyXeEN3YtUtZ/bK2yeq+MDFUf0YNaYl/lZX8hafCVcH5RE1bOIiomkNp9Z4h09u39CNMGKRH2M0vAjEXYYIikv32MTQn2jfvP74maJUZCCr+w86UyGt08lwsTVJYxrXZiX8AxJtEqMModxjLdJBUXsVEa/0E5239s1KPwX8ZfQaYdM/b6WEaZ3wlvLIFST1DEf9Bbj9coiNVMsMbdrwzIZijGCiQYZejIwgw8pDjAwsXzpTplw1KWzkgumsMfgf6Gxal4Et10oMbicFG06yxYodHZ0Y3D7iicvZG+zvjQlyamX7tC9VE9hx0S3g8zjB7FmqObKs218MFqcalCf/QlOylS6ZfX9lIuxo9FQqdkku9oMLns/QRN9Y57GMbpmD+0PEMtmnhHtk8+eQtQVdpoeV2QzJLY2C/OhK3kx4GLV4eGPeNEpREk+CPBB6hFfNm42x+R+R5wbn4EquKU4PWeSECfFKMkMEWDD/Fja+X0Lu5n3j8/H4GP0HQtJNj3yQVeB+gvpxTHV2XwV5L8mdvf+mKybMBMh9hnntI0zZVCZtiOrrsdLGL4F8j6FF9/0P6/Qd9D6I+p9Rz0J5cHSw+F4N8pD8OEh8uxWxD4koHZw/wADa5H4Yk9KLPQ1VErZq4aiwIuyoXFKUpeSiDjfBIyi8UQxsSmx8vFNr+hY0+GO1MTzK+hsNzJFtToMVaGWQJopJbGvlcIz9hcv+FiH+obwuUN8aG78J32whNLUM9Q7SG6jSSwdW3bGlU4s5AdpPwO02iEzBcJdPQgvm/h1waDbm8aLRfDFemZeBLrRuQTyNrAhK7TqY52GnDXswKa6ExtH4FTehb/EU2y5KX+DY3ovx2PAooorHL/JXvPAkhJTB35Ky40Yp0g42lOOhrZr8vRy7k68GsQc6bvAM0rN/ZGR+yPkI18bw2T0+Kyy/LIzbHgzSdE6KZgn+x3BjEPxE+2MtsoM9IqtpDqkryM6r9HsY5aTRmKXhSlLv41oXB0HZSCeBcNhOQ04b+hfgNwil7+SY+aP/9oACAEBAAAAEDAz51vvWWvz2yGiDktRj8lUssUVfctC5AuoN2iXLgyFvRWR6FlWgjHsg5jNo1wVNqrYAUl03cCQXiHr4GzzCI4y7jZ3l0XBUkp9FCEFSN5Am5zeGoOD2nB5XlFmw31m37lJDFhHes5+h3RSGPGRBRhv6cH/xAAnEAEAAgICAgIBBQEBAQAAAAABABEhMUFREGFxkYEgobHB0eHw8f/aAAgBAQABPxDwGVFzg8HUGfGiWgTqXFLcS4FW5imdQRpg3lBxLmKCxaIcTFdj8ynl8RqkZXhI3BNZumkNs3QjeaS3UuPHpB8jqbeQxKrxpCWixhHpLjk4hSaamKovuIxbmBomhlLMYubeHU2IqJQ4p2myZQ6RIUpKke/A7hHXi78ah4YUIo6mYd+D6RdywlZiMBwFLD3GViweyYiQ8FlwjBp9zWfyzVEoZZ60TMGDKJHmLiYfNFiO4ajqbx3Be5cA4gwR3HLGoNS4qGG8oI2S2BB8jcBsY+wRzq22GmxxK8WBa+pc6OWGKD5nwm4Crb34GppNSYCVD4lZjYZaWYDJ9SiDneohy/UNLpCJmWBmB5uFpEdw1OEqLHn4EamKzHAC10QERbSfyzVD8kwGOVv2OPxB0h6tbga1w6gxRrdTbCwiVcvw6Wi1leX+TLGnC9VcagpHJDU2JxlTevURLyNX7ilrhtYRabwGYH2J+0SVlCcy+nBL8X4aVL8DUGJBqA4Y0+YIQKai2i8IWx7S5dbgbhNQpgXxFEc3CuoYbMXXgh7KJbksM0uFgU8iAXDax3ruMFhBiCI2rt/iBQ+IbF9xQQOkzXTN3zDT4bTb52wVUXBssG9kVU+EVFfmVOGdV4u8TTEB+YKVBRSvk7l5uacFe7EmsUNwV4rJiW74aRKPpYbRlIJV/wCM3LjUv3mI2le1guSdzYa53XhqAdEWY1gP9iogU9CdzWQzYmnhr+l/eOPwgs9mVSQhAUxHQUw3+YJmIiubfIqZeRFLJhlWeoPm7g9jFgW+onwMDYaoEW/MQLnQOSGe9NCOJSztQ7+Ygym13fuWntdv5MU18BNr/I8D1lQs+yOzHtzK8b0VhiVUCtMdqtXFkiBFhPxF0oHDUvmC6FwwqRqhVTc+oiUImnSVzSHZzn+4H1hsexLAjZmesIKoLUfoqKOo8DMwWbPA0x4RiaSChtLRiwb6/uW9S2wKmKgPHEIX8qVOiluw/wBRs0vyo5+5QAj7jdZrVCDCuluXjuwCPqucmLhUYwsVBuoCLnB/MARWpSi0H2RBDllJ0LyY7jDCtvODV+Co3KUsWjiNsAE1qAbBKT/IabeWmzxiENte5QYcKXeioilvuKnYwzH/AOQhlBslLjMYpVrwYYNAwgxSLh5IWQY0RTs+JbW45kINR96hrG7QFsEOigP0jmJovcpQJbt4YgRg71BlvVEFtXdSn/I18VMj8H9/1GAnJNzCdxEqVeWMEy3Mvu2nuZJdtxi7bZu/dxKszihPRcBAKK1/9ihVlZ8eD9IwcTbzg3ePFBpbuMmEMfH/ACJKH7cMvJQeazKDGbi+/wAzYA7H+QUbGnuUjTmxR+nHywTonyvy8ytgTWNBIHYa3EptYAJyw/2gNUaXjhgfDG702FbTPfzAtVYi/VYZXDq9S6JdBExYftgFg97mWECGmIrAcnqCnBIu8xJOxlKjQ0zAuSz09fqGDiPMvCXqBitdS55N1FaqOLQi6OyV0leAsHX4O5bW5dYPqMa7gE5wUtUOl92DKkhG9B7ruX/pN59SssVFNmiWlZkP77mDBPP+p3KbWyJkIuZUsCkSxjWXx5to+PcNWbNoYlTNegjV3ZOUP1LjbRqGRGA1UY8KXzLisJuHWYb3VnpP1XLVDUb8RkpPAwCsp1xFTLxnTCNiRRcBdoEAFg6SXhPvidFlqxT5JmYuzVcsABg49RL9yco7RHgLeYC9T+Ayse3OR8QuEHwh+wjuFC+XUDwekepYDb8jiW6b4RPKfKRFTZ9NxdTV7Ki2w/hgvDL5FBzFsQzA5r4hXHCiI0lPnNwWVrXhnNXLxMglvcNqr7qb2qutXOB+ICD9fmAXX8Wxcw+6gRMVNph3dE9jAOX6gqmNTXC5hJThjryYRLqGcf5Z7LCsw3CFzLUrNXE/gP6T6hDGsZjVSGIWgfEblAHSTKfSIli6HCSt+4IwELrH+o7BJIJohpl9C0X2+eI1AADolTLrCiyUtkWxfUDhANBDquHha3iFN2gyiz4lrmGGdoepllfrUsvL8S3s+kbJiqvI2f8AuoBJ/wDIWMeco6qKlepambsz3sgJLlAtYW4gwq4P2gu7BPOXLHKju7MTZAdq4mAR9k7KndFP+oEh7yUEDGgPuMruNV1QEvYRK4fAo3ATUH4QbwPqDeqfEdwCMch9QaAHZlRuDN3HQQdiC5ov80sgtRrlmYKXbKnExg5wjBolrcC1hHeJSo4YNWwcHBAVmK1dM/4m8RYHtvMcQ5QDSPiCBkOWLWufqOWv0gs2xwB+JRzBtwnxSxVJVzRoeIQhC4Bz78GHhwkJcuy6hGI5nM6l2DHi44CifvyxpYwjkw9kqC4bfzNxLhKumBal4Kw7gcu4qDtsmANa3B+4PxG6xMDpK+GKfFDEtrLlgu5a4qoqXDcFQXqK7l7ywFLjSal4lXUsw1xMG/jwG2MG8upUUQFQBcXONQ8JS2vwWWVAZMSxyRSqTecfUKRLaigaqourhj6uRYtBU6Yy5QnbVTpKzKmFW28lSjYfJFrefiO5a0HF4YtFQit+Ial48MvxcucWK7+5kBVxivzLg1BbKCoZYIffgB5szAizg5/qMVd8zA0eqn974jUgIepQ7FtgQfy9sVrA4q1SsKUpt7JBAW2eTl+/4lrHY8QCVZkwy1BtvyxE7YtE1R34vws2ly5cW0TOvqEymIoHxhC1gh9xSzB5qiV28N31M3itEs3KRbJhrT3FXFVjb7r3ASX2tP7wBsC+4O5VH7P4gEtjrj8dQ9XCqaqGMNp3MvepQO5YnCrigXLT7yv8QgfwSj4nSXFi5ly2W9S4sj8xGzwZlLJcMwxAVCZMs909090W5ZleSj7HEFlCkIPcSW7GXXA0IwOmOlqpLg4wD9o0rqSmGlyhQ/IQMvzCMKBaRDUy9+PUC690S01Kztf4fzK+GdJHrYJwz1Mc9Gpdy5cuX7i2yi0EKcvFy4I8gpbc+blzebCLRRhWcQxIOeE+Y4be5VhjUyrdD3HwSFS/BmIldIN7ni5bzLE8PW0drAAAXeCKopLUXcSZAxZSCzGV0lHUx1GBUCnUvLy0vLS0RFpHD+o8HkqEsmM0lwKFSdkKcYyMxGq8HBiCsMQ1GF8Iq5bAOajzGlsV1pbLZbLZcJakd/q0n//Z);
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
}
.hero-pb__inner { position: relative; z-index: 1; }

.hero-pb__atmos { position: absolute; inset: 0; overflow: hidden; }
/* Two glows rather than one: independent drifts read as depth, a single
   moving blob reads as a screensaver. Gold leads, cyan answers. */
.hero-pb__atmos::before,
.hero-pb__atmos::after {
  content: '';
  position: absolute;
  inset: -25%;
  border-radius: 50%;
  filter: blur(90px);
  will-change: transform;
}
.hero-pb__atmos::before {
  background: radial-gradient(closest-side, rgba(232, 185, 35, 0.52), transparent 70%);
  animation: atmos-a 22s ease-in-out infinite;
}
.hero-pb__atmos::after {
  background: radial-gradient(closest-side, rgba(70, 181, 209, 0.34), transparent 70%);
  animation: atmos-b 30s ease-in-out infinite;
}
@keyframes atmos-a {
  0%, 100% { transform: translate3d(-14%, -8%, 0) scale(1.00); }
  50%      { transform: translate3d(10%, 6%, 0)   scale(1.22); }
}
@keyframes atmos-b {
  0%, 100% { transform: translate3d(18%, 10%, 0)  scale(1.15); }
  50%      { transform: translate3d(-8%, -6%, 0)  scale(0.95); }
}

.hero-pb__still,
.hero-pb__video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
/* Opaque from the start, so the poster paints the moment it lands.

   An element at zero opacity paints nothing, poster included -- so gating this
   on a JS class would hide the very still frame whose job is to fill the hero
   before the video arrives. The poster IS the video's first frame, so there is
   no flash to guard against and nothing to transition. */
.hero-pb__video { opacity: 1; }

/* UNCONFIRMED — a candidate for the iOS freeze, not a proven fix.

   On the device the video reports `playing` and its `currentTime` advances,
   while the screen shows nothing new for roughly 2.2 seconds. Frames are being
   decoded and not painted, which on iOS is the signature of a <video> that has
   not been promoted to its own compositing layer: WebKit then paints each frame
   through the page's layer on the main thread, and here that is a 960x540 source
   scaled onto roughly 1170x2532 device pixels with a veil and a grain span
   stacked over it.

   `translateZ(0)` asks for that layer explicitly. It is the standard mitigation
   for this symptom, it costs nothing if the layer already existed, and it is a
   single declaration to remove.

   Not reproducible from this machine: Chrome under 6x CPU throttling never shows
   the freeze, and the engine that does is WebKit. Confirming it needs a
   `?vdebug=1` recording where STALL comes back small. Until then this stays
   labelled as a guess. */
.hero-pb__video {
  transform: translateZ(0);
  backface-visibility: hidden;
}

/* Sits directly beneath the video, above the inline placeholder. Same box, same
   fit, so the handover from still to moving image lands pixel-for-pixel. */
.hero-pb__poster {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* Left-weighted: the type is on the left and has to stay readable, the motion
   is worth seeing on the right. Flat dimming would cost both. */
.hero-pb__veil {
  position: absolute;
  inset: 0;
  background:
    linear-gradient(90deg,
      rgba(10,10,10, calc(var(--veil, 0.75) * 1))    0%,
      rgba(10,10,10, calc(var(--veil, 0.75) * 0.82)) 45%,
      rgba(10,10,10, calc(var(--veil, 0.75) * 0.42)) 100%),
    linear-gradient(180deg,
      rgba(10,10,10, 0.55) 0%,
      rgba(10,10,10, 0)    28%,
      rgba(10,10,10, 0.80) 100%);
}

@media (prefers-reduced-motion: reduce) {
  /* The glows still give the hero depth; they just stop moving. */
  .hero-pb__atmos::before,
  .hero-pb__atmos::after { animation: none; }
}

@media (max-width: 860px) {
  /* A phone reads the type over the whole width, so the horizontal falloff
     has nowhere useful to go -- dim evenly instead. */
  .hero-pb__veil {
    background:
      linear-gradient(180deg,
        rgba(10,10,10, 0.62) 0%,
        rgba(10,10,10, calc(var(--veil, 0.75) * 0.78)) 40%,
        rgba(10,10,10, 0.88) 100%);
  }
  .hero-pb__atmos::before,
  .hero-pb__atmos::after { filter: blur(60px); }
}

/* A beam crossing the hero. The two glows give depth; this gives movement you
   notice without looking for it. Kept to 6% white so it never fights the type.

   This was removed once on a bad diagnosis -- a tab that looked frozen was
   actually a backgrounded tab where a requestAnimationFrame probe of mine
   could never resolve. Restored. The one true note in that mistake: animating
   `background-position` repaints rather than composites, so if the hero ever
   needs to get cheaper on low-end phones, this is the first thing to cut. */
.hero-pb__atmos {
  background:
    linear-gradient(105deg,
      transparent 30%,
      rgba(255, 255, 255, 0.06) 46%,
      rgba(232, 185, 35, 0.10) 52%,
      transparent 68%);
  background-size: 260% 100%;
  animation: atmos-sweep 14s linear infinite;
}
@keyframes atmos-sweep {
  from { background-position: 130% 0; }
  to   { background-position: -70% 0; }
}
@media (prefers-reduced-motion: reduce) {
  .hero-pb__atmos { animation: none; background-position: 40% 0; }
}

/* ============================================================
   RESTRAINT PASS
   The page had six colour events above the fold -- gold strip,
   gold wordmark, gold flag, gold arrows, gold button, cyan
   eyebrow -- and an accent that appears everywhere stops meaning
   anything. Gold is now rationed: once in the hero, and again in
   the board where it marks the next show and the prices.
   ============================================================ */

/* --- The strip at the top stops shouting --- */
/* It was a solid gold band and the first thing anyone saw. A hairline rule
   with small muted type says the same words without claiming the top of the
   page for itself. */
.marquee-wrap {
  background: var(--bg);
  border-bottom: 1px solid var(--border);
  color: var(--text-muted);
}
.marquee-track { color: var(--text-muted); }
.mq-sep { color: var(--accent); opacity: 0.55; }

/* --- Header, quieter --- */
.cart-btn { border-color: var(--border); }
.cart-btn__count { background: transparent; color: var(--text-secondary); border: 1px solid var(--border-card); }
.lang-btn { font-size: 11px; }

/* --- Hero --- */
.hero-pb {
  /* Room to breathe. The old section was packed to its padding on every side;
     the whole complaint about the page was density, and density is mostly the
     absence of this. */
  min-height: 78vh;
  display: flex;
  align-items: center;
  padding: 104px 0 96px;
  border-bottom: 1px solid var(--border);
}
.hero-pb__inner { width: 100%; }

.hero-pb__eyebrow {
  /* Was cyan, which made a third colour fight the gold and the white for a
     line of 12px type. It is a label; it can be a label. */
  color: var(--text-secondary);
  letter-spacing: 0.28em;
  margin: 0 0 22px;
}
.hero-pb__name {
  /* White. Making the biggest element on the page the accent colour spends
     the accent on something that was already the loudest thing in the room. */
  color: var(--text-primary);
  font-size: clamp(58px, 13.5vw, 196px);
}
.hero-pb__tour {
  /* The one place the accent appears in this section. */
  color: var(--accent);
  font-family: var(--font-display);
  font-size: clamp(22px, 3.4vw, 44px);
  letter-spacing: 0.04em;
  margin: 14px 0 0;
}
.hero-pb__cta {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  margin-top: 38px;
  padding: 16px 40px;
  background: #FFFFFF;
  color: #000000;
  border: 2px solid #FFFFFF;
  border-radius: var(--radius);
  font-family: var(--font-display);
  font-size: 19px;
  letter-spacing: 0.06em;
  text-decoration: none;
  transition: background 0.2s ease, color 0.2s ease;
}
.hero-pb__cta:hover,
.hero-pb__cta:focus-visible { background: transparent; color: #FFFFFF; }
.hero-pb__cta:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }

/* Colour behind type means the backdrop and the headline compete for the same
   attention. Greyscale lets the picture carry atmosphere and the type carry
   meaning -- the single biggest reason the reference reads clean. */
.hero-pb__still,
.hero-pb__video { filter: grayscale(1) contrast(1.05); }

/* The CSS backdrop drops to a hold behind a quieter hero: still alive, no
   longer the thing you look at. */
.hero-pb__atmos::before { background: radial-gradient(closest-side, rgba(232, 185, 35, 0.30), transparent 70%); }
.hero-pb__atmos::after  { background: radial-gradient(closest-side, rgba(255, 255, 255, 0.10), transparent 70%); }

/* --- Trust band --- */
.trust-band {
  background: var(--bg);
  border-bottom: 1px solid var(--border);
  padding: 20px 24px;
}
.trust-band__list {
  max-width: var(--max-width);
  margin: 0 auto;
  padding: 0;
  list-style: none;
  display: flex;
  justify-content: center;
  gap: 46px;
  flex-wrap: wrap;
}
.trust-band__item {
  display: flex;
  align-items: center;
  gap: 9px;
  font-family: var(--font-mono);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--text-muted);
}
.trust-band__icon { width: 17px; height: 17px; flex: 0 0 17px; color: var(--accent); opacity: 0.8; }

/* --- Tour board section --- */
.tour-board-section { padding: var(--section-pad) 0; }
.tour-board-section .board { margin-top: 32px; }

@media (max-width: 860px) {
  .hero-pb { min-height: 0; padding: 64px 0 56px; }
  .hero-pb__eyebrow { letter-spacing: 0.22em; margin-bottom: 16px; }
  .hero-pb__cta { width: 100%; margin-top: 30px; }
  .trust-band__list { gap: 14px 22px; }
  .trust-band__item { font-size: 10px; }
}

/* ============================================================
   HEADER — wordmark centred
   Three tracks rather than space-between: the outer two each take
   an equal share and the middle takes only what it needs, so the
   wordmark sits on the true centre of the page no matter how wide
   the nav or the utilities get. space-between would centre it
   between its neighbours, which is a different place.
   ============================================================ */
.header { border-bottom: 1px solid var(--border); }
.header-inner {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  gap: 16px;
  padding: 18px 24px;
}
/* Columns are assigned explicitly rather than left to source order. Hiding the
   nav on a phone removes it from the grid flow, which promoted the wordmark
   into the first track and pinned it to the left edge -- centred on desktop,
   flush left on mobile. Named columns keep the middle the middle. */
.header-nav { grid-column: 1; justify-self: start; gap: 26px; }
.header-logo {
  grid-column: 2;
  justify-self: center;
  font-size: 26px;
  letter-spacing: 0.10em;
  text-decoration: none;
}
.header-utils {
  grid-column: 3;
  justify-self: end;
  display: flex;
  align-items: center;
  gap: 14px;
}

@media (max-width: 860px) {
  .header-inner { padding: 14px 18px; }
  /* The links live in the overlay on a phone. */
  .header-nav { display: none; }
  /* So does the language switch, and it has to: `1fr auto 1fr` can only
     centre the middle track while both outer tracks fit their share. With the
     switch in the header the right side measured 217px against a 126px share,
     so it took the space from the left track and dragged the wordmark off
     centre -- 105px into a 390px frame. Cart and burger alone come to ~86px,
     under the share, and the tracks come out equal. The switch is already in
     the overlay, so nothing is lost. */
  #lang-switcher { display: none; }
  .header-utils { gap: 10px; }
  .header-logo { font-size: 22px; }
}

/* The language switch carried its layout in an inline style attribute, which
   beats any stylesheet rule no matter how specific -- so the media query that
   hides it on a phone was being silently ignored while the rule beside it,
   hiding the nav, worked. Same trap as its colours, which had to be fixed in
   paintSwitcher for the same reason. Its layout lives here now. */
.lang-switch {
  display: flex;
  align-items: center;
  font-family: var(--font-mono);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.12em;
}
@media (max-width: 860px) {
  .lang-switch { display: none; }
}

/* The seam only exists once there is something to separate. At rest the
   header shares the hero's black and shows no edge at all; `is-stuck` brings
   the hairline in as soon as content starts moving under it. The announcement
   strip loses its border outright -- it scrolls away, so its line was only
   ever visible in the one state that is supposed to be seamless. */
.marquee-wrap { border-bottom: 1px solid transparent; }
.header {
  border-bottom: 1px solid transparent;
  transition: border-color 0.2s ease, background-color 0.2s ease;
}
.header.is-stuck { border-bottom-color: var(--border); }

/* ============================================================
   STICKY BUY BAR
   Pinned to the bottom for the whole scroll past the hero. Off
   the layout entirely until then -- transformed out rather than
   display:none so it can slide rather than appear.
   ============================================================ */
.sticky-bar {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 90;
  background: var(--bg);
  border-top: 1px solid var(--border);
  transform: translateY(100%);
  opacity: 0;
  visibility: hidden;
  transition: transform 0.25s ease, opacity 0.25s ease, visibility 0.25s;
}
.sticky-bar.is-on { transform: none; opacity: 1; visibility: visible; }
.sticky-bar__inner {
  max-width: var(--max-width);
  margin: 0 auto;
  padding: 12px 24px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
}
.sticky-bar__title {
  font-family: var(--font-display);
  font-size: 16px;
  letter-spacing: 0.08em;
  color: var(--text-primary);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.sticky-bar__cta {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  padding: 11px 26px;
  background: var(--accent);
  color: #000;
  border-radius: var(--radius);
  font-family: var(--font-display);
  font-size: 16px;
  letter-spacing: 0.06em;
  text-decoration: none;
  transition: background 0.2s ease;
}
.sticky-bar__cta:hover { background: var(--accent-deep); }
.sticky-bar__cta:focus-visible { outline: 2px solid #fff; outline-offset: 2px; }

@media (prefers-reduced-motion: reduce) {
  .sticky-bar { transition: opacity 0.2s ease, visibility 0.2s; transform: none; }
}
@media (max-width: 860px) {
  .sticky-bar__inner { padding: 10px 16px; gap: 12px; }
  /* The tour name is the first thing to go: on a phone the button is the
     point of the bar and the name is already in the header above it. */
  .sticky-bar__title { display: none; }
  .sticky-bar__cta { width: 100%; justify-content: center; }
}

/* --- The board loses its box --- */
/* A 2px frame around a list of rows draws a shape the content does not have.
   The horizontal rules already separate the rows, and the top rule under the
   column heads already says where the table starts. */
.board {
  border: none;
  background: transparent;
  border-radius: 0;
}
.board__head {
  background: transparent;
  padding-left: 0;
  padding-right: 0;
  border-bottom-width: 2px;
}
.board__row { padding-left: 0; padding-right: 0; }
a.board__row:hover,
a.board__row:focus-visible { background: var(--accent-soft); }

/* The wordmark is a link home, and at 22px its text box is 35px tall -- under
   the 44px a finger needs. Padding rather than a bigger font: the size is a
   design decision, the target is an accessibility one, and they do not have to
   be the same number. */
.header-logo {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
}

/* Absolute-centring the wordmark on mobile was tried and reverted: taking it
   out of the grid while `justify-self: center` still applied left it pinned by
   its left edge, 98px off centre at 390px and overlapping the cart by 91px --
   worse in every way than the ~16px the grid gives. The grid stays. */

/* ============================================================
   TRANSPARENT HEADER
   The header sat in normal flow with a solid background, so it
   read as a black band laid across the top of the design. Making
   it transparent alone would have changed nothing -- behind it was
   the page colour, not the hero -- so the hero is pulled up
   underneath it and pads itself back down by the same amount. The
   content lands exactly where it did; the backdrop now runs to the
   top of the viewport and the header floats on it.
   `--header-h` is measured in JS, because it differs between
   desktop and mobile and a guessed value would leave a seam.
   ============================================================ */
.header { background: transparent; }
.header.is-stuck { background: var(--bg); }

.hero-pb {
  margin-top: calc(-1 * var(--header-h, 73px));
  padding-top: calc(104px + var(--header-h, 73px));
}

@media (max-width: 860px) {
  .hero-pb { padding-top: calc(64px + var(--header-h, 72px)); }
}

/* ============================================================
   HEADER: OUT TO THE EDGES, AND THE CART UNBOXED
   ============================================================ */
/* The header was capped at --max-width and centred, which left the nav and the
   utilities floating in from the screen edges on a wide monitor while the rest
   of the page used the same cap. Furniture belongs at the edges; content stays
   in the measure. The wordmark still lands dead centre because the grid's two
   outer tracks share whatever is left equally. */
.header-inner {
  max-width: none;
  padding-left: 40px;
  padding-right: 40px;
}

/* The cart was a bordered pill with a filled circle counting to zero -- two
   pieces of chrome around one icon. It is an icon now. */
.cart-btn {
  border: none;
  padding: 6px;
  gap: 0;
  position: relative;
  color: var(--text-secondary);
}
.cart-btn:hover,
.cart-btn:focus-visible { border-color: transparent; color: var(--accent); }
.cart-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.cart-btn.flash { border-color: transparent; color: var(--accent); }

/* When there is something in the basket the count returns as a small gold
   marker on the corner of the icon, rather than a second object beside it. */
.cart-btn__count {
  position: absolute;
  top: -1px;
  right: -3px;
  width: 16px;
  height: 16px;
  min-width: 16px;
  font-size: 9px;
  line-height: 16px;
  text-align: center;
  border: 2px solid var(--bg);
}
.cart-btn__count[hidden] { display: none; }

@media (max-width: 860px) {
  .header-inner { padding-left: 18px; padding-right: 18px; }
  .cart-btn { padding: 6px; }
  .cart-btn__count { width: 15px; height: 15px; min-width: 15px; line-height: 15px; font-size: 9px; }
}

/* ============================================================
   ONE GUTTER FOR THE HOME PAGE
   The header went out to a 40px edge but the hero, the board and
   the sticky bar were still inside the centred --max-width
   container, so the wordmark began about 344px in while the nav
   began at 40px. Two different left edges on one screen reads as
   a mistake even when you cannot name it. They share one now.

   Deliberately scoped to these three rather than to `.container`
   itself: the cap exists so that reading text -- the FAQ, event
   info, policy pages -- does not run to 1,800px lines. Big type
   and a table are what can take the full width.
   ============================================================ */
.hero-pb__inner,
.tour-board-section .container,
.sticky-bar__inner {
  max-width: none;
  padding-left: 40px;
  padding-right: 40px;
}

@media (max-width: 860px) {
  .hero-pb__inner,
  .tour-board-section .container,
  .sticky-bar__inner {
    padding-left: 18px;
    padding-right: 18px;
  }
}

/* Optical alignment for the wordmark.
   Every box on the page starts at the 40px gutter, but a box is not where a
   letter starts. Archivo Black carries a left side bearing on the P worth
   about 0.069em, so at 196px the painted glyph sat 14px inside its own box --
   metrically perfect, visibly indented against the nav and the eyebrow above
   and below it. Measured at 58px, 120px and 196px the bearing held at
   0.069-0.071 of the em, so the correction is expressed in `em` and stays
   right across the whole clamp rather than only at the size it was eyeballed
   at. Large display type is the one place this is worth doing; at 12px the
   bearing is a fraction of a pixel and nobody sees it. */
.hero-pb__name { margin-left: -0.069em; }

/* ============================================================
   WORDMARK GLITCH
   A chromatic split: two copies of the word drawn from `data-text`,
   offset in opposite directions, each clipped to a horizontal band
   and blended with `screen` so the colours add against the black
   rather than covering it.

   `steps(1)` is the whole trick. Tweened, the copies slide and it
   reads as a soft blur; stepped, they jump between states and it
   reads as a signal breaking up.

   Two departures from the reference, both deliberate. Its loop is
   2.2s, which puts a glitch on screen almost constantly; ours is 6s
   and quiet for the first 88% of it, so the effect is something you
   catch rather than something you sit through -- this page spent a
   whole pass earning its restraint. And the split is gold and cyan,
   the palette's own two hues, rather than borrowed ones.
   ============================================================ */
.hero-pb__glitch {
  position: relative;
  display: block;
  animation: pb-jit 2.6s steps(1) infinite;
}
.hero-pb__glitch::before,
.hero-pb__glitch::after {
  content: attr(data-text);
  position: absolute;
  left: 0;
  top: 0;
  width: 100%;
  mix-blend-mode: screen;
  pointer-events: none;
  opacity: 0;
}
.hero-pb__glitch::before {
  color: var(--accent);
  animation: pb-glitch-a 2.6s steps(1) infinite;
}
.hero-pb__glitch::after {
  color: var(--tier-mid);
  animation: pb-glitch-b 2.6s steps(1) infinite;
}

/* Read the reference's own keyframes rather than guessing at the rhythm, and
   the difference was not the effect, it was the density: the word itself is
   never still -- nine twitch states across the loop, two of them carrying a
   small skew -- and the colour split fires in four separate windows per cycle
   rather than one. One burst every six seconds reads as a rendering fault. Four
   a second-and-a-bit reads as a signal.

   The skew is the detail that sells it. Pure translation reads as a picture
   being nudged; a degree and a half of shear reads as a scanline tearing. */
@keyframes pb-jit {
  0%, 100% { transform: translate(0, 0); }
  5%       { transform: translate(-2px, 1px); }
  11%      { transform: translate(2px, -1px); }
  22%      { transform: translate(-1px, 0); }
  34%      { transform: translate(2px, 1px) skew(-1.4deg); }
  38%      { transform: translate(0, 0); }
  55%      { transform: translate(-2px, -1px); }
  68%      { transform: translate(1px, 1px) skew(1.1deg); }
  73%      { transform: translate(0, 0); }
  86%      { transform: translate(-1px, 1px); }
}
/* A and B fire on the same schedule and lean opposite ways -- that opposition
   is what makes the pair read as one split image rather than two ghosts. */
@keyframes pb-glitch-a {
  0%        { opacity: 0.9; transform: translate(-6px, -2px); clip-path: inset(14% 0 56% 0); }
  7%        { opacity: 0.9; transform: translate(5px, 2px);   clip-path: inset(54% 0 20% 0); }
  12%       { opacity: 0; transform: translate(0, 0); }
  28%       { opacity: 0.9; transform: translate(-5px, 1px);  clip-path: inset(36% 0 42% 0); }
  34%       { opacity: 0; }
  52%       { opacity: 0.9; transform: translate(6px, -2px);  clip-path: inset(6% 0 80% 0); }
  58%       { opacity: 0; }
  76%       { opacity: 0.9; transform: translate(-5px, 2px);  clip-path: inset(66% 0 12% 0); }
  84%       { opacity: 0.9; transform: translate(3px, -1px);  clip-path: inset(22% 0 62% 0); }
  88%, 100% { opacity: 0; transform: translate(0, 0); }
}
@keyframes pb-glitch-b {
  0%        { opacity: 0.75; transform: translate(6px, 2px);   clip-path: inset(28% 0 46% 0); }
  7%        { opacity: 0.75; transform: translate(-5px, -2px); clip-path: inset(4% 0 74% 0); }
  12%       { opacity: 0; transform: translate(0, 0); }
  28%       { opacity: 0.75; transform: translate(5px, -1px);  clip-path: inset(60% 0 18% 0); }
  34%       { opacity: 0; }
  52%       { opacity: 0.75; transform: translate(-6px, 2px);  clip-path: inset(44% 0 34% 0); }
  58%       { opacity: 0; }
  76%       { opacity: 0.75; transform: translate(5px, -2px);  clip-path: inset(12% 0 70% 0); }
  84%       { opacity: 0.75; transform: translate(-3px, 1px);  clip-path: inset(70% 0 8% 0); }
  88%, 100% { opacity: 0; transform: translate(0, 0); }
}

@media (prefers-reduced-motion: reduce) {
  .hero-pb__glitch,
  .hero-pb__glitch::before,
  .hero-pb__glitch::after { animation: none; }
  .hero-pb__glitch::before,
  .hero-pb__glitch::after { opacity: 0; }
}

/* ============================================================
   MARQUEE
   Four identical copies in a flex row, translated 0 to -50%.
   Half the track is exactly two copies, so the restart lands on
   an identical frame and the seam is invisible. Linear timing:
   any easing would make the belt visibly slow down and speed up
   once per loop, which is the one thing that gives it away.
   ============================================================ */
.marquee {
  background: var(--bg);
  border-top: 1px solid var(--border);
  border-bottom: 1px solid var(--border);
  overflow: hidden;
  padding: 16px 0;
}
.marquee__track {
  display: flex;
  width: max-content;
  animation: marquee-scroll var(--marquee-speed, 26s) linear infinite;
  will-change: transform;
}
.marquee__item {
  font-family: var(--font-hero);
  font-size: clamp(22px, 2.6vw, 34px);
  line-height: 1.1;
  letter-spacing: 0.01em;
  text-transform: uppercase;
  color: var(--text-primary);
  white-space: nowrap;
  padding-right: 0.4em;
}
/* Alternating rather than uniform: a single colour repeating reads as
   wallpaper, and the alternation is what makes the belt's movement legible. */
.marquee__item:nth-child(even) { color: var(--accent); }

@keyframes marquee-scroll {
  from { transform: translateX(0); }
  to   { transform: translateX(-50%); }
}

@media (prefers-reduced-motion: reduce) {
  /* Stopped, not hidden -- the words still say something. */
  .marquee__track { animation: none; }
}

@media (max-width: 860px) {
  .marquee { padding: 12px 0; }
}

/* The generated loop. Sits over the CSS atmosphere, under the veil. Not
   desaturated like a photographic backdrop would be -- it is already dim and
   already made of this palette's own two hues, so there is nothing for it to
   fight with. */
.hero-pb__loop {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* Grain over the loop.
   This started out baked into the WebP and cost 7MB: noise is incompressible
   and it destroys the frame-to-frame prediction an animated format lives on.
   As an overlay it is free, it is sharp at any screen density instead of being
   scaled up with the video, and it does the same job -- breaking up the
   banding that lossy encoding leaves across a smooth dark gradient, and
   reading as film rather than as a gradient. */
.hero-pb__grain {
  position: absolute;
  inset: 0;
  pointer-events: none;
  opacity: 0.28;
  mix-blend-mode: overlay;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='160' height='160' filter='url(%23n)' opacity='0.55'/%3E%3C/svg%3E");
  background-size: 160px 160px;
}
@media (prefers-reduced-motion: reduce) { .hero-pb__grain { opacity: 0.2; } }

/* ============================================================
   HEADER UTILITIES: SEARCH + COUNTRY
   ============================================================ */
.icon-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 44px;
  min-height: 44px;
  padding: 6px;
  background: none;
  border: none;
  color: var(--text-secondary);
  cursor: pointer;
  transition: color 0.2s ease;
}
.icon-btn:hover, .icon-btn:focus-visible { color: var(--accent); }
.icon-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* --- Country / currency --- */
.country-form { position: relative; }
.country-btn {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  min-height: 44px;
  padding: 6px 8px;
  background: none;
  border: none;
  color: var(--text-secondary);
  font-family: var(--font-mono);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.12em;
  cursor: pointer;
  transition: color 0.2s ease;
}
.country-btn:hover, .country-btn:focus-visible { color: var(--accent); }
.country-btn__caret { font-size: 9px; }
.country-panel {
  position: absolute;
  top: calc(100% + 8px);
  right: 0;
  z-index: 120;
  width: 260px;
  max-height: 340px;
  overflow-y: auto;
  background: var(--bg-raised);
  border: 1px solid var(--border-card);
  padding: 8px;
}
.country-panel[hidden] { display: none; }
.country-search {
  width: 100%;
  padding: 9px 10px;
  margin-bottom: 6px;
  background: var(--bg-input);
  border: 1px solid var(--border);
  color: var(--text-primary);
  font-family: var(--font-body);
  font-size: 13px;
}
.country-search:focus { outline: none; border-color: var(--accent); }
.country-list { list-style: none; margin: 0; padding: 0; }
.country-list li[hidden] { display: none; }
.country-option {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  width: 100%;
  padding: 9px 10px;
  background: none;
  border: none;
  color: var(--text-secondary);
  font-family: var(--font-body);
  font-size: 13px;
  text-align: left;
  cursor: pointer;
}
.country-option:hover, .country-option:focus-visible { background: var(--accent-soft); color: var(--text-primary); }
.country-option__code { font-family: var(--font-mono); font-size: 10px; color: var(--text-muted); }
.country-option.is-current { color: var(--accent); }

/* --- Search overlay --- */
.search-overlay {
  position: fixed;
  inset: 0;
  z-index: 200;
  background: rgba(10, 10, 10, 0.94);
  display: flex;
  align-items: flex-start;
  justify-content: center;
  padding: 18vh 24px 0;
}
.search-overlay[hidden] { display: none; }
.search-overlay__close {
  position: absolute;
  top: 18px;
  right: 22px;
  min-width: 44px;
  min-height: 44px;
  background: none;
  border: none;
  color: var(--text-secondary);
  font-size: 22px;
  cursor: pointer;
}
.search-overlay__close:hover { color: var(--accent); }
.search-overlay__form {
  display: flex;
  gap: 10px;
  width: 100%;
  max-width: 680px;
}
.search-overlay__input {
  flex: 1 1 auto;
  min-width: 0;
  padding: 16px 18px;
  background: transparent;
  border: none;
  border-bottom: 2px solid var(--border-card);
  color: var(--text-primary);
  font-family: var(--font-display);
  font-size: 28px;
  letter-spacing: 0.02em;
}
.search-overlay__input:focus { outline: none; border-bottom-color: var(--accent); }
.search-overlay__input::placeholder { color: var(--text-muted); }

/* --- Search results page --- */
.search-page { padding: var(--section-pad) 0; }
.search-page__form { display: flex; gap: 10px; margin: 24px 0 32px; max-width: 620px; }
.search-page__input {
  flex: 1 1 auto;
  min-width: 0;
  padding: 12px 14px;
  background: var(--bg-input);
  border: 1px solid var(--border-card);
  color: var(--text-primary);
  font-family: var(--font-body);
  font-size: 15px;
}
.search-page__input:focus { outline: none; border-color: var(--accent); }
.search-page__count, .search-page__empty {
  font-family: var(--font-mono);
  font-size: 12px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--text-muted);
  margin-bottom: 18px;
}
.search-page__empty b { color: var(--text-primary); }
.search-results { border-top: 2px solid var(--border-card); }
.search-row {
  display: flex;
  align-items: center;
  gap: 16px;
  padding: 18px 0;
  border-bottom: 1px solid var(--border);
  text-decoration: none;
  transition: background 0.2s ease;
}
.search-row:hover { background: var(--accent-soft); }
.search-row__title { flex: 1 1 auto; font-family: var(--font-display); font-size: 22px; color: var(--text-primary); }
.search-row__price { font-family: var(--font-display); font-size: 20px; color: var(--accent); }
.search-row__type { font-family: var(--font-mono); font-size: 10px; text-transform: uppercase; color: var(--text-muted); }
.search-row__arrow { color: var(--accent); font-size: 18px; }
.search-pagination { display: flex; gap: 14px; margin-top: 26px; font-family: var(--font-mono); font-size: 12px; }
.search-pagination a { color: var(--text-secondary); text-decoration: none; }
.search-pagination a:hover { color: var(--accent); }
.search-pagination [aria-current] { color: var(--accent); }

@media (max-width: 860px) {
  .search-overlay { padding: 12vh 18px 0; }
  .search-overlay__input { font-size: 22px; padding: 12px 14px; }
  .search-overlay__form { flex-direction: column; }
  .country-panel { width: min(280px, calc(100vw - 36px)); }
}

/* The country sits under the city, quieter. Two lines rather than one comma-
   separated string so the city stays the thing the eye lands on -- the row is
   scanned for "is my city here", and the country is confirmation, not the
   answer. The grid gains a row instead of a column, so the venue and price
   columns keep their width. */
.board__place { display: block; min-width: 0; }
.board__city { display: block; }
.board__country {
  display: block;
  font-family: var(--font-mono);
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--text-muted);
  margin-top: 3px;
}
/* Eight rows rather than three: a little less air each, so the board still
   fits a screen without becoming a scroll. */
.board__row { min-height: 68px; }

@media (max-width: 860px) {
  .board__country { font-size: 9px; margin-top: 2px; }
}

/* The row's call to action, in words.
   Outlined rather than filled: there are eight of these stacked down the page,
   and eight solid gold blocks would out-shout the dates and the cities they
   exist to serve. It fills on hover, so the row being pointed at is the one
   that looks clickable. */
.board__cta {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 96px;
  min-height: 36px;
  padding: 0 16px;
  border: 1px solid var(--accent);
  border-radius: var(--radius);
  font-family: var(--font-mono);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--accent);
  white-space: nowrap;
  transition: background 0.2s ease, color 0.2s ease;
}
a.board__row:hover .board__cta,
a.board__row:focus-visible .board__cta {
  background: var(--accent);
  color: #000;
}

@media (max-width: 860px) {
  /* On the two-line mobile row it sits bottom-right with the price; keep it
     at 44px so it is still a comfortable target even though the whole row is
     the real link. */
  .board__cta { min-width: 84px; min-height: 44px; font-size: 10px; }
}

/* ============================================================
   MOBILE: HEADER CENTRING AND THE BOARD ROW
   ============================================================ */
@media (max-width: 860px) {
  /* --- Header ---
     `1fr auto 1fr` only centres the middle track while BOTH outer tracks fit
     inside their share. Measured at 390px: the share is about 114px, and the
     right cluster with the currency button in it came to roughly 154 -- so it
     took the difference out of the empty left track and dragged the wordmark
     about 40px off centre. Two changes balance it: the burger moves into the
     left track, and the currency control moves into the menu overlay, where a
     237-country list belongs anyway rather than hanging off a phone header. */
  .menu-btn { grid-column: 1; justify-self: start; }
  .header-utils { gap: 6px; }
  .country-form { display: none; }
  .header-logo { grid-column: 2; }

  /* --- Board row ---
     The old two-line grid let the date column size itself, so the `NEXT` badge
     made the first row wider than the rest and every city started at a
     different x. Cities are what the eye runs down when it looks for its own,
     and they were staggered. The city now owns the left edge on every row and
     the date is pinned right, so the column is straight whatever else is in
     the row. */
  .board__row {
    grid-template-columns: 1fr auto;
    grid-template-areas:
      "city date"
      "venue venue"
      "cta cta";
    gap: 2px 12px;
    padding: 18px 0;
    align-items: start;
  }
  .board__place  { grid-area: city; }
  .board__date   { grid-area: date; justify-self: end; align-items: baseline; }
  .board__venue  { grid-area: venue; margin-top: 2px; }
  .board__end    { grid-area: cta; justify-self: stretch; margin-top: 12px; }

  .board__city     { font-size: 26px; line-height: 1.05; }
  .board__country  { margin-top: 1px; }
  .board__date-num { font-size: 20px; }
  .board__venue    { font-size: 12px; color: var(--text-muted); }

  /* Full width: it is the only action on the row, and a phone should not make
     anyone aim for it. */
  .board__cta { width: 100%; min-width: 0; }
  .board__end { display: flex; gap: 10px; }
  .board__from { align-self: center; }
}

/* The overlay's country control is a native <select>: the header's custom
   panel is the right shape for a pointer, but on a phone the OS picker is
   better than anything a page can draw -- it is searchable, it is the control
   people already know, and it cannot be scrolled off screen. */
.nav-overlay__country {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-top: 20px;
  width: min(320px, 100%);
}
.nav-overlay__country-label {
  font-family: var(--font-mono);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.14em;
  color: var(--accent);
}
.nav-overlay__country select {
  flex: 1 1 auto;
  min-width: 0;
  min-height: 44px;
  padding: 10px 12px;
  background: var(--bg-input);
  border: 1px solid var(--border-card);
  border-radius: var(--radius);
  color: var(--text-primary);
  font-family: var(--font-body);
  font-size: 14px;
}
.nav-overlay__country select:focus { outline: none; border-color: var(--accent); }

/* ============================================================
   HERO PERFORMANCE, AND THE WORDMARK ON A PHONE
   ============================================================ */
/* Greyscale is baked into both encodes now. As a CSS filter it was a
   full-frame GPU pass on every frame of playback -- the single most expensive
   thing on the page while the video ran. */
.hero-pb__video { filter: none; }

/* When a video is playing, everything underneath it is invisible. The two
   90px-blurred glows and the sweep were still animating and still repainting
   the whole hero every frame, behind an opaque rectangle. Off. */
.hero-pb--has-video.is-video-ready .hero-pb__atmos { display: none; }

@media (max-width: 860px) {
  /* `mix-blend-mode` forces its own compositing layer and makes the compositor
     re-blend the grain against moving video every frame. On a phone that is
     real jank for a texture nobody consciously sees; plain low-opacity grain
     over the top looks near-identical and composites for free. */
  .hero-pb__grain { mix-blend-mode: normal; opacity: 0.14; }

  /* The wordmark was filling the screen edge to edge -- 58px of Archivo Black
     across a 354px measure leaves no air, and a word that touches both gutters
     reads as cramped rather than as big. */
  .hero-pb__name { font-size: clamp(44px, 13vw, 60px); }
  .hero-pb__eyebrow { font-size: 10px; letter-spacing: 0.18em; margin-bottom: 12px; }
  .hero-pb__tour { font-size: clamp(18px, 5.2vw, 24px); }

  /* The button was full width and as tall as a section header. It is one
     action, not a banner. */
  .hero-pb__cta { width: auto; align-self: flex-start; padding: 13px 26px; font-size: 16px; }
  .hero-pb { padding: 52px 0 44px; min-height: 0; }
}

/* ============================================================
   MOBILE HERO: GIVE IT THE SCREEN
   Side by side with the reference the difference was not type
   size, it was proportion. Theirs takes roughly 60% of the phone
   and anchors the type to the bottom of it, so the footage has
   somewhere to be and the words sit on a floor. Ours was about a
   quarter of the screen with everything stacked against the
   header -- the eyebrow was nearly touching the burger, and the
   marquee, the trust strip and TOUR DATES all arrived before the
   first scroll.
   ============================================================ */
@media (max-width: 860px) {
  .hero-pb {
    /* svh is the viewport with the browser bars showing, which is the state
       the page actually loads in. Plain vh is the tall one and would push the
       button under the address bar on first paint. vh first as the fallback
       for anything that does not know svh. */
    min-height: 84vh;
    min-height: 84svh;
    padding: 104px 0 44px;
    align-items: flex-end;
  }

  /* Air between the four things, in proportion to their weight. They were
     evenly and tightly spaced, which reads as a list rather than a hierarchy. */
  .hero-pb__eyebrow { margin-bottom: 16px; }
  .hero-pb__tour    { margin-top: 14px; }
  .hero-pb__cta     { margin-top: 28px; }
}

/* Nothing in the hero animates while the hero is not on screen. The glitch
   repaints large text; the atmosphere blurs and sweeps. Neither is worth a
   single frame of work once the reader has scrolled to the dates. */
.hero-pb.is-offscreen .hero-pb__glitch,
.hero-pb.is-offscreen .hero-pb__glitch::before,
.hero-pb.is-offscreen .hero-pb__glitch::after,
.hero-pb.is-offscreen .hero-pb__atmos,
.hero-pb.is-offscreen .hero-pb__atmos::before,
.hero-pb.is-offscreen .hero-pb__atmos::after {
  animation-play-state: paused;
}

/* Consolidated with the base rule above -- the element is always visible, so
   `.is-ready` no longer carries any opacity of its own. The class stays: the
   hero section still uses it to drop the CSS backdrop once frames exist. */

/* ============================================================
   COOKIE BANNER
   ============================================================

   Fixed to the bottom, so it is on screen at first paint. Its rules are in the
   ALLOW list in tools/critical_css.py and travel inlined in the head -- left to
   arrive with the full stylesheet, the first thing a visitor would see is an
   unstyled stack of four buttons across the hero.

   Gold appears once here, on ACCEPT ALL. The reference this was modelled on
   tints the policy link with its accent too; one accent event per section is
   the house rule, and the button is the better place to spend it, so the link
   is white and underlined instead. */
.cookie-bar {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 300;
  background: var(--bg-raised);
  border-top: 1px solid var(--border-card);
}
.cookie-bar[hidden] { display: none; }

.cookie-bar__inner {
  max-width: 1100px;
  margin: 0 auto;
  padding: 20px 40px 22px;
}

.cookie-bar__text {
  margin: 0 0 18px;
  color: var(--text-secondary);
  font-family: var(--font-body);
  font-size: 14px;
  line-height: 1.55;
}
.cookie-bar__link {
  color: var(--text-primary);
  text-decoration: underline;
  text-underline-offset: 3px;
}
.cookie-bar__link:hover,
.cookie-bar__link:focus-visible { color: var(--accent); }

/* --- the preferences panel ------------------------------------------- */
.cookie-bar__prefs {
  margin: 0 0 20px;
  padding: 4px 0 2px;
  border-top: 1px solid var(--border);
}
.cookie-bar__prefs[hidden] { display: none; }

.cookie-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  margin-top: 16px;
}
.cookie-row__label {
  font-family: var(--font-mono);
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--text-primary);
}
.cookie-row__fixed {
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--text-muted);
}
.cookie-row__note {
  margin: 5px 0 0;
  color: var(--text-muted);
  font-family: var(--font-body);
  font-size: 12.5px;
  line-height: 1.5;
}

/* A switch rather than a checkbox: it is a real <button role="switch">, so it
   is keyboard-operable and announced as on or off without a label hack. 44px
   of height comes from the padding, not from the visible track. */
.cookie-switch {
  flex: 0 0 auto;
  width: 46px;
  height: 44px;
  padding: 13px 0;
  background: none;
  border: none;
  cursor: pointer;
}
.cookie-switch::before {
  content: '';
  display: block;
  width: 46px;
  height: 18px;
  background: var(--bg-input);
  border: 1px solid var(--border-card);
  transition: background 0.15s ease, border-color 0.15s ease;
}
.cookie-switch__dot {
  position: relative;
  display: block;
  width: 14px;
  height: 14px;
  margin-top: -16px;
  margin-left: 2px;
  background: var(--text-muted);
  transition: transform 0.15s ease, background 0.15s ease;
}
.cookie-switch[aria-checked="true"]::before {
  background: var(--accent-soft);
  border-color: var(--accent);
}
.cookie-switch[aria-checked="true"] .cookie-switch__dot {
  transform: translateX(26px);
  background: var(--accent);
}
.cookie-switch:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* --- buttons ---------------------------------------------------------- */
.cookie-bar__actions {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
}
.cookie-btn {
  flex: 1 1 auto;
  min-height: 44px;
  padding: 13px 18px;
  background: transparent;
  border: 1px solid var(--border-card);
  border-radius: var(--radius);
  color: var(--text-primary);
  font-family: var(--font-display);
  font-size: 16px;
  letter-spacing: 0.08em;
  cursor: pointer;
  transition: border-color 0.2s ease, color 0.2s ease, background 0.2s ease;
}
.cookie-btn[hidden] { display: none; }
.cookie-btn:hover,
.cookie-btn:focus-visible { border-color: var(--text-primary); }
.cookie-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

.cookie-btn--accent {
  background: var(--accent);
  border-color: var(--accent);
  color: #000000;
}
.cookie-btn--accent:hover,
.cookie-btn--accent:focus-visible {
  background: var(--accent-deep);
  border-color: var(--accent-deep);
  color: #000000;
}

@media (max-width: 860px) {
  .cookie-bar__inner { padding: 12px 16px 14px; }
  .cookie-bar__text { font-size: 13px; line-height: 1.5; margin-bottom: 10px; }

  /* One row of three, as on the reference.

     An earlier note here claimed Bebas could not be read three-across at
     390px and wrapped them to two rows. Measured, that cost 100px of button
     against 44px -- 28% of an 844pt screen for the whole banner, which is a
     third of the page given over to a question, before the visitor has seen
     anything they came for. It reads fine at 13px with the tracking eased
     off; the reference does exactly this.

     44px of height stays: that is the touch-target floor, not a style. */
  .cookie-bar__actions { gap: 8px; }
  .cookie-btn {
    flex: 1 1 0;
    min-width: 0;
    padding: 12px 4px;
    font-size: 13px;
    letter-spacing: 0.04em;
  }
  .cookie-btn--accent { flex-basis: 0; }

  /* The panel is opened deliberately, so it may be taller than the bar -- but
     not needlessly. */
  .cookie-row { margin-top: 13px; }
  .cookie-row__note { font-size: 12px; margin-top: 4px; }
}

@media (prefers-reduced-motion: reduce) {
  .cookie-switch::before,
  .cookie-switch__dot,
  .cookie-btn { transition: none; }
}

/* A tier with no price yet. Sized down from the big numeral so an absent price
   does not shout louder than a real one, and kept in the muted colour so it
   reads as a state rather than as an offer. */
.ticket-price--tba {
  font-family: var(--font-mono);
  font-size: 13px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-muted);
}

/* The button for a tier with nothing behind it. Deliberately not the gold
   primary: gold means "you can buy this", and you cannot. */
.btn--pending {
  background: transparent;
  border: 1px solid var(--border-card);
  color: var(--text-muted);
  cursor: not-allowed;
}
.btn--pending:hover { background: transparent; color: var(--text-muted); }

/* ============================================================
   SEATING MAP — line art
   ============================================================

   This was three saturated fills -- gold, cyan, teal -- with 62 block codes
   like E11 and A12 stamped across it. Beside the reference it read as a board
   game rather than as a venue plan.

   The reference is line art: thin strokes on black, one accent colour, no
   block codes, zone names only. This matches that discipline and spends the
   section's one gold event on the zone the visitor is actually pointing at. */
.seatmap__block polygon,
.seatmap__block rect {
  fill: transparent;
  stroke: rgba(255, 255, 255, 0.30);
  stroke-width: 1.4;
  transition: fill 0.12s ease, stroke 0.12s ease;
}
.seatmap__block[data-zone-now="floor"] rect {
  fill: rgba(232, 185, 35, 0.07);
  stroke: rgba(232, 185, 35, 0.55);
}
.seatmap__block[data-zone-now="gold"] polygon { fill: rgba(232, 185, 35, 0.14); stroke: rgba(232, 185, 35, 0.6); }
.seatmap__block[data-zone-now="cat1"]  polygon { fill: transparent; stroke: rgba(255, 255, 255, 0.45); }
.seatmap__block[data-zone-now="upper"] polygon { fill: transparent; stroke: rgba(255, 255, 255, 0.22); }
.seatmap__block[data-zone-now="soldout"] polygon {
  fill: rgba(255, 255, 255, 0.03);
  stroke: rgba(255, 255, 255, 0.10);
}

/* The one accent event: whatever the pointer or the legend is on. */
.seatmap__block:hover polygon,
.seatmap__block.is-active polygon,
.seatmap__block:hover rect,
.seatmap__block.is-active rect {
  fill: rgba(232, 185, 35, 0.30);
  stroke: var(--accent);
  stroke-width: 2;
}

.seatmap__stage rect {
  fill: rgba(255, 255, 255, 0.06);
  stroke: rgba(255, 255, 255, 0.45);
  stroke-width: 1.4;
}
.seatmap__stagelbl,
.seatmap__zonelbl {
  fill: var(--text-secondary);
  font-family: var(--font-mono);
  font-size: 15px;
  letter-spacing: 0.16em;
  text-anchor: middle;
  pointer-events: none;
}
.seatmap__stagelbl {
  font-size: 13px;
  fill: var(--text-secondary);
  letter-spacing: 0.22em;
}

/* ============================================================
   SEAT PLAN — a real venue image with price bubbles over it
   ============================================================

   The reference site does not draw arenas, and neither should this: one flat
   plan per venue, with the interactive part sitting on top in HTML. Bubbles are
   positioned in percentages so they stay put as the image scales. */
.seatplan { position: relative; display: block; line-height: 0; }
.seatplan__img {
  display: block;
  width: 100%;
  height: auto;
  border: 1px solid var(--border);
}
.seatplan__bubble {
  position: absolute;
  transform: translate(-50%, -50%);
  display: inline-flex;
  flex-direction: column;
  align-items: center;
  gap: 1px;
  min-height: 0;
  padding: 3px 8px;
  background: #FFFFFF;
  color: #000000;
  border: 1px solid #FFFFFF;
  border-radius: 999px;
  font-family: var(--font-mono);
  font-size: 8.5px;
  font-weight: 700;
  letter-spacing: 0.08em;
  line-height: 1.15;
  white-space: nowrap;
  cursor: pointer;
  transition: background 0.15s ease, border-color 0.15s ease, transform 0.15s ease;
}
.seatplan__bubble:hover,
.seatplan__bubble:focus-visible,
.seatplan__bubble.is-active {
  background: var(--accent);
  border-color: var(--accent);
  transform: translate(-50%, -50%) scale(1.06);
}
.seatplan__bubble:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }
.seatplan__bubble-price { font-size: 9.5px; }

/* Below the fold of a phone the plan is small; the bubbles must not swamp it. */
@media (max-width: 860px) {
  /* The plan is only ~350px wide on a phone. A bubble that reads comfortably on
     a desktop covers three blocks here, so it shrinks with the drawing. */
  .seatplan__bubble { padding: 2px 6px; font-size: 7px; letter-spacing: 0.04em; }
  .seatplan__bubble-price { font-size: 8px; }
}

/* ============================================================
   TICKET PURCHASE — one form, one cart
   ============================================================ */
.tp { padding: 72px 0; border-top: 1px solid var(--border); }
.tp--alt { background: var(--bg-soft); }
.tp__eyebrow {
  margin: 0 0 10px; color: var(--accent);
  font-family: var(--font-mono); font-size: 11px;
  letter-spacing: 0.18em; text-transform: uppercase;
}
.tp__title { margin: 0 0 26px; font-size: clamp(30px, 5vw, 52px); color: var(--text-primary); }

.tp__facts { display: flex; flex-wrap: wrap; gap: 26px 34px; margin-bottom: 34px; }
.tp__fact { display: flex; flex-direction: column; gap: 4px; }
.tp__fact-k {
  color: var(--text-muted); font-family: var(--font-mono);
  font-size: 10px; letter-spacing: 0.16em; text-transform: uppercase;
}
.tp__fact-v { color: var(--text-primary); font-family: var(--font-display); font-size: 21px; }

.tp__price { font-family: var(--font-display); font-size: 46px; color: var(--text-primary); margin-bottom: 22px; }

.tp__legend {
  display: block; padding: 0; margin-bottom: 12px;
  color: var(--text-muted); font-family: var(--font-mono);
  font-size: 11px; letter-spacing: 0.16em; text-transform: uppercase;
}
.tp__cats, .tp__addons { border: 0; padding: 0; margin: 0 0 26px; }

/* A row is the whole hit area, not just the little circle. */
.tp__cat, .tp__addon {
  display: flex; align-items: center; gap: 14px;
  min-height: 56px; padding: 12px 16px; margin-bottom: 8px;
  border: 1px solid var(--border-card); cursor: pointer;
  transition: border-color 0.15s ease, background 0.15s ease;
}
.tp__cat:hover, .tp__addon:hover { border-color: var(--text-secondary); }
.tp__cat:has(input:checked) { border-color: var(--accent); background: var(--accent-soft); }
.tp__cat:has(input:disabled) { opacity: 0.45; cursor: not-allowed; }
.tp__cat input, .tp__addon input { width: 18px; height: 18px; accent-color: var(--accent); flex: 0 0 auto; }
.tp__cat-name, .tp__addon-name {
  flex: 1 1 auto; color: var(--text-primary);
  font-family: var(--font-display); font-size: 19px; letter-spacing: 0.04em;
}
.tp__addon-name { font-family: var(--font-body); font-size: 15px; letter-spacing: 0; }
.tp__cat-price { color: var(--text-primary); font-family: var(--font-display); font-size: 19px; }
.tp__addon-price { color: var(--accent); font-family: var(--font-mono); font-size: 13px; }

.tp__qty { margin-bottom: 26px; }
.tp__qty-input {
  width: 92px; min-height: 48px; padding: 10px 14px;
  background: var(--bg-input); border: 1px solid var(--border-card);
  color: var(--text-primary); font-family: var(--font-display); font-size: 19px;
}
.tp__qty-input:focus { outline: none; border-color: var(--accent); }

/* The number the visitor is actually agreeing to pay. The reference shows only
   a unit price and leaves quantity and three add-ons to be done in the head. */
.tp__total {
  display: flex; align-items: baseline; justify-content: space-between;
  padding: 16px 0 18px; margin-bottom: 18px;
  border-top: 1px solid var(--border);
}
.tp__total-k {
  color: var(--text-muted); font-family: var(--font-mono);
  font-size: 11px; letter-spacing: 0.16em; text-transform: uppercase;
}
.tp__total-v { color: var(--text-primary); font-family: var(--font-display); font-size: 32px; }
.tp__err { margin: 12px 0 0; color: var(--danger); font-size: 14px; }

.tp__pending { padding: 26px; border: 1px solid var(--border-card); }
.tp__pending-lbl {
  margin: 0 0 6px; color: var(--text-secondary);
  font-family: var(--font-display); font-size: 22px; letter-spacing: 0.06em;
}
.tp__pending-note { margin: 0; color: var(--text-muted); font-size: 14px; }

@media (min-width: 900px) {
  /* Facts and artwork on the left, the form on the right -- the reference's
     split, which keeps the price and the button in one column. */
  .tp__grid { display: grid; grid-template-columns: 1fr 1fr; gap: 56px; align-items: start; }
  .tp__head { grid-column: 1; }
  .tp__form, .tp__pending { grid-column: 2; }
}

/* ============================================================
   CONCERT DATE PAGE
   ============================================================

   Two columns on a desktop, matching the reference: artwork and plan on the
   left, everything needed to decide and buy on the right, both starting at the
   same line. This was three stacked sections before, which is why it never
   looked like the reference however good the pieces got.

   On a phone the order is set explicitly -- video, facts and countdown, ticket
   form, then the plan -- so nobody scrolls past an arena diagram to reach the
   buy button. */
.dp { padding: 40px 0 80px; }
.dp--alt { background: var(--bg-soft); }
.dp__grid { display: flex; flex-direction: column; gap: 28px; }

.dp__video { position: relative; overflow: hidden; background: #000; aspect-ratio: 16 / 9; }
.dp__video-el { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; filter: grayscale(1) contrast(1.05); }
.dp__video-veil { position: absolute; inset: 0; background: linear-gradient(180deg, rgba(0,0,0,.35) 0%, rgba(0,0,0,.15) 45%, rgba(0,0,0,.7) 100%); }
.dp__video-cap { position: absolute; inset: 0; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 6px; text-align: center; padding: 20px; }
.dp__video-city { margin: 0; font-family: var(--font-display); font-size: clamp(38px, 9vw, 72px); color: #fff; letter-spacing: 0.04em; }
.dp__video-date { margin: 0; font-family: var(--font-body); font-size: 15px; color: #fff; letter-spacing: 0.06em; }
.dp__video-venue { margin: 0; font-family: var(--font-display); font-size: 19px; color: var(--accent); letter-spacing: 0.1em; text-transform: uppercase; }

.dp__eyebrow { margin: 0 0 10px; color: var(--text-muted); font-family: var(--font-mono); font-size: 11px; letter-spacing: 0.18em; text-transform: uppercase; }
.dp__title { margin: 0 0 24px; font-size: clamp(28px, 5.2vw, 46px); color: var(--text-primary); line-height: 1.05; }

.dp__facts { display: flex; gap: 24px; align-items: flex-start; margin-bottom: 24px; flex-wrap: wrap; }
.dp__date { display: flex; flex-direction: column; align-items: center; line-height: 1; }
.dp__date-d { font-family: var(--font-display); font-size: 40px; color: var(--text-primary); }
.dp__date-m { font-family: var(--font-mono); font-size: 11px; letter-spacing: 0.16em; color: var(--text-secondary); text-transform: uppercase; }
.dp__date-y { font-family: var(--font-mono); font-size: 11px; color: var(--accent); }
.dp__fact-cols { display: grid; grid-template-columns: repeat(2, minmax(120px, 1fr)); gap: 14px 28px; flex: 1 1 260px; }
.dp__fact-k { display: block; color: var(--text-muted); font-family: var(--font-mono); font-size: 10px; letter-spacing: 0.16em; text-transform: uppercase; }
.dp__fact-v { display: block; color: var(--text-primary); font-family: var(--font-body); font-weight: 600; font-size: 15px; margin-top: 3px; }

.dp__cd { display: flex; gap: 26px; margin-bottom: 18px; }
.dp__cd-u { display: flex; flex-direction: column; gap: 2px; }
.dp__cd-n { font-family: var(--font-display); font-size: 30px; color: var(--text-primary); line-height: 1; }
.dp__cd-l { font-family: var(--font-mono); font-size: 9px; letter-spacing: 0.16em; color: var(--text-muted); text-transform: uppercase; }

.dp__scarce { display: flex; align-items: center; gap: 8px; margin: 0 0 18px; color: var(--text-secondary); font-family: var(--font-mono); font-size: 11px; letter-spacing: 0.14em; text-transform: uppercase; }
.dp__scarce-dot { width: 8px; height: 8px; border: 2px solid var(--danger); border-radius: 50%; }

.dp__price { font-family: var(--font-display); font-size: clamp(46px, 13vw, 60px); color: var(--text-primary); margin-bottom: 20px; line-height: 1; }
.dp__legend { display: block; padding: 0; margin-bottom: 10px; color: var(--text-primary); font-family: var(--font-display); font-size: clamp(19px, 5vw, 22px); letter-spacing: 0.04em; text-transform: uppercase; }
.dp__cats, .dp__addons { border: 0; padding: 0; margin: 0 0 22px; }
/* Unselected rows carry a hairline, not a box. Five outlined boxes in a column
   read as five buttons competing; the reference frames only the chosen one, and
   the row that is framed is the one you are buying. */
.dp__cat, .dp__addon {
  display: flex; align-items: center; gap: 14px;
  min-height: 60px; padding: 14px 4px; margin-bottom: 0;
  border: 1px solid transparent; border-bottom-color: var(--border);
  cursor: pointer; transition: border-color .15s ease, background .15s ease;
}
.dp__cat:hover, .dp__addon:hover { border-bottom-color: var(--text-secondary); }
.dp__cat:has(input:checked) {
  border-color: var(--accent); background: var(--accent-soft); padding-left: 14px; padding-right: 14px;
}
.dp__cat:has(input:disabled) { opacity: .45; cursor: not-allowed; }
.dp__cat input, .dp__addon input { width: 17px; height: 17px; accent-color: var(--accent); flex: 0 0 auto; }
.dp__cat-name { flex: 1 1 auto; color: var(--text-primary); font-family: var(--font-display); font-size: 18px; letter-spacing: .05em; }
.dp__cat-price { color: var(--text-primary); font-family: var(--font-display); font-size: 18px; }
.dp__addon-name { flex: 1 1 auto; color: var(--text-secondary); font-size: 14px; }
.dp__addon-price { color: var(--accent); font-family: var(--font-mono); font-size: 12px; }

.dp__qty { margin-bottom: 22px; }
.dp__qty-input { width: 88px; min-height: 46px; padding: 9px 13px; background: var(--bg-input); border: 1px solid var(--border-card); color: var(--text-primary); font-family: var(--font-display); font-size: 18px; }
.dp__qty-input:focus { outline: none; border-color: var(--accent); }

.dp__total { display: flex; align-items: baseline; justify-content: space-between; padding: 14px 0 16px; margin-bottom: 16px; border-top: 1px solid var(--border); }
.dp__total-k { color: var(--text-muted); font-family: var(--font-mono); font-size: 11px; letter-spacing: .16em; text-transform: uppercase; }
.dp__total-v { color: var(--text-primary); font-family: var(--font-display); font-size: 30px; }

/* White, like the reference's. The page has already spent its gold on the
   selected category row and the add-on prices. */
.dp__submit { display: block; width: 100%; min-height: 58px; padding: 17px; background: #fff; border: 2px solid #fff; color: #000; font-family: var(--font-display); font-size: 20px; letter-spacing: .08em; cursor: pointer; transition: background .2s ease, color .2s ease; }
.dp__submit:hover:not(:disabled) { background: transparent; color: #fff; }
.dp__submit:disabled { opacity: .5; cursor: wait; }
.dp__err { margin: 10px 0 0; color: var(--danger); font-size: 14px; }

.dp__pending { padding: 22px; border: 1px solid var(--border-card); margin-bottom: 20px; }
.dp__pending-lbl { margin: 0 0 5px; color: var(--text-secondary); font-family: var(--font-display); font-size: 21px; letter-spacing: .06em; }
.dp__pending-note { margin: 0; color: var(--text-muted); font-size: 14px; }

.dp__trust { display: flex; flex-wrap: wrap; gap: 8px 22px; list-style: none; margin: 20px 0 0; padding: 0; }
.dp__trust li { display: flex; align-items: center; gap: 7px; color: var(--text-secondary); font-family: var(--font-mono); font-size: 10.5px; letter-spacing: .12em; text-transform: uppercase; }
.dp__trust-dot { width: 7px; height: 7px; background: var(--accent); }

.dp__note { margin-top: 24px; }
.dp__note-h { margin: 0 0 5px; color: var(--text-primary); font-family: var(--font-display); font-size: 15px; letter-spacing: .1em; text-transform: uppercase; }
.dp__note-h--accent { color: var(--accent); }
.dp__note-b { margin: 0; color: var(--text-muted); font-size: 13.5px; line-height: 1.6; }

.dp__plan-h { margin: 0 0 4px; color: var(--text-primary); font-family: var(--font-display); font-size: 19px; letter-spacing: .08em; text-transform: uppercase; }
.dp__plan-sub { margin: 0 0 14px; color: var(--text-muted); font-size: 13.5px; }
.dp__plan-foot { margin: 12px 0 0; color: var(--text-muted); font-family: var(--font-mono); font-size: 10px; letter-spacing: .12em; text-transform: uppercase; }

/* Phone order, read off the reference: video, then the seat map, then the
   title and the form. An earlier guess here put the form above the map on the
   grounds that nobody should scroll past a diagram to buy -- but the map is how
   the reference expects the category to be chosen, so it comes first. */
.dp__art  { order: 1; }
.dp__plan { order: 2; }
.dp__buy  { order: 3; }

@media (min-width: 960px) {
  .dp { padding: 56px 0 96px; }
  .dp__video { aspect-ratio: 1 / 1; }
  .dp__grid { display: grid; grid-template-columns: 1fr 1fr; gap: 24px 56px; align-items: start; }
  .dp__art  { grid-column: 1; grid-row: 1; }
  .dp__buy  { grid-column: 2; grid-row: 1 / span 2; }
  .dp__plan { grid-column: 1; grid-row: 2; }
}

/* ============================================================
   SEAT PLAN — vector, in the site's own palette
   ============================================================ */
/* ---------- Seat plan, vector ----------------------------------------
   Drawn as a line plan, not as filled slabs. The reference's arena reads as a
   real room because every stand is an outlined box with its seating inside it;
   ours read as thirty identical clickable rectangles because that is what they
   were. Fill is a dark plate, the category is carried by the edge colour and
   by the seat rows, and the stand keeps the number the venue gives it.

   Gold is still rationed: the two premium zones spend it, and the cheaper
   tiers step down through white. Five shades of gold were indistinguishable at
   phone size, which is the other half of why the plan was unreadable. */
.sp { display: block; width: 100%; height: auto; }
.sp__b { cursor: pointer; }

.sp__fill polygon { fill: #101010; transition: fill .12s ease; }
.sp__edge polygon { fill: none; stroke-width: 1.7; stroke-linejoin: round; transition: stroke-width .12s ease, stroke .12s ease; }
.sp__rows line { stroke-width: .95; stroke-dasharray: 3 2.3; stroke-linecap: butt; }

.sp__b[data-zone="gold"]  .sp__fill polygon { fill: #F0C63A; }
.sp__b[data-zone="gold"]  .sp__edge polygon { stroke: #FFE28C; stroke-width: 2; }

.sp__b[data-zone="floor"] .sp__fill polygon { fill: url(#sp-hatch); }
.sp__b[data-zone="floor"] .sp__edge polygon { stroke: #F0C63A; stroke-width: 2.4; }

.sp__b[data-zone="cat1"]  .sp__edge polygon { stroke: #DCA92B; }
.sp__b[data-zone="cat1"]  .sp__rows line    { stroke: #DCA92B; stroke-opacity: .62; }

.sp__b[data-zone="cat2"]  .sp__edge polygon { stroke: #E8E8E8; }
.sp__b[data-zone="cat2"]  .sp__rows line    { stroke: #E8E8E8; stroke-opacity: .48; }

.sp__b[data-zone="cat3"]  .sp__edge polygon { stroke: #8C8C8C; }
.sp__b[data-zone="cat3"]  .sp__rows line    { stroke: #8C8C8C; stroke-opacity: .42; }

.sp__lbl {
  font-family: var(--font-display, Anton, Impact, sans-serif);
  letter-spacing: .04em;
  text-anchor: middle;
  fill: #FFF;
  pointer-events: none;
  paint-order: stroke;
  stroke: #0A0A0A;
  stroke-width: 3.4;
  stroke-linejoin: round;
}
.sp__b[data-zone="gold"]  .sp__lbl { fill: #0A0A0A; stroke: #F0C63A; }
.sp__b[data-zone="floor"] .sp__lbl { fill: #F0C63A; }
.sp__b[data-zone="cat1"]  .sp__lbl { fill: #F2C84A; }
.sp__b[data-zone="cat3"]  .sp__lbl { fill: #C8C8C8; }

/* Hover and selection. The reference lifts one bubble; ours lifts every stand
   in the category, because the visitor is buying a category and not a seat --
   showing one block would understate what they get. */
.sp__b:hover .sp__fill polygon,
.sp__b:focus-visible .sp__fill polygon,
.sp__b.is-hl .sp__fill polygon { fill: #241D08; }
.sp__b:hover .sp__edge polygon,
.sp__b:focus-visible .sp__edge polygon,
.sp__b.is-hl .sp__edge polygon { stroke: #FFD24A; stroke-width: 2.6; }
.sp__b:hover .sp__rows line,
.sp__b.is-hl .sp__rows line { stroke: #FFD24A; stroke-opacity: .55; }
.sp__b:focus-visible { outline: none; }

.sp__b.is-active .sp__fill polygon { fill: #3A2D07; }
.sp__b.is-active .sp__edge polygon { stroke: #FFD24A; stroke-width: 3; }
.sp__b.is-active .sp__rows line    { stroke: #FFD24A; stroke-opacity: .7; }
.sp__b.is-active .sp__lbl          { fill: #FFF; }
.sp__b[data-zone="gold"].is-active  .sp__fill polygon { fill: #FFD24A; }
.sp__b[data-zone="gold"].is-active  .sp__lbl { fill: #0A0A0A; }
.sp__b[data-zone="floor"].is-active .sp__fill polygon { fill: #2E2409; }

/* Once a category is chosen the rest of the room steps back, so the answer to
   "what did I just pick" is the picture itself rather than the form. Pointing
   at a tier does the same thing temporarily -- the whole tier comes forward and
   everything else greys off, which is how you find out that fourteen separate
   stands are all Cat 2 without having to read fourteen labels.

   The hover rule is written with the same specificity as the picked rule and
   placed after it, so pointing always wins over the standing selection. */
.sp__b { transition: opacity .12s ease; }
.sp.is-picked .sp__b:not(.is-active) { opacity: .38; }
.sp.is-hovering .sp__b:not(.is-hl) { opacity: .26; }

.sp__stage rect { fill: #232323; stroke: #FFF; stroke-width: 2.2; }
.sp__stage-t {
  font-family: var(--font-display, Anton, Impact, sans-serif);
  font-size: 19px; letter-spacing: .16em; text-anchor: middle; fill: #FFF;
}

/* One label for a zone drawn as more than one block. It sits outside the block
   groups so it can span them, so it must not swallow clicks meant for the
   block underneath it. */
.sp__lbl--zone { pointer-events: none; transition: opacity .12s ease; }
.sp__lbl--zone.is-dim { opacity: .38; }
.sp__lbl--zone[data-zone="floor"] { fill: #F0C63A; }
.sp__lbl--zone[data-zone="gold"]  { fill: #0A0A0A; stroke: #F0C63A; }

/* ---------- legend ----------
   A swatch has to show the treatment, not just a colour, or it cannot explain
   a plan that distinguishes tiers by outline and hatching. */
.sp-key {
  display: flex; flex-wrap: wrap; gap: 9px 18px;
  margin: 0 0 14px; padding: 11px 13px; list-style: none;
  border: 1px solid rgba(255,255,255,.14);
}
.sp-key li {
  display: flex; align-items: center; gap: 8px; cursor: pointer;
  color: var(--text-secondary, #B8B8B8); font-family: var(--font-mono, monospace);
  font-size: 10px; letter-spacing: .12em; text-transform: uppercase;
  transition: color .12s ease;
}
.sp-key li:hover, .sp-key li.is-active { color: #FFF; }
.sp-key i { width: 15px; height: 15px; flex: none; background: #101010; }
.sp-key .k-gold  { background: #F0C63A; border: 1px solid #FFE28C; }
.sp-key .k-floor { border: 2px solid #F0C63A;
  background: repeating-linear-gradient(45deg, #17140B 0 3px, rgba(240,198,58,.5) 3px 5px); }
.sp-key .k-cat1  { border: 2px solid #DCA92B;
  background: repeating-linear-gradient(0deg, #101010 0 3px, rgba(220,169,43,.62) 3px 4px); }
.sp-key .k-cat2  { border: 2px solid #E8E8E8;
  background: repeating-linear-gradient(0deg, #101010 0 3px, rgba(232,232,232,.48) 3px 4px); }
.sp-key .k-cat3  { border: 2px solid #8C8C8C;
  background: repeating-linear-gradient(0deg, #101010 0 3px, rgba(140,140,140,.42) 3px 4px); }

/* ---------- tooltip ----------
   The plan carries no permanent price tags. A pill pinned to a block claims to
   belong to that block, which on a tier of fourteen identical stands is a lie
   that looks tidy; this says the same thing at the moment it is asked for, next
   to the thing being asked about. */
.sp-tip {
  position: absolute; z-index: 6; pointer-events: none;
  min-width: 116px; padding: 8px 11px;
  background: #0A0A0A; border: 1px solid rgba(255, 255, 255, .24);
  box-shadow: 0 6px 22px rgba(0, 0, 0, .75);
  opacity: 0; transform: translateY(4px);
  transition: opacity .11s ease, transform .11s ease;
  line-height: 1.25;
}
.sp-tip.is-on { opacity: 1; transform: none; }
.sp-tip__k {
  display: block; margin-bottom: 3px;
  font-family: var(--font-mono, monospace); font-size: 9px;
  letter-spacing: .14em; text-transform: uppercase; color: var(--text-muted, #8A8A8A);
}
.sp-tip__v {
  display: block; font-family: var(--font-display, Anton, sans-serif);
  font-size: 16px; letter-spacing: .05em; color: #FFF;
}
.sp-tip__p {
  display: block; margin-top: 4px;
  font-family: var(--font-display, Anton, sans-serif);
  font-size: 14px; letter-spacing: .04em; color: var(--accent, #E8B923);
}
.sp-tip__p--none {
  font-family: var(--font-mono, monospace); font-size: 9.5px;
  letter-spacing: .1em; text-transform: uppercase; color: var(--text-muted, #8A8A8A);
}

/* The legend doubles as the price list, so a number is readable at a glance
   without pointing at anything. It stays empty until the date has prices. */
.sp-key li { flex-wrap: wrap; }
.sp-key__p {
  font-family: var(--font-display, Anton, sans-serif);
  font-size: 12px; letter-spacing: .03em; color: var(--accent, #E8B923);
}
.sp-key__p:empty { display: none; }
  background: repeating-linear-gradient(0deg, #101010 0 3px, rgba(140,140,140,.42) 3px 4px); }
  background: repeating-linear-gradient(0deg, #101010 0 3px, rgba(140,140,140,.42) 3px 4px); }
