/* ==========================================================================
   Beat Bunker — Components
   Small reusable named class set. Each block maps to one Astro component.
   ========================================================================== */

/* --- Layout primitives ------------------------------------------------- */
.container {
  width: 100%;
  max-width: var(--maxw);
  margin-inline: auto;
  padding-inline: var(--gutter);
}
@media (max-width: 767px) {
  .container { padding-inline: var(--gutter-mobile); }
}

.section {
  padding-block: var(--section-md);
}
.section--lg { padding-block: var(--section-lg); }
.section--sm { padding-block: var(--section-sm); }

/* Colour-scheme modifiers (carried from the Webflow scheme system) */
.section--ink { background: var(--color-surface); }
.section--accent { background: var(--color-accent); color: var(--color-accent-ink); }
/* Photo-backed CTA band: the brand gradient, no overlay. Dark ink on the bright
   gradient keeps text legible and reads bolder than white. */
.section--accent-image {
  color: var(--bb-black);
  background-image: url("../assets/images/background.webp");
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
}
/* Eyebrow becomes a crisp dark outlined pill so it pops off the colour */
.section--accent-image .eyebrow {
  color: var(--bb-black);
  border: var(--border-w) solid var(--bb-black);
  padding: var(--space-1) var(--space-3);
  border-radius: var(--radius-pill);
}

.section__head {
  display: flex;
  align-items: end;
  justify-content: space-between;
  gap: var(--space-5);
  margin-bottom: var(--space-8);
  flex-wrap: wrap;
}

/* --- Page hero (interior page header) ---------------------------------- */
.page-hero { display: grid; gap: var(--space-8); }
.page-hero__text { display: grid; gap: var(--space-4); max-width: var(--maxw-prose); }
.page-hero__lead { font-size: var(--fs-md); color: var(--color-text-muted); }
.page-hero__media {
  border-radius: var(--radius-xl);
  overflow: hidden;
  aspect-ratio: 21 / 9;
}
.page-hero__media img { width: 100%; height: 100%; object-fit: cover; }
@media (max-width: 767px) { .page-hero__media { aspect-ratio: 16 / 10; } }

/* --- Eyebrow / pill label ---------------------------------------------- */
.eyebrow {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  font-family: var(--font-body);
  font-size: var(--fs-xs);
  font-weight: var(--fw-bold);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--color-accent);
}

/* --- Buttons ----------------------------------------------------------- */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  font-family: var(--font-display);
  font-size: var(--fs-base);
  text-transform: uppercase;
  letter-spacing: 0.02em;
  line-height: 1;
  position: relative;
  padding: var(--space-4) var(--space-6);
  border: var(--border-w) solid var(--color-accent);
  border-radius: var(--radius-pill);
  background: var(--color-accent);
  color: var(--bb-black);
  /* Two offset systems, same 6px signature:
     - Filled buttons (primary + ink) paint the offset as a BRAND GRADIENT via a
       pseudo-element (a box-shadow can't be a gradient) — see --btn-shadow-grad.
     - Outline buttons keep a solid box-shadow (a gradient would show through
       their transparent body) — see --btn-shadow.
     Both are contextual to the SURFACE, set on the container, never the button.
     Default = brand gradient / brand-blue; bright accent surfaces flip below. */
  --btn-shadow: var(--shadow-hard-blue);
  --btn-shadow-grad: var(--grad-brand);
  /* Lift via top/left, NOT transform — a transform creates a stacking context
     that would trap the gradient pseudo in front of the fill. */
  top: 0;
  left: 0;
  transition: top var(--dur) var(--ease),
              left var(--dur) var(--ease),
              box-shadow var(--dur) var(--ease),
              background-color var(--dur) var(--ease),
              color var(--dur) var(--ease);
}
.btn:hover {
  top: -2px;
  left: -2px;
}
/* Primary filled button: the face goes white on hover/focus (text stays black),
   letting the brand-gradient offset behind it pop. Outline/ghost/ink and the
   day-filter keep their own hover states. */
.btn:not(.btn--outline):not(.btn--ghost):not(.btn--ink):not(.day-filter__btn):hover,
.btn:not(.btn--outline):not(.btn--ghost):not(.btn--ink):not(.day-filter__btn):focus-visible {
  background: var(--bb-white);
  border-color: var(--bb-white);
  color: var(--bb-black);
}
/* Brand-gradient offset for filled buttons. Sits behind the button (z-index -1)
   so the opaque fill covers everything but the 6px down-right sliver. Disabled
   on outline/ghost (transparent body would let it show through) below. */
.btn::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  border-radius: inherit;
  background: var(--btn-shadow-grad);
  transform: translate(6px, 6px);
  opacity: 0;
  transition: opacity var(--dur) var(--ease);
}
.btn:hover::before,
.btn:focus-visible::before { opacity: 1; }

/* Bright accent surfaces (the CtaBand gradient, solid accent): a blue offset
   competes with the bright field, so the offset flips to black there. This is
   the only surface that overrides the default blue. */
.section--accent .btn,
.section--accent-image .btn { --btn-shadow: var(--shadow-hard-black); --btn-shadow-grad: linear-gradient(var(--bb-black), var(--bb-black)); }
/* ...except the ink button, whose own fill IS black — a black offset would be
   the same colour as the button. It keeps a brand offset instead (gradient for
   the filled pseudo, blue for any solid fallback).
   RULE: a button's hard-offset shadow is never the same colour as the button. */
.section--accent .btn--ink,
.section--accent-image .btn--ink { --btn-shadow: var(--shadow-hard-blue); --btn-shadow-grad: var(--grad-brand); }
.btn:active {
  top: 0;
  left: 0;
  box-shadow: none;
}
.btn:active::before { opacity: 0; }

.btn--outline {
  background: transparent;
  color: var(--color-text);
  border-color: var(--color-border-strong);
}
.btn--outline:hover,
.btn--outline:focus-visible {
  border-color: var(--bb-black);
  color: var(--color-accent);
  background: var(--color-surface);   /* opaque fill so the brand-gradient offset reads only at the offset, not through the body */
  box-shadow: none;
}

/* Ink sticker: solid black pill that carries the signature hard offset shadow
   at rest (not just on hover). The headline CTA button. Its offset is brand-blue,
   never black — a black offset would be the same colour as the black fill and
   vanish (see the no-same-colour rule on the accent-surface override above). */
.btn--ink {
  background: var(--bb-black);
  color: var(--color-text);
  border-color: var(--bb-black);
  font-size: var(--fs-md);
  padding: var(--space-4) var(--space-8);
}
/* The ink headline carries its brand-gradient offset at rest, not just on hover. */
.btn--ink::before { opacity: 1; }
.btn--ink:hover,
.btn--ink:focus-visible { color: var(--color-accent); }

/* Arrow that slides forward on hover/focus (used inside .btn--ink) */
.btn__arrow {
  width: 1em;
  height: 1em;
  flex-shrink: 0;
  transition: transform var(--dur) var(--ease);
}
.btn:hover .btn__arrow,
.btn:focus-visible .btn__arrow { transform: translateX(var(--space-1)); }

/* Leading icon inside a button — a currentColor SVG painted via mask so it
   always tracks the button's text colour (black on the yellow Tickets pill,
   etc.). Same masking pattern as .lockup__icon; the source file's own fill is
   irrelevant under a mask. */
.btn__icon {
  width: 1.15em;
  height: 1.15em;
  flex-shrink: 0;
  background: currentColor;
  -webkit-mask: var(--icon) center / contain no-repeat;
          mask: var(--icon) center / contain no-repeat;
}
.btn__icon--ticket { --icon: url("../assets/svg/ticket.svg"); }

.btn--ghost {
  background: transparent;
  border-color: transparent;
  color: var(--color-text);
  padding-inline: var(--space-2);
}
.btn--ghost:hover {
  color: var(--color-accent);
  box-shadow: none;
  top: 0;
  left: 0;
}
/* Ghost is a plain text link — no offset at all. (Outline DOES use the gradient
   pseudo; on hover it takes an opaque fill so the gradient shows only at the
   offset, see .btn--outline:hover.) */
.btn--ghost::before { content: none; }

.btn[aria-disabled="true"],
.btn:disabled {
  opacity: 0.5;
  pointer-events: none;
}

.btn--sm { font-size: var(--fs-sm); padding: var(--space-3) var(--space-5); }

/* --- Tag --------------------------------------------------------------- */
.tag {
  display: inline-flex;
  align-items: center;
  font-size: var(--fs-xs);
  font-weight: var(--fw-bold);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  padding: var(--space-1) var(--space-3);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-pill);
  color: var(--color-text);
}
.tag[hidden] { display: none; }   /* keep [hidden] winning over inline-flex (day filter) */

/* --- Card shell -------------------------------------------------------- */
.card {
  background: var(--color-surface);
  border: var(--border-w) solid var(--color-border);
  border-radius: var(--radius-xl);
  padding: var(--pad-card-md);
}

/* --- Badge (layered logo mark) ----------------------------------------- */
.badge {
  display: grid;
  aspect-ratio: 1;
}
.badge > * {
  grid-area: 1 / 1;
  width: 100%;
  height: 100%;
}
/* Continuously rotating ring (the outer logo layer) */
.badge__outer {
  transform-origin: 50% 50%;
  animation: badge-spin var(--dur-spin) linear infinite;
  will-change: transform;
}
@keyframes badge-spin {
  to { transform: rotate(360deg); }
}
@media (prefers-reduced-motion: reduce) {
  .badge__outer { animation: none; }
}
/* Static full badge (inner + outer ring, no rotation) — used in the nav */
.badge--static .badge__outer { animation: none; }

/* --- Site header / nav (sleek bar) ------------------------------------- */
/* Clean translucent sticky bar with a hairline base. The .container keeps
   max-width + gutter; .nav__bar is just the flex row. Links are muted
   uppercase with a single thin accent underline that grows on hover; the
   current page stays underlined. No box, no offset shadow, no link pills. */
.site-header {
  position: sticky;
  top: 0;
  z-index: var(--z-header);
  background: color-mix(in srgb, var(--color-bg) 80%, transparent);
  backdrop-filter: blur(12px);
  border-bottom: 1px solid var(--color-border);
}

/* Home splash: a fixed 3-zone bar that overlays the hero and MERGES on scroll.
   It starts transparent (the video reads full-bleed behind it) with the centre
   nav links pushed down to the hero's base via --merge-y; as you scroll, main.js
   drives --merge-y -> 0 (links rise into the centre) and --merge-progress 0 -> 1
   (the bar solidifies), fusing logo+lockup · links · countdown+Tickets into one
   sticky bar. --merge-progress / --merge-y default to the resting (un-merged)
   state; with JS off the bar paints solid (see html:not(.js) below). */
.site-header--splash {
  position: fixed;
  inset: 0 0 auto 0;
  background: color-mix(in srgb, var(--color-bg) calc(var(--merge-progress, 0) * 80%), transparent);
  backdrop-filter: blur(calc(var(--merge-progress, 0) * 12px));
  border-bottom-color: color-mix(in srgb, var(--color-border) calc(var(--merge-progress, 0) * 100%), transparent);
}
.nav__cta { flex-shrink: 0; }

/* The 3-zone bar. The left/right clusters are pinned at the top from the start
   (the "split" top chrome); only the centre links travel. */
.nav__cluster { display: flex; align-items: center; gap: var(--space-5); }
.nav__cluster--start { gap: var(--space-4); }
.nav__cluster .nav__lockup,
.nav__cluster .nav__countdown { margin-right: 0; }   /* clusters own their layout — no margin-auto */

@media (min-width: 992px) {
  .nav__bar--splash {
    display: grid;
    grid-template-columns: 1fr auto 1fr;
    column-gap: var(--space-5);
  }
  .nav__bar--splash .nav__cluster--start { justify-self: start; }
  .nav__bar--splash .nav__menu--splash { justify-self: center; }
  .nav__bar--splash .nav__cluster--end { justify-self: end; }
  .nav__bar--splash .nav__toggle { display: none; }
  /* The centre links are pushed down to the hero base, then rise on scroll.
     Gated behind .js so a no-JS visitor (no scroll handler to bring them back
     up) gets the links resting in the bar centre instead of stranded at the base. */
  .js .nav__bar--splash .nav__menu--splash {
    transform: translateY(var(--merge-y, 55svh));
    will-change: transform;
  }
}

/* Below the merge breakpoint: a normal flex bar — logo+lockup left, the right
   cluster (countdown<1200 hidden, Tickets) + burger pushed right, links behind
   the burger as the usual dropdown panel. No fly-up (the menu is out of flow). */
@media (max-width: 991px) {
  .nav__bar--splash { display: flex; align-items: center; }
  .nav__bar--splash .nav__cluster--start { margin-right: auto; }
}
@media (max-width: 479px) {
  .nav__bar--splash .nav__lockup { display: none; }   /* keep logo + Tickets + burger on one row */
}

/* JS disabled entirely: paint the splash bar solid so the centred links read
   over the hero (they can't fly without the scroll handler). */
html:not(.js) .site-header--splash {
  background: color-mix(in srgb, var(--color-bg) 80%, transparent);
  backdrop-filter: blur(12px);
  border-bottom-color: var(--color-border);
}

.nav__bar {
  display: flex;
  align-items: center;
  gap: var(--space-5);
  min-height: var(--header-h);
}
/* Logo + lockup are a tight left cluster (logo, then lockup). The lockup's
   margin-right:auto absorbs ALL free space after it, pushing whatever follows
   to the right edge — the menu on desktop, the burger on mobile. One rule, no
   floating: the lockup is always inline in the bar, never in the dropdown. */
.nav__lockup { margin-right: auto; }
.nav__logo {
  display: inline-flex;
  align-items: center;
  flex-shrink: 0;
}
.nav__logo svg,
.nav__logo img { width: 4rem; height: 4rem; }
.nav__menu {
  display: flex;
  align-items: center;
  gap: var(--space-8);
}
.nav__list {
  display: flex;
  align-items: center;
  gap: var(--space-6);
  list-style: none;
  margin: 0;
  padding: 0;
}
.nav__link {
  position: relative;
  font-family: var(--font-display);
  text-transform: uppercase;
  font-size: var(--fs-base);
  letter-spacing: 0.04em;
  color: var(--color-text-muted);
  padding-block: var(--space-2);
  transition: color var(--dur) var(--ease);
}
.nav__link::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 2px;
  background: var(--color-accent);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform var(--dur) var(--ease);
}
.nav__link:hover,
.nav__link:focus-visible,
.nav__link[aria-current="page"] { color: var(--color-text); }
.nav__link:hover::after,
.nav__link:focus-visible::after,
.nav__link[aria-current="page"]::after { transform: scaleX(1); }

/* Text-swap on hover/focus (NavLabel). Two stacked copies of the label live in a
   one-line clip (.nav__swap); the stack (.nav__swap-stack) slides up exactly one
   line so the resting copy exits and the incoming accent copy (.nav__swap-line--in)
   takes its place. Only one copy is ever visible (the clip hides the rest).
   --swap-h is one line; the slide is one --swap-h. */
.nav__swap {
  --swap-h: 1.25em;
  display: inline-block;
  height: var(--swap-h);
  overflow: hidden;
  vertical-align: bottom;
}
.nav__swap-stack {
  display: block;
  transition: transform var(--dur-swap) var(--ease-swap);
}
.nav__swap-line {
  display: block;
  height: var(--swap-h);
  line-height: var(--swap-h);
}
.nav__swap-line--in { color: var(--color-accent); }   /* incoming copy reads as intentional */
/* The swap fires on every link that carries a NavLabel — the bar links (incl.
   the home splash), the mega-menu titles, the Info sidebar, and the footer nav. */
.nav__link:hover .nav__swap-stack,
.nav__link:focus-visible .nav__swap-stack,
.nav__mega-item:hover .nav__swap-stack,
.nav__mega-item:focus-visible .nav__swap-stack,
.subnav__link:hover .nav__swap-stack,
.subnav__link:focus-visible .nav__swap-stack,
.footer__nav-list a:hover .nav__swap-stack,
.footer__nav-list a:focus-visible .nav__swap-stack {
  transform: translateY(calc(-1 * var(--swap-h)));
}
/* Reduced motion: no slide — just recolour the resting copy on hover/focus. */
@media (prefers-reduced-motion: reduce) {
  .nav__swap-stack { transition: none; }
  .nav__link:hover .nav__swap-stack,
  .nav__link:focus-visible .nav__swap-stack,
  .nav__mega-item:hover .nav__swap-stack,
  .nav__mega-item:focus-visible .nav__swap-stack,
  .subnav__link:hover .nav__swap-stack,
  .subnav__link:focus-visible .nav__swap-stack,
  .footer__nav-list a:hover .nav__swap-stack,
  .footer__nav-list a:focus-visible .nav__swap-stack { transform: none; }
  .nav__link:hover .nav__swap-line,
  .nav__link:focus-visible .nav__swap-line,
  .nav__mega-item:hover .nav__swap-line,
  .nav__mega-item:focus-visible .nav__swap-line,
  .subnav__link:hover .nav__swap-line,
  .subnav__link:focus-visible .nav__swap-line,
  .footer__nav-list a:hover .nav__swap-line,
  .footer__nav-list a:focus-visible .nav__swap-line { color: var(--color-accent); }
}

/* --- Nav dropdown (the Info hub) --------------------------------------- */
/* The Info item is a disclosure: a toggle button + a submenu of hub links.
   Desktop = a floating panel (hover, or click via aria-expanded; keyboard
   focus-within is the no-JS fallback). Mobile = an inline accordion. */
.nav__item--dropdown { position: relative; }
.nav__dropdown-toggle {
  background: none;
  border: 0;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
}
/* Caret = a rotated box chevron (shapes, not glyphs — rule 12) */
.nav__caret {
  width: 0.45em;
  height: 0.45em;
  border-right: var(--border-w) solid currentColor;
  border-bottom: var(--border-w) solid currentColor;
  transform: translateY(-0.12em) rotate(45deg);
  transition: transform var(--dur) var(--ease);
}
.nav__dropdown-toggle[aria-expanded="true"] .nav__caret { transform: translateY(0.06em) rotate(-135deg); }
/* The panel: a card that just FADES (the cascade is carried by the items). */
/* The Info dropdown is a MEGA-MENU: a wide dark card with a small label and a
   2-column grid of rich items (icon tile + title + description). */
.nav__submenu {
  position: absolute;
  top: calc(100% + var(--space-3));
  right: 0;                /* open leftward from the trigger so it stays on-canvas */
  left: auto;
  z-index: 1;
  width: max-content;
  max-width: min(34rem, calc(100vw - var(--gutter) * 2));
  margin: 0;
  padding: var(--space-5);
  background: var(--color-surface);
  border: var(--border-w) solid var(--color-border-strong);
  border-radius: var(--radius-xl);
  opacity: 0;
  visibility: hidden;
  transition: opacity var(--dur-menu) var(--ease-out), visibility var(--dur-menu);
}
/* Invisible bridge across the gap so hover stays continuous from the toggle
   to the panel (otherwise the panel flickers shut as the cursor crosses). */
.nav__submenu::before {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  top: calc(var(--space-3) * -1);
  height: var(--space-3);
}
.nav__item--dropdown:hover .nav__submenu,
.js .nav__dropdown-toggle[aria-expanded="true"] + .nav__submenu,
html:not(.js) .nav__item--dropdown:focus-within .nav__submenu {
  opacity: 1;
  visibility: visible;
}
.nav__mega-label {
  margin: 0 0 var(--space-5);
  font-family: var(--font-body);
  font-size: var(--fs-xs);
  font-weight: var(--fw-bold);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--color-text-muted);
}
.nav__mega-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--space-3) var(--space-5);
  list-style: none;
  margin: 0;
  padding: 0;
}
/* Items keep .nav__sublink for the cascade + keyboard roving; the visible tile
   is .nav__mega-item. Each carries an inline --i so the stagger is one rule. */
.nav__sublink {
  opacity: 0;
  transform: translateY(calc(-1 * var(--menu-slide)));
  transition: opacity var(--dur-menu) var(--ease-out),
              transform var(--dur-menu) var(--ease-out);
}
.nav__item--dropdown:hover .nav__sublink,
.js .nav__dropdown-toggle[aria-expanded="true"] + .nav__submenu .nav__sublink,
html:not(.js) .nav__item--dropdown:focus-within .nav__sublink {
  opacity: 1;
  transform: none;
  transition-delay: calc(var(--i, 0) * var(--stagger));
}
.nav__mega-item {
  display: flex;
  align-items: flex-start;   /* icon tile aligns to the top of the title */
  gap: var(--space-3);
  padding: var(--space-2);
  border-radius: var(--radius-md);
}
.nav__mega-icon {
  flex-shrink: 0;
  display: grid;
  place-items: center;
  width: 2.5rem;
  height: 2.5rem;
  border: var(--border-w) solid var(--color-border);
  border-radius: var(--radius-md);
  color: var(--color-text);
  transition: border-color var(--dur) var(--ease), color var(--dur) var(--ease);
}
.nav__mega-svg { width: 1.25rem; height: 1.25rem; }
.nav__mega-text { display: grid; gap: 0; }
.nav__mega-title {
  font-family: var(--font-display);
  text-transform: uppercase;
  font-size: var(--fs-base);
  letter-spacing: 0.01em;
  line-height: 1.15;
  color: var(--color-text);
  transition: color var(--dur) var(--ease);
}
.nav__mega-desc {
  font-family: var(--font-body);
  font-size: var(--fs-sm);
  color: var(--color-text-muted);
}
.nav__mega-item:hover .nav__mega-icon,
.nav__mega-item:focus-visible .nav__mega-icon { border-color: var(--color-accent); color: var(--color-accent); }
.nav__mega-item:hover .nav__mega-title,
.nav__mega-item:focus-visible .nav__mega-title { color: var(--color-accent); }
/* Reduced motion: no slide, no stagger — just a quick fade. */
@media (prefers-reduced-motion: reduce) {
  .nav__submenu { transition: opacity var(--dur) linear, visibility var(--dur); }
  .nav__sublink { transform: none; transition: opacity var(--dur) linear; }
  .nav__item--dropdown:hover .nav__sublink,
  .js .nav__dropdown-toggle[aria-expanded="true"] + .nav__submenu .nav__sublink,
  html:not(.js) .nav__item--dropdown:focus-within .nav__sublink { transition-delay: 0ms; }
}

.nav__actions { display: flex; align-items: center; gap: var(--space-4); }

.nav__toggle {
  display: none;
  align-items: center;
  justify-content: center;
  background: transparent;
  border: 1px solid var(--color-border-strong);
  border-radius: var(--radius-pill);
  padding: var(--space-3) var(--space-4);
  color: var(--color-text);
}
/* Burger that morphs to an X when the menu is open (aria-expanded, toggled by
   main.js). Three CSS bars — shapes, not glyphs (rule 12). */
.nav__burger {
  position: relative;
  display: block;
  width: var(--space-5);
  height: var(--border-w);
  background: currentColor;
  transition: background var(--dur) var(--ease);
}
.nav__burger::before,
.nav__burger::after {
  content: "";
  position: absolute;
  left: 0;
  width: 100%;
  height: var(--border-w);
  background: currentColor;
  transition: transform var(--dur) var(--ease);
}
.nav__burger::before { top: calc(var(--space-2) * -1); }
.nav__burger::after  { top: var(--space-2); }
.nav__toggle[aria-expanded="true"] .nav__burger { background: transparent; }
.nav__toggle[aria-expanded="true"] .nav__burger::before { transform: translateY(var(--space-2)) rotate(45deg); }
.nav__toggle[aria-expanded="true"] .nav__burger::after  { transform: translateY(calc(var(--space-2) * -1)) rotate(-45deg); }

@media (max-width: 991px) {
  .nav__toggle { display: inline-flex; }
  .nav__menu {
    position: absolute;
    inset: var(--header-h) 0 auto 0;
    flex-direction: column;
    align-items: stretch;
    gap: 0;
    padding: var(--space-4) var(--gutter) var(--space-6);
    background: var(--color-bg);
    border-bottom: 1px solid var(--color-border);
    display: none;
  }
  .nav__menu[data-open="true"] { display: flex; }
  .nav__list { flex-direction: column; align-items: stretch; gap: 0; }
  .nav__link {
    display: block;
    font-size: var(--fs-lg);
    padding-block: var(--space-4);
    border-bottom: 1px solid var(--color-border);
  }
  .nav__link::after { display: none; }
  /* Mega-menu becomes an inline single-column accordion in the mobile panel */
  .nav__dropdown-toggle { width: 100%; justify-content: space-between; }
  .nav__submenu {
    position: static;
    max-width: none;
    margin: 0;
    padding: var(--space-3) 0 var(--space-3) var(--space-5);
    border: 0;
    border-radius: 0;
    background: transparent;
    display: none;
  }
  .nav__submenu::before { display: none; }   /* no hover bridge in the accordion */
  .js .nav__dropdown-toggle[aria-expanded="true"] + .nav__submenu { display: block; }
  html:not(.js) .nav__submenu { display: block; }   /* no-JS: show all hub links */
  .nav__mega-label { display: none; }        /* the Info toggle above already labels it */
  .nav__mega-grid { grid-template-columns: 1fr; gap: var(--space-3); }
  .nav__mega-icon { width: 2.5rem; height: 2.5rem; }
  .nav__mega-title { font-size: var(--fs-base); }
  .nav__actions { margin-top: var(--space-4); }
  .nav__actions .btn { width: 100%; justify-content: center; }
}

/* --- Lockup (icon + label pairs: dates / location) --------------------- */
/* Festival lockup — calendar + dates over pin + place, stacked as a block and
   anchored by an accent rule so it reads as a deliberate unit, not loose text.
   Icons are the currentColor SVGs masked to the accent; labels are display
   caps. Reusable; the nav groups it beside the logo via .nav__lockup. */
.lockup {
  display: inline-grid;
  gap: var(--space-1);
  padding-left: var(--space-4);
  border-left: var(--border-w) solid var(--color-accent);
  font-family: var(--font-display);
  text-transform: uppercase;
  font-size: var(--fs-base);
  letter-spacing: 0.03em;
  line-height: 1.05;
  color: var(--color-text);
}
.lockup__item { display: inline-flex; align-items: center; gap: var(--space-2); white-space: nowrap; }
.lockup__icon {
  width: 1.1em;
  height: 1.1em;
  flex-shrink: 0;
  background: var(--color-accent);
  -webkit-mask: var(--icon) center / contain no-repeat;
          mask: var(--icon) center / contain no-repeat;
}
.lockup__icon--cal { --icon: url("../assets/svg/calenar.svg"); }
.lockup__icon--pin { --icon: url("../assets/svg/location.svg"); }

/* In the nav: always inline in the bar, grouped beside the logo at every width
   (desktop and mobile). On small phones it shrinks so logo + lockup + burger
   still fit on one row. */
@media (max-width: 479px) {
  .nav__lockup { font-size: var(--fs-sm); padding-left: var(--space-3); }
}

/* --- Info layout + sidebar sub-nav ------------------------------------- */
/* Dedicated hub pages: a sticky left sidebar of the Info links (same set as the
   nav dropdown) beside the page content. Collapses to a stacked sub-nav on
   narrow screens. The sidebar links mirror the dropdown: muted, current = accent. */
.info-layout {
  display: grid;
  grid-template-columns: minmax(0, 16rem) minmax(0, 1fr);
  gap: var(--space-12);
  align-items: start;
}
@media (max-width: 860px) { .info-layout { grid-template-columns: 1fr; gap: var(--space-8); } }
/* The content column stacks the prose body and an optional per-topic FAQ block. */
.info-layout__main { display: grid; gap: var(--space-10); align-content: start; }
.info-faqs { display: grid; gap: var(--space-5); }
.info-faqs__title { font-size: var(--h4); }

.subnav { position: sticky; top: calc(var(--header-h) + var(--space-6)); }
.subnav__title {
  font-family: var(--font-body);
  font-size: var(--fs-xs);
  font-weight: var(--fw-bold);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--color-text-muted);
  margin: 0 0 var(--space-3);
}
.subnav__list { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--space-2); }
.subnav__link {
  display: flex;
  align-items: center;
  gap: var(--space-4);
  /* Flush-left with the "Info" title at rest (no inline padding); the grey box
     + its padding appear on hover, nudging the content to the right. */
  padding: var(--space-3) 0;
  border-radius: var(--radius-md);
  font-family: var(--font-display);
  text-transform: uppercase;
  font-size: var(--fs-base);
  letter-spacing: 0.02em;
  color: var(--color-text-muted);
  transition: color var(--dur) var(--ease),
              background-color var(--dur) var(--ease),
              padding var(--dur) var(--ease);
}
.subnav__icon {
  flex-shrink: 0;
  display: grid;
  place-items: center start;   /* glyph flush-left so the row aligns with the "Info" heading */
  width: 1.75rem;
  height: 1.75rem;
  color: var(--color-text-muted);
  transition: color var(--dur) var(--ease);
}
.subnav__icon-svg { width: 1.75rem; height: 1.75rem; }
.subnav__link:hover,
.subnav__link:focus-visible {
  color: var(--color-text);
  background-color: var(--color-surface);
  padding-inline: var(--space-3);
}
.subnav__link:hover .subnav__icon,
.subnav__link:focus-visible .subnav__icon { color: var(--color-accent); }
.subnav__link[aria-current="page"] { color: var(--color-accent); }
.subnav__link[aria-current="page"] .subnav__icon { color: var(--color-accent); }
@media (max-width: 860px) {
  .subnav { position: static; }
}

/* --- Hero (home splash) ------------------------------------------------ */
/* Full-viewport video splash with the spinning badge logo centred. It sits
   below the standard SiteHeader (sticky top) and fills the rest of the first
   screen (100svh minus the bar). The header carries the brand/lockup/countdown/
   Tickets, so the hero has no chrome of its own. */
.hero {
  position: relative;
  min-height: 100svh;            /* full screen — the splash header overlays it (out of flow) */
  display: grid;
  place-items: center;
  isolation: isolate;
}
.hero__media {
  position: absolute;
  inset: 0;
  overflow: hidden;
  z-index: -2;
}
.hero__media video,
.hero__media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}
/* A soft vignette so the centred logo reads over any frame of the footage. */
.hero::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background: linear-gradient(180deg, rgba(0,0,0,0.45) 0%, rgba(0,0,0,0.12) 35%, rgba(0,0,0,0.12) 65%, rgba(0,0,0,0.45) 100%);
}
/* Centrepiece: the spinning badge logo, centred in the viewport. */
.hero__title { margin: 0; line-height: 0; }
.hero__logo { width: clamp(11rem, 30vw, 18rem); height: auto; }
@media (max-width: 479px) {
  .hero__logo { width: clamp(9rem, 38vw, 13rem); }
}
/* The home's nav links live in the gateway SiteHeader (.nav__menu--splash),
   pushed down to this hero's base until you scroll — see .site-header--splash. */

/* --- Intro band -------------------------------------------------------- */
.intro__grid {
  display: grid;
  gap: var(--space-10);
  grid-template-columns: 1fr 1fr;
  align-items: center;
}
.intro__body { display: grid; gap: var(--space-5); max-width: var(--maxw-prose); }
.intro__lead { font-size: var(--fs-md); }
.intro__body p { color: var(--color-text-muted); }
.intro__body p.intro__lead { color: var(--color-text); }
/* Triptych variant: two text columns flank the portrait promo video, which
   stands as a central pillar — vertically centred and taller than the copy, so
   it reads as the anchor the words wrap around. Left column carries the head +
   lead + CTA; the right column (.intro__aside) holds the supporting paragraphs.
   Used on the home IntroBand (the Weekender band); about.html keeps the plain
   head|body grid above. */
.intro__grid--triptych {
  align-items: center;
  grid-template-columns: 1fr auto 1fr;   /* text · portrait media · text */
}
.intro__grid--triptych .intro__text { display: grid; gap: var(--space-6); align-content: center; }
.intro__grid--triptych .intro__media { width: var(--promo-w); max-width: 100%; }
.intro__grid--triptych .intro__aside {
  display: grid;
  gap: var(--space-5);
  align-content: center;
  max-width: var(--maxw-prose);
}
.intro__grid--triptych .intro__aside p { color: var(--color-text-muted); }
/* Tablet: video keeps centre stage between the two text blocks, stacked. */
@media (max-width: 991px) {
  .intro__grid--triptych { grid-template-columns: 1fr; gap: var(--space-8); }
}
@media (max-width: 767px) {
  .intro__grid { grid-template-columns: 1fr; }
}

/* --- Tickets ----------------------------------------------------------- */
.tickets__grid {
  display: grid;
  gap: var(--space-6);
  grid-template-columns: repeat(2, 1fr);
}
@media (max-width: 767px) {
  .tickets__grid { grid-template-columns: 1fr; }
}
.ticket-card {
  position: relative;
  overflow: hidden;
  display: grid;
  gap: var(--space-5);
  align-content: start;
}
.ticket-card[data-disabled="true"] { opacity: 0.5; }
.ticket-card__title { font-size: var(--fs-lg); }
.ticket-card__price {
  font-family: var(--font-accent);
  font-size: var(--fs-3xl);
  line-height: 1;
}
.ticket-card__from {
  display: inline-block;
  font-family: var(--font-body);
  font-size: var(--fs-sm);
  font-weight: var(--fw-regular);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--color-text-muted);
}
.ticket-card__deposit { color: var(--color-accent); font-weight: var(--fw-bold); }
.ticket-card__detail { color: var(--color-text-muted); }
.ticket-card__note {
  padding-top: var(--space-4);
  border-top: 1px solid var(--color-border);
  font-size: var(--fs-sm);
  color: var(--color-text-muted);
}

/* Ticket groups (weekend / day / glamping) */
.ticket-groups { margin-top: var(--space-10); }
.ticket-group + .ticket-group { margin-top: var(--space-12); }
.ticket-group__head { margin-bottom: var(--space-6); }
.ticket-group__title { font-size: var(--h4); }
.tickets__grid--single { grid-template-columns: 1fr; }
.tickets__grid--solo { grid-template-columns: min(100%, 34rem); justify-content: center; }

/* Release switcher (mini-tabs inside a ticket card) */
.ticket-release {
  display: grid;
  gap: var(--space-4);
  align-content: start;
}
.ticket-release[hidden] { display: none; }   /* keep [hidden] winning over the grid above */

.ticket-release--soldout .ticket-card__detail { color: var(--color-soldout-ink); }
/* A sold-out release still shows what it cost, struck through + the dimmer grey so the
   price reads as closed (not buyable) and the ladder of rising prices is clear. */
.ticket-release--soldout .ticket-card__price {
  color: var(--bb-neutral-500);
  text-decoration: line-through;
  text-decoration-thickness: 2px;
}

/* Split release — two real ticket shapes side by side in one release panel
   (Weekend left, Saturday right). Each child is a `.pass`: a line-style ticket
   with a perforated stub, punched notches and a rounded outline. The card it
   sits in (.ticket-card--passes) goes black so the surface-filled tickets and
   their notch cut-outs read against the backdrop. */
.ticket-release--split { grid-template-columns: 1fr 1fr; gap: var(--space-6); align-items: stretch; }
@media (max-width: 767px) {
  .ticket-release--split { grid-template-columns: 1fr; }
}
/* Single-pass release (e.g. First release — Weekend only, no Saturday option).
   One ticket, capped and centred so it doesn't stretch the full card width. */
.ticket-release--single { justify-items: center; }
.ticket-release--single .pass { width: min(100%, 32rem); }

/* The pass — one ticket. Stub (perforated edge) + main body. overflow stays
   visible so the corner status ribbon can wrap past the edges; the notch circles
   need no clip because they are painted in the backdrop colour (see below). */
.pass {
  position: relative;
  display: grid;
  grid-template-columns: auto 1fr;
  align-items: stretch;
  background: var(--color-surface);
  border: var(--border-w) solid var(--color-border);
  border-radius: var(--radius-lg);
  color: var(--color-text);   /* live (available) passes read white; sold-out drops to grey below */
}
/* Sold-out passes read inactive purely through a dimmer text colour — no opacity
   tint (that dulled the pink ribbon) and no desaturation. The title and features
   drop to a darker grey; the pink "Sold out" ribbon, badge and accent stub word
   all stay full strength. */
.pass.ticket-release--soldout .ticket-card__title,
.pass.ticket-release--soldout .pass__feat { color: var(--color-soldout-ink); }
/* Sold-out chrome reads fully grey — never yellow or white. The badge mark
   desaturates, the vertical stub word drops off the accent, and the inert
   "Sold out" button is a flat grey pill (no accent fill, no offset shadow).
   Only the pink corner ribbon keeps its colour. */
.ticket-release--soldout .pass__badge { filter: grayscale(1) brightness(1.1); opacity: 0.7; }
.ticket-release--soldout .pass__type { color: var(--color-soldout-ink); }
.ticket-release--soldout .pass__main .btn {
  background: var(--bb-neutral-700);
  border-color: var(--bb-neutral-700);
  color: var(--color-text-muted);
  box-shadow: none;
  opacity: 1;
}
.ticket-release--soldout .pass__main .btn::before { display: none; }   /* kill the brand-gradient offset */
/* Perforated stub — brand badge over a vertical pass-type word, divided from
   the body by a dashed perforation. */
.pass__stub {
  position: relative;
  display: grid;
  justify-items: center;
  align-content: space-between;
  gap: var(--space-5);
  padding: var(--space-5) var(--space-4);
  border-right: var(--border-w) dashed var(--color-border);
}
.pass__badge { width: var(--space-10); }
.pass__type {
  writing-mode: vertical-rl;
  transform: rotate(180deg);        /* reads bottom-to-top up the stub */
  font-family: var(--font-display);
  font-size: var(--fs-md);
  line-height: 1;
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--color-accent);
}
/* Punched notches — backdrop-coloured circles centred on the perforation at the
   top and bottom edges, with the grey outline carried around the curve. Each circle
   is clipped to just its INNER half (the bite into the ticket); the outline border
   on that half continues the ticket's edge line around the semicircle, and the outer
   half is hidden so no stray arc floats on the backdrop. */
.pass__stub::before,
.pass__stub::after {
  content: "";
  position: absolute;
  left: 100%;
  width: var(--space-5);
  height: var(--space-5);
  transform: translate(-50%, -50%);
  border-radius: var(--radius-full);
  background: var(--pass-notch, var(--color-bg));
  border: var(--border-w) solid var(--color-border);
}
.pass__stub::before { top: calc(-1 * var(--border-w)); clip-path: inset(50% 0 0 0); }   /* centre on the outer edge; keep the lower half */
.pass__stub::after { top: calc(100% + var(--border-w)); clip-path: inset(0 0 50% 0); }  /* centre on the outer edge; keep the upper half */
/* Ticket body — title, price, detail, action. */
.pass__main {
  display: grid;
  gap: var(--space-4);
  align-content: start;
  padding: var(--space-6);
}
/* Skimmable feature list — short label per row, led by a masked grey icon. */
.pass__feats {
  display: grid;
  gap: var(--space-3);
  margin: 0;
  padding: 0;
  list-style: none;
}
.pass__feat {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  font-size: var(--fs-sm);
  font-weight: var(--fw-bold);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--color-text);   /* white on live passes; sold-out drops to dim grey */
}
.pass__feat::before {
  content: "";
  flex: none;
  width: 1.4em;
  height: 1.4em;
  background: currentColor;   /* icon follows the row's text colour (muted, or dimmer when sold out) */
  -webkit-mask: var(--icon) center / contain no-repeat;
          mask: var(--icon) center / contain no-repeat;
}
.pass__feat--tent { --icon: url("../assets/svg/tent.svg"); }
.pass__feat--cal { --icon: url("../assets/svg/calenar.svg"); }
.pass__feat--ticket { --icon: url("../assets/svg/ticket.svg"); }
/* Black backdrop so the surface tickets + notch cut-outs read. overflow visible
   so the passes' corner ribbons can wrap past the card's inner padding. */
.ticket-card--passes { background: var(--bb-black); --pass-notch: var(--bb-black); overflow: visible; }

/* Pass status corner ribbon — the canonical wrapped corner ribbon, top-right only.
   `.pass__ribbon` is a small corner box (overflow:hidden) pinned just outside the
   ticket's top-right corner; `.pass__ribbon-band` is the banner clipped to it; the
   wrapper's ::before/::after are the darker fold tails at the corner so the ribbon
   reads as folded over and pinned behind. The whole shape derives from one size var
   (`--ribbon-size`) using the canonical ratios — band = 1.5x, offset = /15, tail = /30,
   band-left = /6, band-top = /5, padding = /10 — so the fold tails line up with the
   band ends at any size. Green = available, pink = sold out. */
.pass__ribbon {
  --ribbon-size: var(--space-20);
  --ribbon-shade: var(--bb-green-dark);
  position: absolute;
  z-index: 1;                       /* own stacking context: keeps the -1 fold tails inside */
  top: calc(var(--ribbon-size) / -15);
  right: calc(var(--ribbon-size) / -15);
  width: var(--ribbon-size);
  height: var(--ribbon-size);
  overflow: hidden;
  pointer-events: none;
}
.pass__ribbon::before,
.pass__ribbon::after {
  position: absolute;
  z-index: -1;
  content: "";
  display: block;
  border: calc(var(--ribbon-size) / 30) solid var(--ribbon-shade);
  border-top-color: transparent;
  border-right-color: transparent;
}
.pass__ribbon::before { top: 0; left: 0; }
.pass__ribbon::after { bottom: 0; right: 0; }
.pass__ribbon-band {
  position: absolute;
  display: block;
  left: calc(var(--ribbon-size) / -6);
  top: calc(var(--ribbon-size) / 5);
  width: calc(var(--ribbon-size) * 1.5);
  padding: calc(var(--ribbon-size) / 10) 0;
  transform: rotate(45deg);
  background: var(--bb-green);
  box-shadow: var(--shadow-soft);
  color: var(--bb-black);
  text-align: center;
  line-height: 1;
  font-family: var(--font-display);
  font-size: var(--fs-sm);
  letter-spacing: var(--tracking-tight);
  text-transform: uppercase;
}
.ticket-release--soldout .pass__ribbon { --ribbon-shade: var(--bb-pink-dark); }
.ticket-release--soldout .pass__ribbon-band { background: var(--bb-pink); }
/* Narrow screens: passes stack to one column (see .ticket-release--split), so pull
   in the card + pass padding and trim the ribbon a touch — it should overhang the
   corner, not swamp a full-width single ticket. */
@media (max-width: 479px) {
  .ticket-card--passes { padding: var(--pad-card-sm); }
  .pass__main { padding: var(--space-5); min-width: 0; }
  .pass__stub { padding: var(--space-5) var(--space-3); }
  /* The Sink price token is very wide; at fs-3xl "£150.00" overruns the narrow
     single-column pass and (overflow:visible) spills the page. Step it down. */
  .pass .ticket-card__price { font-size: var(--fs-xl); }
  .pass__ribbon { --ribbon-size: var(--space-16); }
  .pass__ribbon-band { font-size: var(--fs-xs); }
}

/* Feature ticket card (single, wide — e.g. glamping) */
.ticket-card--feature { gap: var(--space-8); }
.ticket-card--feature .ticket-card__body,
.ticket-card--feature .ticket-card__aside { display: grid; gap: var(--space-4); align-content: start; }
@media (min-width: 768px) {
  .ticket-card--feature { grid-template-columns: 1fr auto; align-items: center; column-gap: var(--space-12); }
  .ticket-card--feature .ticket-card__aside { justify-items: start; }
  /* media variant: slider | body | aside */
  .ticket-card--feature.ticket-card--media { grid-template-columns: var(--card-media-w) 1fr auto; }
}

/* --- Media slider (single-slide carousel with controls) ----------------
   Distinct from .reels__track (a multi-item scroller). No-JS: the viewport
   scroll-snaps through every slide, so all images stay reachable. main.js
   reveals prev/next, builds dot indicators, and drives the scroll. */
.slider { position: relative; }
.slider__viewport {
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  scroll-behavior: smooth;
  border-radius: var(--radius-lg);
  scrollbar-width: none;
  -webkit-overflow-scrolling: touch;
}
.slider__viewport::-webkit-scrollbar { display: none; }
.slider__track {
  display: flex;
  margin: 0;
  padding: 0;
  list-style: none;
}
.slider__slide {
  flex: 0 0 100%;
  scroll-snap-align: center;
}
.slider__slide img {
  display: block;
  width: 100%;
  height: auto;          /* override the width/height attrs so aspect-ratio wins */
  aspect-ratio: 4 / 3;
  object-fit: cover;
}
.slider__btn {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--space-8);
  height: var(--space-8);
  padding: 0;
  border: var(--border-w) solid var(--color-border-strong);
  border-radius: var(--radius-full);
  background: var(--color-surface);
  color: var(--color-text);
  cursor: pointer;
  transition: background-color var(--dur) var(--ease),
              color var(--dur) var(--ease),
              border-color var(--dur) var(--ease);
}
.slider__btn:hover {
  background: var(--color-accent);
  color: var(--bb-black);
  border-color: var(--color-accent);
}
.slider__btn:disabled { opacity: 0.4; cursor: default; }
.slider__btn svg { width: var(--space-5); height: var(--space-5); }
.slider__btn--prev { left: var(--space-3); }
.slider__btn--next { right: var(--space-3); }
.slider__dots {
  display: flex;
  justify-content: center;
  gap: var(--space-2);
  margin-top: var(--space-3);
}
.slider__dot {
  width: var(--space-2);
  height: var(--space-2);
  padding: 0;
  border: 0;
  border-radius: var(--radius-full);
  background: var(--color-border-strong);
  cursor: pointer;
  transition: background-color var(--dur) var(--ease), transform var(--dur) var(--ease);
}
.slider__dot[aria-current="true"] { background: var(--color-accent); transform: scale(1.4); }
@media (prefers-reduced-motion: reduce) {
  .slider__viewport { scroll-behavior: auto; }
}

/* Countdown — ultra-modern segmented readout: each unit is a hairline-bordered
   surface tile with a big tabular-mono number and a micro label, an accent
   tick anchoring the top edge. */
.countdown {
  display: flex;
  gap: var(--space-3);
  justify-content: center;
  flex-wrap: wrap;
  margin-top: var(--space-4);
}
.countdown__unit {
  position: relative;
  display: grid;
  justify-items: center;
  gap: var(--space-2);
  min-width: 6rem;
  padding: var(--space-4);
  background: var(--color-surface);
  border: var(--border-w) solid var(--color-border);
  border-radius: var(--radius-lg);
  overflow: hidden;
}
.countdown__num {
  font-family: var(--font-accent);
  font-size: var(--fs-2xl);
  line-height: 1;
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.02em;
}
.countdown__label {
  font-size: var(--fs-xs);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--color-text-muted);
}

/* Compact bar countdown (nav) — number over a micro label, thin vertical
   dividers between units. Layout from the reference; brand colours only, no
   surface tiles and no accent tick (those stay on the big .countdown). */
.countdown--bar { margin: 0; gap: 0; flex-wrap: nowrap; }
.countdown--bar .countdown__unit {
  min-width: 0;
  padding: 0 var(--space-4);
  background: none;
  border: 0;
  border-radius: 0;
  gap: var(--space-1);
}
.countdown--bar .countdown__unit + .countdown__unit {
  border-left: var(--border-w) solid var(--color-border);
}
.countdown--bar .countdown__num { font-size: var(--fs-lg); }
.countdown--bar .countdown__label { font-size: var(--fs-2xs); }

/* Nav slot: desktop-only, grouped with the date lockup on the left of the bar.
   Below 1200px it is hidden and the lockup reclaims its margin-right:auto. */
.nav__countdown { display: none; }
@media (min-width: 1200px) {
  .nav__lockup { margin-right: var(--space-6); }
  .nav__countdown { display: flex; margin-right: auto; }
}

/* --- Tabs (segmented, ARIA tablist) ------------------------------------ */
.tabs__list {
  display: inline-flex;
  flex-wrap: wrap;
  gap: var(--space-1);
  padding: var(--space-1);
  border: var(--border-w) solid var(--color-border);
  border-radius: var(--radius-pill);
  margin-bottom: var(--space-8);
}
.tabs__tab {
  font-family: var(--font-display);
  text-transform: uppercase;
  font-size: var(--fs-base);
  letter-spacing: 0.02em;
  line-height: 1;
  padding: var(--space-3) var(--space-6);
  border: none;
  border-radius: var(--radius-pill);
  background: transparent;
  color: var(--color-text);
  transition: background-color var(--dur) var(--ease), color var(--dur) var(--ease);
}
.tabs__tab[aria-selected="true"] {
  background: var(--color-accent);
  color: var(--color-accent-ink);
}
.tabs__tab[aria-selected="false"]:hover { color: var(--color-accent); }

/* Folder tabs — three equal tab shapes across the card's top. Each is an
   OUTLINE (top + sides, open at the bottom) with no fill, so the active tab
   connects straight down into the card content — there is no baseline rule.
   Active = accent label + accent outline. A brand corner ribbon sits in each
   tab's top-right corner: pink = sold out, green = available. */
.tabs--folder .tabs__list {
  display: flex;
  flex-wrap: nowrap;
  justify-content: flex-start;
  align-items: stretch;
  gap: var(--space-3);
  margin: 0 0 var(--space-6);
  padding: 0;
  border: 0;
  border-radius: 0;
  background: transparent;
}
.tabs--folder .tabs__tab {
  position: relative;
  min-width: 0;
  font-size: var(--fs-md);
  line-height: var(--lh-tight);
  text-align: center;
  padding: var(--space-5) var(--space-4);
  /* tab shape — outline on top + sides, open at the bottom onto the content */
  border: var(--border-w) solid var(--color-border);
  border-bottom: 0;
  border-radius: var(--radius-lg) var(--radius-lg) 0 0;
  background: transparent;
  color: var(--color-text-muted);
  transition: color var(--dur) var(--ease), border-color var(--dur) var(--ease);
}
/* Sold-out (inactive) tabs — smaller, share the remaining ~half, greyed, no hover. */
.tabs--folder .tabs__tab--soldout {
  flex: 1 1 0;
  font-size: var(--fs-sm);
  color: var(--color-text-muted);
  opacity: 0.65;
}
/* The available (live) tab is the emphasis: ~half the width, white text. */
.tabs--folder .tabs__tab--available {
  flex: 2 1 0;
  color: var(--color-text);
  font-weight: var(--fw-bold);
}
/* Only the available (live) tab ever takes the accent highlight. A sold-out
   release is never "active" in yellow — its tab stays grey even when selected,
   lifting just to a stronger grey border for mild selection feedback. */
.tabs--folder .tabs__tab--available:hover,
.tabs--folder .tabs__tab--available[aria-selected="true"] { border-color: var(--color-accent); }
.tabs--folder .tabs__tab--soldout[aria-selected="true"] { border-color: var(--color-border-strong); }
/* (Status ribbon lives on the ticket passes, not the tabs — see .pass__ribbon.) */
/* Narrow screens: tighten the three tabs so the row never overflows. Smaller
   gap + padding + type let the two-word labels (First Release / Final Release)
   wrap to two lines and still share one row at 390px. */
@media (max-width: 479px) {
  .tabs--folder .tabs__list { gap: var(--space-2); }
  .tabs--folder .tabs__tab { font-size: var(--fs-xs); padding: var(--space-3) var(--space-1); }
  .tabs--folder .tabs__tab--soldout { font-size: var(--fs-xs); }
  .tabs--folder .tabs__tab--available { font-size: var(--fs-sm); }
}

/* --- Day filter (segmented toggle) ------------------------------------- */
/* Visually a segmented control matching .tabs__list (one bordered container,
   the active day a filled accent segment). Semantically it stays a filter
   (role="group" + aria-pressed), NOT a tablist — it filters one shared grid
   rather than swapping panels. Buttons keep the base `.btn` class for markup
   parity, so this overrides the pill chrome to read as tab segments. */
.day-filter {
  display: inline-flex;
  flex-wrap: wrap;
  gap: var(--space-1);
  padding: var(--space-1);
  border: var(--border-w) solid var(--color-border);
  border-radius: var(--radius-pill);
  margin-bottom: var(--space-8);
}
.day-filter[hidden] { display: none; }   /* no-JS: keep the bar hidden (filtering needs JS) */
.day-filter__btn::before { content: none; }   /* segments are flat — no gradient offset */
/* Segments mirror .tabs__tab: no border/shadow/lift; active = accent fill. */
.day-filter__btn {
  border-color: transparent;
  background: transparent;
  color: var(--color-text);
  box-shadow: none;
  padding: var(--space-3) var(--space-6);
}
.day-filter__btn:hover { box-shadow: none; top: 0; left: 0; }
.day-filter__btn[aria-pressed="true"] {
  background: var(--color-accent);
  color: var(--color-accent-ink);
  box-shadow: none;
  top: 0;
  left: 0;
}
.day-filter__btn[aria-pressed="false"] {
  background: transparent;
  color: var(--color-text);
  border-color: transparent;
}
.day-filter__btn[aria-pressed="false"]:hover {
  color: var(--color-accent);
  border-color: transparent;
  box-shadow: none;
  top: 0;
  left: 0;
}

/* --- Reels carousel ---------------------------------------------------- */
.reels__track {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: minmax(15rem, 17rem);
  gap: var(--space-5);
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  padding-bottom: var(--space-4);
  scrollbar-width: thin;
}
.reel-card {
  scroll-snap-align: start;
  position: relative;
  aspect-ratio: 9 / 16;
  border-radius: var(--radius-xl);
  overflow: hidden;
}
.reel-card img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.reel-card__cap {
  position: absolute;
  inset: auto 0 0 0;
  padding: var(--space-8) var(--space-4) var(--space-4);
  font-size: var(--fs-sm);
  background: linear-gradient(180deg, transparent, rgba(0,0,0,0.85));
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.reel-card__play {
  position: absolute;
  inset: var(--space-3) var(--space-3) auto auto;
  width: 2.25rem;
  height: 2.25rem;
  display: grid;
  place-items: center;
  border-radius: var(--radius-full);
  background: var(--color-accent);
  color: var(--color-accent-ink);
}

/* --- Promo player ------------------------------------------------------ */
/* A continuously-looping muted preview with bottom-edge controls: a labelled
   "Watch 2025 Film" play pill bottom-left that opens the full film (with sound)
   in the video modal, and a pause toggle bottom-right that stops/resumes the
   looping preview. The play pill is an <a> to the full file, so no-JS it just
   opens the video; main.js upgrades it to launch the modal and wires the pause
   toggle (which is JS-only, so it's hidden until upgraded). Borderless media
   per build rule #10. */
.promo {
  position: relative;
  max-width: var(--promo-w);
  margin-inline: auto;
  border-radius: var(--radius-xl);
  overflow: hidden;
  background: var(--color-surface);
}
.promo__loop {
  display: block;
  width: 100%;
  aspect-ratio: 9 / 16;
  object-fit: cover;
}
/* Controls pinned along the bottom edge over a gradient scrim for legibility:
   play pill left, pause toggle right. */
.promo__controls {
  position: absolute;
  inset: auto 0 0 0;
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: var(--space-3);
  padding: var(--space-10) var(--space-4) var(--space-4);
  background: linear-gradient(180deg, transparent, rgba(0,0,0,0.55));
}
/* Play = labelled accent pill (icon + "Watch 2025 Film"), carries the
   signature hard offset and lifts on hover/focus. */
.promo__play {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-2) var(--space-4) var(--space-2) var(--space-2);
  border-radius: var(--radius-pill);
  background: var(--color-accent);
  color: var(--color-accent-ink);
  font-family: var(--font-display);
  text-transform: uppercase;
  letter-spacing: var(--tracking-label);
  font-size: var(--fs-sm);
  line-height: 1;
  box-shadow: var(--shadow-hard-black);
  transition: transform var(--dur) var(--ease), box-shadow var(--dur) var(--ease);
}
.promo__play:hover,
.promo__play:focus-visible { transform: translate(-2px, -2px); }
.promo__play-icon {
  display: grid;
  place-items: center;
  width: var(--space-6);
  height: var(--space-6);
  flex-shrink: 0;
  border-radius: var(--radius-full);
  background: var(--color-accent-ink);
  color: var(--color-accent);
}
.promo__play-icon svg { width: 0.9em; height: 0.9em; margin-left: 0.1em; }
/* Pause = circular toggle for the looping preview. JS-only, so hidden until
   main.js upgrades the page (.js on <html>); swaps to a play glyph when paused. */
.promo__pause {
  display: none;
  flex-shrink: 0;
  place-items: center;
  width: var(--space-10);
  height: var(--space-10);
  border-radius: var(--radius-full);
  border: var(--border-w) solid var(--color-border-strong);
  background: rgba(0, 0, 0, 0.4);
  color: var(--bb-white);
  cursor: pointer;
  transition: transform var(--dur) var(--ease), border-color var(--dur) var(--ease), color var(--dur) var(--ease);
}
.js .promo__pause { display: grid; }
.promo__pause:hover,
.promo__pause:focus-visible { border-color: var(--color-accent); color: var(--color-accent); transform: translate(-2px, -2px); }
.promo__pause svg { width: var(--space-5); height: var(--space-5); }
.promo__pause .promo__pause-resume { display: none; }
.promo__pause[aria-pressed="true"] .promo__pause-glyph { display: none; }
.promo__pause[aria-pressed="true"] .promo__pause-resume { display: block; }
.promo__play:focus-visible,
.promo__pause:focus-visible {
  outline: var(--border-w) solid var(--color-focus);
  outline-offset: 3px;
}

/* --- Video modal ------------------------------------------------------- */
/* Native <dialog>: top-layer, Escape-to-close and focus-trap for free.
   Plays the full film with sound; main.js wires the trigger -> showModal()
   and pauses/resets the video on close. */
.video-modal {
  /* Portrait lightbox: height-driven so a 9:16 film fills the viewport
     vertically, with width derived from it (and capped on narrow screens). */
  width: min(92vw, calc(var(--video-modal-h) * 9 / 16));
  max-width: 92vw;
  padding: 0;
  border: 0;
  background: transparent;
  color: var(--color-text);
  overflow: visible;
}
.video-modal::backdrop {
  background: var(--color-overlay);
}
/* Bound open: the lightbox springs in with an overshoot pop while the backdrop
   fades up behind it — gives the open more oomph than a hard cut. Honoured only
   where motion is welcome. */
.video-modal[open] {
  animation: video-modal-pop var(--dur-slow) var(--ease) both;
}
.video-modal[open]::backdrop {
  animation: video-modal-fade var(--dur) var(--ease) both;
}
@keyframes video-modal-pop {
  0%   { opacity: 0; transform: scale(0.7) translateY(2rem); }
  55%  { opacity: 1; transform: scale(1.05) translateY(-0.25rem); }
  75%  { transform: scale(0.98); }
  100% { opacity: 1; transform: scale(1) translateY(0); }
}
@keyframes video-modal-fade {
  from { opacity: 0; }
  to   { opacity: 1; }
}
@media (prefers-reduced-motion: reduce) {
  .video-modal[open],
  .video-modal[open]::backdrop { animation: none; }
}
.video-modal__inner { position: relative; }
/* On-brand frame: the film sits inside a 2px accent stroke carrying the
   signature hard offset, so the lightbox reads as a deliberate Beat Bunker
   "sticker", not a bare video on black. */
.video-modal__frame {
  border-radius: var(--radius-xl);
  overflow: hidden;
  background: var(--bb-black);
  border: var(--border-w) solid var(--color-accent);
  box-shadow: var(--shadow-hard);
}
.video-modal__video {
  display: block;
  width: 100%;
  aspect-ratio: 9 / 16;
  background: var(--bb-black);
}
/* Close = accent disc overlaying the frame's top-right corner (sits ON the
   film, not floating above it where a tall portrait pushes it into the nav).
   Black glyph + black hard-offset lift on hover — the offset is never accent on
   accent (no-same-colour rule). */
.video-modal__close {
  position: absolute;
  top: var(--space-4);
  right: var(--space-4);
  z-index: 2;
  display: grid;
  place-items: center;
  width: var(--space-10);
  height: var(--space-10);
  padding: 0;
  border: 0;
  border-radius: var(--radius-full);
  background: var(--color-accent);
  color: var(--bb-black);
  cursor: pointer;
  transition: transform var(--dur) var(--ease), box-shadow var(--dur) var(--ease);
}
.video-modal__close:hover,
.video-modal__close:focus-visible {
  transform: translate(-2px, -2px);
  box-shadow: var(--shadow-hard-black);
}
.video-modal__close:focus-visible {
  outline: var(--border-w) solid var(--color-focus);
  outline-offset: 3px;
}
.video-modal__close svg { width: var(--space-5); height: var(--space-5); }

/* --- News -------------------------------------------------------------- */
.news__grid {
  display: grid;
  gap: var(--space-6);
  grid-template-columns: repeat(2, 1fr);
}
@media (max-width: 767px) { .news__grid { grid-template-columns: 1fr; } }
/* Flush-at-rest, frames-on-hover. Rest: no chrome — the photo runs edge-to-edge,
   rounded to the card radius and zoomed, chip hidden. Hover/focus: a bordered
   --color-surface panel comes in and the photo pulls back into an inset frame,
   rounds down and settles to 1×, while the category chip fades in.
   The card's outer box, its (zero) padding and the media box height are all
   CONSTANT, so the body (date/title/excerpt) never moves between states — only
   the surface, the image inset/zoom and the chip animate. The framing happens
   INSIDE the media box (height locked by aspect-ratio), so nothing below shifts. */
.news-card {
  display: grid;
  gap: var(--space-3);
  grid-template-rows: auto 1fr;
  background: transparent;
  border: var(--border-w) solid transparent;
  border-radius: var(--radius-xl);
  transition: background var(--dur) var(--ease), border-color var(--dur) var(--ease);
}
.news-card:hover,
.news-card:focus-within {
  background: var(--color-surface);
  border-color: var(--color-border);
}
.news-card__media {
  position: relative;
  aspect-ratio: 16 / 10;
  border-radius: var(--radius-xl);
  overflow: hidden;
  padding: 0;
  transition: padding var(--dur) var(--ease);
}
.news-card:hover .news-card__media,
.news-card:focus-within .news-card__media { padding: var(--space-5); }
.news-card__media img {
  width: 100%; height: 100%; object-fit: cover;
  border-radius: 0;
  transform: scale(1.03);
  transition: transform var(--dur-slow) var(--ease), border-radius var(--dur) var(--ease);
}
.news-card:hover .news-card__media img,
.news-card:focus-within .news-card__media img { transform: none; border-radius: var(--radius-lg); }
/* Category chip: a .tag pinned over the photo, hidden at rest, fades in as the
   card frames on hover. */
.news-card__chip {
  position: absolute;
  top: var(--space-3);
  left: var(--space-3);
  z-index: 1;
  background: var(--color-bg);
  opacity: 0;
  transition: opacity var(--dur) var(--ease);
}
.news-card:hover .news-card__chip,
.news-card:focus-within .news-card__chip { opacity: 1; }
/* Body indents in step with the media inset on hover so the text stays aligned
   with the framed photo's left edge. Horizontal only — vertical position holds. */
.news-card__body {
  display: grid;
  gap: var(--space-3);
  align-content: start;
  /* Constant width so the copy never rewraps between states — only the body's
     x-position shifts (translate, not padding) to stay aligned with the framed
     photo on hover. Sized to the hover frame and left-aligned at rest. */
  width: calc(100% - 2 * var(--space-5));
  transition: transform var(--dur) var(--ease);
}
.news-card:hover .news-card__body,
.news-card:focus-within .news-card__body { transform: translateX(var(--space-5)); }
/* Date drops down from above on hover: hidden and raised at rest, slides + fades
   into place on hover. Its grid slot is reserved either way, so the title and
   excerpt below never move. */
.news-card__date {
  font-size: var(--fs-xs);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--color-accent);
  opacity: 0;
  transform: translateY(calc(-1 * var(--space-5)));
  transition: opacity var(--dur) var(--ease), transform var(--dur) var(--ease);
}
.news-card:hover .news-card__date,
.news-card:focus-within .news-card__date { opacity: 1; transform: none; }
.news-card__title { font-size: var(--fs-lg); }
.news-card__title a { color: inherit; text-decoration: none; }
.news-card__excerpt { color: var(--color-text-muted); font-size: var(--fs-sm); }
@media (prefers-reduced-motion: reduce) {
  .news-card,
  .news-card__media,
  .news-card__media img,
  .news-card__body,
  .news-card__date,
  .news-card__chip { transition: none; }
  .news-card__date { transform: none; }
  .news-card:hover .news-card__media img,
  .news-card:focus-within .news-card__media img { transform: none; }
}

/* News index grouped by festival year: a Sink year label + an accent rule that
   runs to the edge, then the grid. Newest year sits first. */
.news-year + .news-year { margin-top: var(--space-16); }
.news-year__head { display: flex; align-items: center; gap: var(--space-5); margin-bottom: var(--space-8); }
.news-year__label {
  font-family: var(--font-display);
  font-size: var(--h3);
  line-height: 1;
  letter-spacing: var(--tracking-heading);
  color: var(--color-text);
}
.news-year__rule { flex: 1; height: var(--border-w); background: var(--color-accent); border-radius: var(--radius-pill); }

/* --- Prose (long-form rich text) --------------------------------------- */
/* Body copy for articles, history, legal. Restores list markers that the
   base reset strips, and tints links + markers with the accent. */
.prose { max-width: var(--maxw-prose); display: grid; gap: var(--space-5); }
.prose > * { max-width: none; }
.prose p { color: var(--color-text-muted); }
.prose h2 { font-size: var(--h4); margin-top: var(--space-6); }
.prose h3 { font-size: var(--fs-xl); margin-top: var(--space-5); }
.prose > :first-child { margin-top: 0; }
.prose ul, .prose ol { padding-left: var(--space-6); display: grid; gap: var(--space-2); color: var(--color-text-muted); }
.prose ul { list-style: disc; }
.prose ol { list-style: decimal; }
.prose li::marker { color: var(--color-accent); }
/* Inline prose links tint + underline — but never restyle a .btn that lives in
   the body copy (a button keeps its own look; only true text links get this). */
.prose a:not(.btn) { color: var(--color-accent); text-decoration: underline; text-underline-offset: 0.15em; }
.prose a:not(.btn):hover { text-decoration-thickness: 2px; }
.prose strong { color: var(--color-text); font-weight: var(--fw-bold); }
/* Inline imagery in long-form bodies. Borderless (non-negotiable #10): the photo
   sits flush with rounded corners, no --color-border stroke. <figure> wraps an
   optional <figcaption>; a bare <img> works too. */
.prose figure { display: grid; gap: var(--space-3); }
.prose img,
.prose figure img {
  width: 100%; height: auto; display: block;
  border-radius: var(--radius-xl); overflow: hidden;
}
.prose figcaption { font-size: var(--fs-sm); color: var(--color-text-muted); }

/* --- Article (news detail) --------------------------------------------- */
/* Two-column editorial layout (mirrors the artist-detail hero): feature image
   in a left column that sticks while you read, headline + body + pager in the
   right column (the pager lines up with the text, not the full width).
   Collapses to one column on tablet. */
.article { max-width: var(--maxw-article); margin-inline: auto; }
.article__grid {
  display: grid;
  grid-template-columns: minmax(0, 20rem) minmax(0, 1fr);
  gap: var(--space-10);
  align-items: start;
}
.article__media { position: sticky; top: calc(var(--header-h) + var(--space-6)); }
.article__feature { border-radius: var(--radius-xl); overflow: hidden; }
.article__feature img { width: 100%; height: auto; display: block; }
.article__main { display: grid; gap: var(--space-8); min-width: 0; }
.article__main > .prose { max-width: var(--maxw-prose); }
/* Pager lives inside the text column; the standalone top margin is dropped so
   the column gap handles its spacing. */
.article__main > .pager { margin-top: var(--space-4); }
/* Topline: the "All news" back link sits inline with the date, both in the
   eyebrow style (the .page-hero__text header carries it as its eyebrow row). */
.article__topline { flex-wrap: wrap; }
.article__back { display: inline-flex; align-items: center; gap: var(--space-1); color: inherit; }
.article__back:hover, .article__back:focus-visible { color: var(--color-text); }
.article__back svg { width: 0.85rem; height: 0.85rem; }
.article__sep { width: var(--space-1); height: var(--space-1); border-radius: var(--radius-full); background: currentColor; opacity: 0.55; }
@media (max-width: 767px) {
  .article__grid { grid-template-columns: 1fr; gap: var(--space-8); }
  .article__media { position: static; max-width: 24rem; }
}

/* --- Pager (prev / next) ----------------------------------------------- */
/* Inspired by the artist-detail nav: the circular chevron control is the hero
   (identical treatment to .artist-nav-btn — grey outline at rest, accent +
   hard-offset lift on hover), with a small thumbnail and the post title beside
   it. No surface card, no "older/newer" words. */
.pager {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--space-6);
  margin-top: var(--space-12);
}
.pager__link {
  display: flex;
  align-items: center;
  gap: var(--space-4);
  text-decoration: none;
}
/* Next tile mirrors: chevron + thumbnail on the right, title to the right edge */
.pager__link--next { flex-direction: row-reverse; text-align: right; }
/* Circular chevron control — same treatment as .artist-nav-btn */
.pager__arrow {
  flex-shrink: 0;
  width: 3rem; height: 3rem;
  display: grid; place-items: center;
  border: var(--border-w) solid var(--color-border-strong);
  border-radius: var(--radius-full);
  color: var(--color-text);
  transition: border-color var(--dur) var(--ease), color var(--dur) var(--ease), transform var(--dur) var(--ease), box-shadow var(--dur) var(--ease);
}
.pager__arrow svg { width: 1.5rem; height: 1.5rem; }
.pager__link:hover .pager__arrow,
.pager__link:focus-visible .pager__arrow {
  border-color: var(--color-accent);
  color: var(--color-accent);
  transform: translate(-3px, -3px);
  box-shadow: var(--shadow-hard);
}
.pager__thumb {
  width: 3.5rem;
  height: 3.5rem;
  flex-shrink: 0;
  border-radius: var(--radius-lg);
  overflow: hidden;
}
.pager__thumb img { width: 100%; height: 100%; object-fit: cover; }
.pager__title {
  flex: 1;
  min-width: 0;
  font-family: var(--font-display);
  font-size: var(--fs-md);
  line-height: var(--lh-heading);
  letter-spacing: var(--tracking-heading);
  text-transform: uppercase;
  color: var(--color-text);
  transition: color var(--dur) var(--ease);
}
.pager__link:hover .pager__title,
.pager__link:focus-visible .pager__title { color: var(--color-accent); }
.pager__link[aria-disabled="true"] { opacity: 0.4; pointer-events: none; }
@media (max-width: 479px) { .pager { grid-template-columns: 1fr; } }

/* --- Feature grid (photo + title + copy) ------------------------------- */
/* The "how it unfolds" step cards on content pages. Photo-led tiles in a
   responsive grid; distinct from news-card (no date, square-ish media). */
.feature-grid {
  display: grid;
  gap: var(--space-6);
  grid-template-columns: repeat(3, 1fr);
}
@media (max-width: 991px) { .feature-grid { grid-template-columns: 1fr 1fr; } }
@media (max-width: 479px) { .feature-grid { grid-template-columns: 1fr; } }
.feature {
  display: grid;
  grid-template-rows: auto 1fr;
  background: var(--color-surface);
  border: var(--border-w) solid var(--color-border);
  border-radius: var(--radius-xl);
  overflow: hidden;
}
.feature__media { aspect-ratio: 1 / 1; }
.feature__media img { width: 100%; height: 100%; object-fit: cover; }
.feature__body { display: grid; gap: var(--space-3); padding: var(--space-5) var(--pad-card-md) var(--pad-card-md); align-content: start; }
.feature__title { font-size: var(--fs-lg); }
.feature__text { color: var(--color-text-muted); font-size: var(--fs-sm); }

/* --- Split (media + text row, alternating) ----------------------------- */
.split {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--space-10);
  align-items: center;
}
.split + .split { margin-top: var(--space-12); }
.split__media {
  border-radius: var(--radius-xl);
  overflow: hidden;
  aspect-ratio: 4 / 3;
}
.split__media img { width: 100%; height: 100%; object-fit: cover; }
.split__body { display: grid; gap: var(--space-4); max-width: var(--maxw-prose); }
.split__body p { color: var(--color-text-muted); }
.split--flip .split__media { order: 2; }
@media (max-width: 767px) {
  .split { grid-template-columns: 1fr; gap: var(--space-6); }
  .split--flip .split__media { order: 0; }
}

/* --- Chapters (history, photo-led timeline) ---------------------------- */
/* The redesigned history "timeline": each past event is a photo-led card,
   media and story alternating sides (.chapter--flip). Multi-photo years
   carry a .slider in the media cell; photo-less years use .chapter--text
   (a centred interlude carried by an oversized year). The brand accent
   cycles per card via --chapter-accent (turbo / pink / green / blue),
   tinting the eyebrow rule and the signature hard offset shadow. */
.chapters { display: grid; gap: var(--space-16); }

.chapter {
  --chapter-accent: var(--color-accent);
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--space-10);
  align-items: center;
}
.chapter--flip .chapter__media { order: 2; }

.chapter__media { min-width: 0; }
.chapter__frame {
  margin: 0;
  border: var(--border-w) solid var(--color-border-strong);
  border-radius: var(--radius-xl);
  overflow: hidden;
  aspect-ratio: 4 / 3;
  box-shadow: 6px 6px 0 0 var(--chapter-accent);
}
.chapter__frame img { width: 100%; height: 100%; object-fit: cover; display: block; }
/* slider variant: inherit the offset shadow + match the framed radius.
   Shadow goes on the viewport (the element that carries the radius + border)
   so it follows the rounded corners instead of poking out as a square box. */
.chapter__media .slider__viewport {
  border-radius: var(--radius-xl);
  border-color: var(--color-border-strong);
  box-shadow: 6px 6px 0 0 var(--chapter-accent);
}

.chapter__body { display: grid; gap: var(--space-3); max-width: var(--maxw-prose); }
.chapter__place { color: var(--chapter-accent); }
.chapter__place::before {
  content: "";
  width: var(--space-6);
  height: var(--border-w);
  background: var(--chapter-accent);
}
.chapter__year {
  font-family: var(--font-display);
  font-size: var(--h2);
  line-height: 0.95;
  text-transform: uppercase;
  letter-spacing: var(--tracking-heading);
}
.chapter__text { color: var(--color-text-muted); margin-top: var(--space-2); }

/* photo-less years: a centred interlude carried by an oversized year */
.chapter--text {
  grid-template-columns: 1fr;
  justify-items: center;
  text-align: center;
  gap: var(--space-4);
  padding-block: var(--space-6);
}
.chapter--text .chapter__body { justify-items: center; }
.chapter--text .chapter__place { justify-content: center; }
.chapter--text .chapter__year { font-size: var(--fs-4xl); }

@media (max-width: 767px) {
  .chapter { grid-template-columns: 1fr; gap: var(--space-6); }
  .chapter--flip .chapter__media { order: 0; }
  .chapter--text .chapter__year { font-size: var(--fs-3xl); }
}

/* --- Reveal on scroll -------------------------------------------------- */
/* Progressive: gated behind the .js class main.js sets on <html>, so a
   no-JS visitor (and reduced-motion) sees everything immediately. */
.js [data-reveal] {
  opacity: 0;
  transform: translateY(var(--space-5));
  transition: opacity 600ms var(--ease), transform 600ms var(--ease);
}
.js [data-reveal].is-in { opacity: 1; transform: none; }
@media (prefers-reduced-motion: reduce) {
  .js [data-reveal] { opacity: 1; transform: none; transition: none; }
}

/* --- FAQ accordion ----------------------------------------------------- */
.faq { max-width: var(--maxw-prose); }
/* Simple accordion: hairline rows, a flush-left question that shifts to accent on
   hover/open, a plus/minus marker, and the answer below. No surface boxes, no
   text animation. */
.faq__item { border-bottom: 1px solid var(--color-border); }
.faq__item summary {
  list-style: none;
  cursor: pointer;
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: var(--space-4);
  padding: var(--space-5) 0;
  font-family: var(--font-display);
  text-transform: uppercase;
  font-size: var(--fs-md);
  transition: color var(--dur) var(--ease);
}
.faq__item summary:hover,
.faq__item[open] summary { color: var(--color-accent); }
.faq__item summary::-webkit-details-marker { display: none; }
/* Plus/minus marker built from two accent bars (rule #12: shapes, not a glyph).
   On open the vertical bar collapses so the plus reads as a minus. */
.faq__item summary::after {
  content: "";
  flex-shrink: 0;
  width: 0.85em;
  height: 0.85em;
  background:
    linear-gradient(var(--color-accent), var(--color-accent)) center / 100% 2px no-repeat,
    linear-gradient(var(--color-accent), var(--color-accent)) center / 2px 100% no-repeat;
  transition: background-size var(--dur) var(--ease);
}
.faq__item[open] summary::after { background-size: 100% 2px, 2px 0; }
/* Answer sits flush-left under the question. */
.faq__answer { padding: 0 0 var(--space-5); color: var(--color-text-muted); }
@media (prefers-reduced-motion: reduce) {
  .faq__item summary,
  .faq__item summary::after { transition: none; }
}
.faq__more {
  max-width: var(--maxw-prose);
  margin-top: var(--space-8);
  color: var(--color-text-muted);
}

/* --- Newsletter -------------------------------------------------------- */
.newsletter { text-align: center; display: grid; gap: var(--space-6); justify-items: center; }
.newsletter__form {
  display: flex;
  gap: var(--space-3);
  width: 100%;
  max-width: 32rem;
}
@media (max-width: 479px) { .newsletter__form { flex-direction: column; } }
.field {
  flex: 1;
  background: transparent;
  border: var(--border-w) solid var(--color-border-strong);
  border-radius: var(--radius-pill);
  padding: var(--space-4) var(--space-5);
}
.field::placeholder { color: var(--color-text-muted); }
.field:focus-visible { border-color: var(--color-accent); outline: none; }
.newsletter__fine { font-size: var(--fs-xs); color: var(--color-text-muted); }

/* --- CTA band ---------------------------------------------------------- */
/* Asymmetric editorial band: oversized heading left, action pinned right. */
.cta-band {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: center;
  gap: var(--space-8) var(--space-16);
  max-width: var(--maxw-cta);
  margin-inline: auto;
}
.cta-band__text {
  display: grid;
  gap: var(--space-5);
}
.cta-band__text .eyebrow { justify-self: start; }
.cta-band__text :is(h2, h3) {
  font-size: var(--h1);
  line-height: var(--lh-tight);
}
.cta-band__lead {
  font-size: var(--fs-md);
  max-width: var(--maxw-prose);
}
.cta-band__action { justify-self: end; }
@media (max-width: 767px) {
  .cta-band {
    grid-template-columns: 1fr;
    gap: var(--space-6);
  }
  .cta-band__action { justify-self: start; }
}

/* --- Lineup grid + ArtistCard ------------------------------------------ */
.lineup__grid {
  display: grid;
  gap: var(--space-5);
  /* Cards keep a sensible minimum width and wrap, instead of squeezing to
     fit a fixed column count. Falls to a single column on narrow screens. */
  grid-template-columns: repeat(auto-fill, minmax(15rem, 1fr));
}
@media (max-width: 479px) { .lineup__grid { grid-template-columns: 1fr; } }

.artist-card {
  --card-accent: var(--color-accent);
  position: relative;
  display: grid;
  grid-template-rows: auto auto;
  background: var(--color-surface);
  border: var(--border-w) solid var(--color-border);
  border-radius: var(--radius-xl);
  overflow: hidden;
  transition: transform var(--dur) var(--ease), box-shadow var(--dur) var(--ease), border-color var(--dur) var(--ease);
}
.artist-card[hidden] { display: none; }   /* keep [hidden] winning over the grid display (day filter) */
/* Roster status: inactive DJs (data-active="false") stay in the markup — the
   "database" — but drop off the published line-up. Some artists rotate on/off
   each year, so they're hidden, not deleted. Add .lineup__grid--show-all on the
   grid to reveal them (dimmed) for review; remove it to hide them again. They
   still respect the day filter when shown. */
.lineup__grid:not(.lineup__grid--show-all) .artist-card[data-active="false"] { display: none; }
.lineup__grid--show-all .artist-card[data-active="false"] { opacity: 0.55; }
.artist-card:hover,
.artist-card:focus-within {
  transform: translate(-3px, -3px);
  box-shadow: 6px 6px 0 0 var(--card-accent);
  border-color: var(--card-accent);
}
.artist-card__media { aspect-ratio: 1; overflow: hidden; }
.artist-card__media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  filter: grayscale(1) contrast(1.05);
  transition: filter var(--dur) var(--ease), transform var(--dur) var(--ease);
}
.artist-card:hover .artist-card__media img,
.artist-card:focus-within .artist-card__media img {
  filter: grayscale(0);
  transform: scale(1.04);
}
.artist-card__body {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  padding: var(--space-4) var(--space-5);
}
.artist-card__name { font-size: var(--fs-md); }
.artist-card__day {
  flex-shrink: 0;
  font-family: var(--font-body);
  font-size: var(--fs-xs);
  font-weight: var(--fw-bold);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--card-accent);
}
.artist-card__link { position: absolute; inset: 0; }     /* full-cover link */
.artist-card__link span { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0,0,0,0); }
/* category colour variants (cycled across the grid like the export) */
.artist-card[data-accent="pink"] { --card-accent: var(--bb-pink); }
.artist-card[data-accent="green"] { --card-accent: var(--bb-green); }
.artist-card[data-accent="blue"] { --card-accent: var(--bb-blue); }

/* --- Artist detail (two-column: sticky image + scrolling content) ------- */
.artist-detail {
  padding-block: var(--space-12);
}
.artist-detail__grid {
  display: grid;
  grid-template-columns: minmax(0, 26rem) minmax(0, 1fr);
  gap: var(--space-12);
  align-items: start;
}
/* The headshot sticks while the longer content column scrolls past — same
   behaviour as the news article feature image. */
.artist-detail__media {
  position: sticky;
  top: calc(var(--header-h) + var(--space-6));
  aspect-ratio: 1;
  border-radius: var(--radius-xl);
  overflow: hidden;
}
.artist-detail__media img { width: 100%; height: 100%; object-fit: cover; }

.artist-detail__panel {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
}
.artist-detail__cat { margin: 0; color: var(--color-accent); font-weight: var(--fw-bold); letter-spacing: var(--tracking-label); text-transform: uppercase; font-size: var(--fs-xs); }
.artist-detail__panel h1 { margin: 0; }
.artist-detail__set { margin: 0; color: var(--color-text-muted); }

.artist-social { display: flex; flex-wrap: wrap; gap: var(--space-3); }
.artist-social a {
  width: 2.75rem;
  height: 2.75rem;
  display: grid;
  place-items: center;
  border: 1px solid var(--color-border-strong);
  border-radius: var(--radius-full);
  color: var(--color-text);
  transition: border-color var(--dur) var(--ease), color var(--dur) var(--ease), transform var(--dur) var(--ease), box-shadow var(--dur) var(--ease);
}
.artist-social a:hover,
.artist-social a:focus-visible {
  border-color: var(--color-accent);
  color: var(--color-accent);
  transform: translate(-3px, -3px);
  box-shadow: var(--shadow-hard);
}
.artist-social svg { width: 1.2rem; height: 1.2rem; }

.artist-bio {
  display: grid;
  gap: var(--space-4);
  align-content: start;
}
.artist-bio p { color: var(--color-text-muted); max-width: var(--maxw-prose); }

/* Mixcloud player embed — the artist's mix from the CMS bio. One or more
   widget iframes, full width, fixed widget height. */
.mixcloud { display: grid; gap: var(--space-3); }
.mixcloud iframe {
  width: 100%;
  height: var(--mixcloud-h);
  border: 0;
  border-radius: var(--radius-lg);
  display: block;
}

/* Top row of the panel: category eyebrow left, the nav cluster (prev · all-
   artists grid · next) right, vertically centred against the circular buttons.
   The heading sits directly below, so it moves up to the top of the panel. */
.artist-detail__top {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
}

/* Prev/next — bold chevrons in circular buttons; signature offset-shadow
   lift on hover, matching the social icon set. */
.artist-detail__nav {
  display: flex;
  gap: var(--space-3);
  flex-shrink: 0;
}
.artist-nav-btn {
  width: 3rem; height: 3rem;
  display: grid; place-items: center;
  border: var(--border-w) solid var(--color-border-strong);
  border-radius: var(--radius-full);
  background: transparent; color: var(--color-text);
  transition: border-color var(--dur) var(--ease), color var(--dur) var(--ease), transform var(--dur) var(--ease), box-shadow var(--dur) var(--ease);
}
.artist-nav-btn:hover,
.artist-nav-btn:focus-visible {
  border-color: var(--color-accent);
  color: var(--color-accent);
  transform: translate(-3px, -3px);
  box-shadow: var(--shadow-hard);
}
.artist-nav-btn svg { width: 1.5rem; height: 1.5rem; }

@media (max-width: 860px) {
  .artist-detail__grid { grid-template-columns: 1fr; gap: var(--space-8); }
  .artist-detail__media { position: static; width: 100%; max-width: 26rem; margin-inline: auto; }
}

/* --- Merch ------------------------------------------------------------- */
.merch-group + .merch-group { margin-top: var(--space-16); }
.merch-group__head {
  display: grid;
  gap: var(--space-4);
  max-width: var(--maxw-prose);
  margin-bottom: var(--space-8);
}
.merch-group__title-row {
  display: flex;
  align-items: baseline;
  gap: var(--space-4) var(--space-5);
  flex-wrap: wrap;
}
.merch-group__title { font-size: var(--h4); }
.merch-group__price {
  font-family: var(--font-accent);
  font-size: var(--fs-xl);
  line-height: 1;
  color: var(--color-accent);
}
.merch-group__desc { color: var(--color-text-muted); }

.merch__grid {
  display: grid;
  gap: var(--space-5);
  grid-template-columns: repeat(2, 1fr);
}
@media (max-width: 479px) { .merch__grid { grid-template-columns: 1fr; } }

/* Product card: light product tile, dark chrome, front/back flip.
   The flip is a visually-hidden checkbox + styled label — works with no JS;
   hover/focus also lifts the card with the signature hard offset shadow. */
.merch-card {
  --card-accent: var(--color-accent);
  position: relative;
  display: grid;
  grid-template-rows: auto 1fr;
  background: var(--color-surface);
  border: var(--border-w) solid var(--color-border);
  border-radius: var(--radius-xl);
  overflow: hidden;
  transition: transform var(--dur) var(--ease), box-shadow var(--dur) var(--ease), border-color var(--dur) var(--ease);
}
.merch-card:hover,
.merch-card:focus-within {
  transform: translate(-3px, -3px);
  box-shadow: 6px 6px 0 0 var(--card-accent);
  border-color: var(--card-accent);
}
.merch-card[data-accent="pink"] { --card-accent: var(--bb-pink); }
.merch-card[data-accent="green"] { --card-accent: var(--bb-green); }
.merch-card[data-accent="blue"] { --card-accent: var(--bb-blue); }

.merch-card__media {
  position: relative;
  aspect-ratio: 1;
  background: var(--bb-white);   /* product shots are on white — frame them as a clean tile */
  overflow: hidden;
}
.merch-card__face {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: opacity var(--dur) var(--ease);
}
.merch-card__face--back { opacity: 0; }
.merch-card__toggle:checked ~ .merch-card__face--front { opacity: 0; }
.merch-card__toggle:checked ~ .merch-card__face--back { opacity: 1; }

.merch-card__flip {
  position: absolute;
  inset: auto var(--space-3) var(--space-3) auto;
  z-index: 1;
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  font-family: var(--font-display);
  text-transform: uppercase;
  font-size: var(--fs-xs);
  letter-spacing: var(--tracking-label);
  line-height: 1;
  padding: var(--space-2) var(--space-3);
  border: var(--border-w) solid var(--bb-black);
  border-radius: var(--radius-pill);
  background: var(--color-accent);
  color: var(--bb-black);
  cursor: pointer;
  transition: transform var(--dur) var(--ease), box-shadow var(--dur) var(--ease);
}
.merch-card__flip:hover {
  transform: translate(-2px, -2px);
  box-shadow: var(--shadow-hard-black);
}
.merch-card__flip svg { width: 0.9rem; height: 0.9rem; transition: transform var(--dur) var(--ease); }
.merch-card__toggle:checked ~ .merch-card__flip svg { transform: rotateY(180deg); }
.merch-card__toggle:focus-visible ~ .merch-card__flip {
  outline: 2px solid var(--color-focus);
  outline-offset: 2px;
}
.merch-card__flip [data-face-back] { display: none; }
.merch-card__toggle:checked ~ .merch-card__flip [data-face-front] { display: none; }
.merch-card__toggle:checked ~ .merch-card__flip [data-face-back] { display: inline; }

.merch-card__body {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  padding: var(--space-4) var(--space-5);
}
.merch-card__name { font-size: var(--fs-md); }

@media (prefers-reduced-motion: reduce) {
  .merch-card,
  .merch-card__face,
  .merch-card__flip,
  .merch-card__flip svg { transition: none; }
}

/* --- Form (labelled controls — order / contact) ------------------------ */
/* Forms are capped at the prose measure — never the full content width. The
   two-up grid still collapses to one column on narrow screens. */
.form { display: grid; gap: var(--space-6); max-width: var(--maxw-prose); }
.form__grid {
  display: grid;
  gap: var(--space-5);
  grid-template-columns: 1fr 1fr;
}
@media (max-width: 479px) { .form__grid { grid-template-columns: 1fr; } }
.form__group { display: grid; gap: var(--space-2); }
.form__group--full { grid-column: 1 / -1; }
.form__label {
  font-size: var(--fs-sm);
  font-weight: var(--fw-bold);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--color-text-muted);
}
/* Required-field marker — the asterisk is decorative (aria-hidden); the
   accessible cue is the field's aria-required + the form's required-note. */
.form__required { color: var(--color-error-ink); margin-left: 0.15em; }
/* Top-of-form instruction: "* indicates a required field" */
.form__required-note { font-size: var(--fs-sm); color: var(--color-text-muted); margin-bottom: var(--space-5); }
.form__required-note .form__required { margin-left: 0; }
.form__control {
  width: 100%;
  font: inherit;
  color: var(--color-text);
  background: var(--color-surface);
  border: var(--border-w) solid var(--color-border-strong);
  border-radius: var(--radius-lg);
  padding: var(--space-3) var(--space-4);
  transition: border-color var(--dur) var(--ease), box-shadow var(--dur) var(--ease);
}
.form__control::placeholder { color: var(--color-text-muted); }
.form__control:focus-visible {
  outline: none;
  border-color: var(--color-accent);
  box-shadow: var(--shadow-hard);
}
textarea.form__control { min-height: 7rem; resize: vertical; }
.form__select { position: relative; display: grid; }
.form__select select { appearance: none; padding-right: var(--space-10); }
.form__select::after {
  content: "";
  position: absolute;
  top: 50%;
  right: var(--space-4);
  width: 0.6rem;
  height: 0.6rem;
  border-right: var(--border-w) solid var(--color-text-muted);
  border-bottom: var(--border-w) solid var(--color-text-muted);
  transform: translateY(-65%) rotate(45deg);
  pointer-events: none;
}
.form__actions { display: flex; align-items: center; gap: var(--space-5); flex-wrap: wrap; }
/* Consent / agreement row: native checkbox (accent-tinted), text alongside */
.form__check { display: flex; gap: var(--space-3); align-items: flex-start; }
.form__check input[type="checkbox"] {
  flex-shrink: 0;
  margin-top: var(--space-1);
  accent-color: var(--color-accent);
}
.form__check__label { font-size: var(--fs-sm); color: var(--color-text-muted); }
.form__check input[type="radio"] { flex-shrink: 0; margin-top: var(--space-1); accent-color: var(--color-accent); }

/* Radio rating groups (feedback): native accent-tinted radios in a fieldset */
.form__fieldset { border: 0; padding: 0; margin: 0; min-width: 0; }
.form__fieldset > .form__label { display: block; margin-bottom: var(--space-3); }
.form__options { display: flex; flex-wrap: wrap; gap: var(--space-3) var(--space-6); }
.form__options .form__check { align-items: center; }
.form__options .form__check input[type="radio"] { margin-top: 0; }
.form__options .form__check__label { color: var(--color-text); }

/* --- Site footer ------------------------------------------------------- */
/* Distinct dark surface block. Three columns — brand · explore · connect
   (signup + app) — over a prominent credit band, then the legal bottom row. */
.site-footer {
  border-top: var(--border-w) solid var(--color-border);
  background: var(--color-surface);
  padding-block: var(--section-md) var(--space-6);
}
.footer__grid {
  display: grid;
  grid-template-columns: 1.4fr 1fr 1.4fr;
  gap: var(--space-12) var(--space-10);
  align-items: start;
}
@media (max-width: 991px) {
  .footer__grid { grid-template-columns: 1fr 1fr; }
  .footer__connect { grid-column: 1 / -1; }
}
@media (max-width: 767px) {
  .footer__grid { grid-template-columns: 1fr; gap: var(--space-10); }
}

/* Brand column */
.footer__brand { display: grid; gap: var(--space-5); justify-items: start; }
.footer__brand .badge { width: 6rem; }
.footer__tagline { color: var(--color-text-muted); max-width: 24rem; }
.footer__lockup { margin-block: var(--space-1); border-left: 0; padding-left: 0; }

/* Column headings + nav */
.footer__col { display: grid; gap: var(--space-5); align-content: start; }
.footer__heading {
  font-family: var(--font-display);
  text-transform: uppercase;
  font-size: var(--fs-base);
  letter-spacing: 0.03em;
  color: var(--color-text);
}
.footer__heading--app { margin-top: var(--space-4); }
.footer__nav-list { display: grid; gap: var(--space-3); list-style: none; margin: 0; padding: 0; justify-items: start; }
/* Same hover as the bar nav: a 2px accent underline that grows from the left,
   muted text resolves to full white. */
.footer__nav-list a {
  position: relative;
  display: inline-block;
  font-family: var(--font-display);
  text-transform: uppercase;
  letter-spacing: 0.02em;
  color: var(--color-text-muted);
  text-decoration: none;
  padding-bottom: var(--space-1);
  transition: color var(--dur) var(--ease);
}
.footer__nav-list a::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 2px;
  background: var(--color-accent);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform var(--dur) var(--ease);
}
.footer__nav-list a:hover,
.footer__nav-list a:focus-visible { color: var(--color-text); }
.footer__nav-list a:hover::after,
.footer__nav-list a:focus-visible::after { transform: scaleX(1); }

/* Social icons — same circular icon-button family as .artist-social */
.footer__socials { display: flex; flex-wrap: wrap; gap: var(--space-3); }
.footer__socials a {
  width: 2.5rem;
  height: 2.5rem;
  display: grid;
  place-items: center;
  border: 1px solid var(--color-border-strong);
  border-radius: var(--radius-full);
  color: var(--color-text);
  transition: border-color var(--dur) var(--ease), color var(--dur) var(--ease), transform var(--dur) var(--ease), box-shadow var(--dur) var(--ease);
}
.footer__socials a:hover,
.footer__socials a:focus-visible {
  border-color: var(--color-accent);
  color: var(--color-accent);
  transform: translate(-3px, -3px);
  box-shadow: var(--shadow-hard);
}
.footer__socials svg { width: 1.15rem; height: 1.15rem; }

/* Connect column — signup form (reuses .field) + app badges */
.footer__connect-lead { color: var(--color-text-muted); max-width: 28rem; }
.footer__signup { display: flex; flex-wrap: wrap; gap: var(--space-3); width: 100%; max-width: 28rem; }
/* Turnstile widget + success/error status wrap to their own full-width row below the input + button. */
.footer__signup .cf-turnstile,
.footer__signup .form-status { flex: 1 0 100%; margin: 0; }
@media (max-width: 479px) { .footer__signup { flex-direction: column; } }
.footer__fine { font-size: var(--fs-xs); color: var(--color-text-muted); }
.footer__fine a { color: var(--color-text); text-decoration: underline; }
.footer__fine a:hover,
.footer__fine a:focus-visible { color: var(--color-accent); }

/* App store badges (small element family — accent hover lift) */
.footer__apps { display: flex; flex-wrap: wrap; gap: var(--space-3); }
.app-badge {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-2) var(--space-4);
  border: var(--border-w) solid var(--color-border-strong);
  border-radius: var(--radius-lg);
  background: var(--bb-black);
  color: var(--color-text);
  transition: border-color var(--dur) var(--ease), transform var(--dur) var(--ease), box-shadow var(--dur) var(--ease);
}
.app-badge:hover,
.app-badge:focus-visible {
  border-color: var(--color-accent);
  transform: translate(-2px, -2px);
  box-shadow: var(--shadow-hard-blue);
}
.app-badge__icon { width: 1.35rem; height: 1.35rem; flex-shrink: 0; fill: currentColor; }
.app-badge__text { display: grid; line-height: 1.1; text-align: left; }
.app-badge__small { font-size: var(--fs-xs); color: var(--color-text-muted); }
.app-badge__big {
  font-family: var(--font-display);
  text-transform: uppercase;
  font-size: var(--fs-base);
  letter-spacing: 0.01em;
}

/* Grofomo credit — the logo sits inline in the footer bottom bar (between the
   copyright and the legal links). Carries a hover/focus tooltip of services. */
.footer__credit-link {
  position: relative;
  display: inline-flex;
  align-items: center;
  transition: transform var(--dur) var(--ease);
}
.footer__credit-link:hover,
.footer__credit-link:focus-visible { transform: translateY(-2px); }
.footer__credit-logo { height: 1.5rem; width: auto; display: block; }

/* Tooltip — services revealed on hover/focus. Dot separators are CSS round
   boxes (rule #12), not glyphs. */
.footer__credit-tip {
  position: absolute;
  bottom: calc(100% + var(--space-3));
  left: 50%;
  display: inline-flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-2) var(--space-4);
  background: var(--color-surface-2);
  border: var(--border-w) solid var(--color-border-strong);
  border-radius: var(--radius-md);
  white-space: nowrap;
  font-family: var(--font-display);
  text-transform: uppercase;
  font-size: var(--fs-xs);
  letter-spacing: 0.06em;
  color: var(--color-text);
  opacity: 0;
  visibility: hidden;
  transform: translate(-50%, 4px);
  transition: opacity var(--dur) var(--ease), transform var(--dur) var(--ease), visibility var(--dur);
  pointer-events: none;
}
.footer__credit-link:hover .footer__credit-tip,
.footer__credit-link:focus-visible .footer__credit-tip {
  opacity: 1;
  visibility: visible;
  transform: translate(-50%, 0);
}
.footer__credit-sep {
  width: var(--space-1);
  height: var(--space-1);
  border-radius: var(--radius-full);
  background: var(--color-accent);
  flex-shrink: 0;
}

/* Bottom row — copyright · grofomo · legal */
.footer__bottom {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: var(--space-5);
  flex-wrap: wrap;
  margin-top: var(--space-8);
  padding-top: var(--space-6);
  border-top: 1px solid var(--color-border);
  font-size: var(--fs-sm);
  color: var(--color-text-muted);
}
.footer__legal { display: flex; gap: var(--space-5); flex-wrap: wrap; }
.footer__legal a:hover,
.footer__legal a:focus-visible { color: var(--color-accent); }
.footer__legal a[aria-current="page"] { color: var(--color-text); }

/* ==========================================================================
   Poster line-up — interactive festival-poster bill (home page).
   Flat, equal-weight names in the display face; each name is led by a small round
   avatar of that DJ (avatar + name lock as one inline unit, so the photo never
   orphans at a line break). The whole act is one link to the artist page; hover
   or focus reveals a CSS-only tooltip (square photo + optional blurb + a clickable
   "View profile" cue), with a transparent bridge spanning the gap so the pointer
   can reach it. No glyph bullets (rule #12) — the leading avatar of the next act
   is the natural separator. Accent cycles per act via data-accent (turbo default,
   then pink/green/blue), matching the lineup grid.
   ========================================================================== */
.poster__bill {
  margin: var(--space-10) 0 0;
  max-width: var(--maxw-article);
  margin-inline: auto;
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  align-items: center;
  /* resolve the gap in the same em context as the name so the gutter BETWEEN acts
     equals the gutter between an avatar and its name (both --poster-gap of the name size) */
  font-size: var(--poster-name);
  gap: var(--poster-gap);   /* equal vertical + horizontal gutter around avatars */
}
/* the whole act is the link to the artist page (so the tooltip + its View profile
   cue are clickable, not just the name) */
.poster__act {
  --act-accent: var(--color-accent);
  position: relative;
  z-index: 0;
  display: inline-flex;
  color: var(--color-text);
  text-decoration: none;
}
.poster__act[data-accent="pink"]  { --act-accent: var(--bb-pink); }
.poster__act[data-accent="green"] { --act-accent: var(--bb-green); }
.poster__act[data-accent="blue"]  { --act-accent: var(--bb-blue); }
.poster__act:hover,
.poster__act:focus-within { z-index: var(--z-popover); }   /* lift the active act so its tooltip clears siblings AND the sticky header */
.poster__act:focus-visible {
  outline: var(--border-w) solid var(--color-focus);
  outline-offset: 4px;
  border-radius: var(--radius-md);
}

.poster__name {
  display: inline-flex;
  align-items: center;
  gap: var(--poster-gap);
  font-family: var(--font-display);
  text-transform: uppercase;
  font-size: var(--poster-name);
  letter-spacing: var(--tracking-heading);
  line-height: 1;
  transition: color var(--dur) var(--ease);
}
.poster__act:hover .poster__name,
.poster__act:focus-within .poster__name { color: var(--act-accent); }
.poster__label { display: block; }

/* leading avatar — round DJ photo locked to the left of its name, optically
   centred on the caps; em-scaled so it tracks the fluid display size */
.poster__avatar {
  flex: none;
  width: var(--poster-avatar);
  height: var(--poster-avatar);
  border-radius: var(--radius-full);
  overflow: hidden;
  background: var(--color-surface-2);
  /* nudge to the cap optical centre — the line box sits a hair below it */
  margin-block: -0.04em 0.06em;
  transition: outline-color var(--dur) var(--ease);
  outline: var(--border-w) solid transparent;
  outline-offset: 2px;
}
.poster__act:hover .poster__avatar,
.poster__act:focus-within .poster__avatar { outline-color: var(--act-accent); }
.poster__avatar img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

/* tooltip — a small preview card centred above the name */
.poster__tip {
  position: absolute;
  bottom: calc(100% + var(--space-3));
  left: 50%;
  width: var(--poster-tip-w);
  max-width: calc(100vw - var(--space-8));   /* never exceed the viewport on narrow screens */
  display: flex;
  flex-direction: column;
  overflow: hidden;
  background: var(--color-surface-2);
  border: var(--border-w) solid var(--act-accent);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-soft);
  opacity: 0;
  visibility: hidden;
  transform: translate(-50%, 6px);
  transition: opacity var(--dur) var(--ease), transform var(--dur) var(--ease), visibility var(--dur);
  pointer-events: none;
}
.poster__act:hover .poster__tip,
.poster__act:focus-within .poster__tip {
  opacity: 1;
  visibility: visible;
  transform: translate(-50%, 0);
  pointer-events: auto;   /* clickable once shown — the act is the link */
}
/* invisible bridge across the gap so the pointer can travel name -> tooltip
   without crossing a dead zone that would close it */
.poster__tip::after {
  content: "";
  position: absolute;
  top: 100%;
  left: 0;
  right: 0;
  height: var(--space-3);
}
.poster__tip-media {
  aspect-ratio: 1 / 1;
  overflow: hidden;
  background: var(--color-surface);
}
.poster__tip-media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
.poster__tip-text {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  padding: var(--space-3) var(--space-4) var(--space-4);
  text-align: left;
}
.poster__tip-blurb {
  font-family: var(--font-body);
  font-size: var(--fs-sm);
  line-height: var(--lh-body);
  color: var(--color-text-muted);
}
/* click-through cue — accent label + arrow making it obvious the name links out.
   Arrow is a masked currentColor SVG (rule #13). */
.poster__tip-cta {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  font-family: var(--font-body);
  font-size: var(--fs-xs);
  font-weight: var(--fw-bold);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--act-accent);
}
.poster__tip-cta::after {
  content: "";
  width: 0.85em;
  height: 0.85em;
  background: currentColor;
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M5 12h14M13 6l6 6-6 6'/%3E%3C/svg%3E") center / contain no-repeat;
          mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M5 12h14M13 6l6 6-6 6'/%3E%3C/svg%3E") center / contain no-repeat;
  transition: transform var(--dur) var(--ease);
}
.poster__act:hover .poster__tip-cta::after,
.poster__act:focus-within .poster__tip-cta::after { transform: translateX(var(--space-1)); }
@media (prefers-reduced-motion: reduce) {
  .poster__tip-cta::after { transition: none; }
}

@media (max-width: 479px) {
  .poster__bill { gap: var(--space-1) var(--space-4); }
}
@media (prefers-reduced-motion: reduce) {
  .poster__name,
  .poster__tip { transition: opacity var(--dur) var(--ease); }
  .poster__tip { transform: translate(-50%, 0); }
}
