/* playajazz — site stylesheet
 * All colors, spacing, type and motion come from tokens.css.
 * Never write a hex value or a raw spacing number in this file.
 */

/* ══════════════════════════════════════════════════════════════
   1 · Reset + base
   ══════════════════════════════════════════════════════════════ */

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

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
}

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
}

body {
  margin: 0;
  background: var(--bg-primary);
  color: var(--text-on-dark);
  font-family: var(--font-body);
  font-size: 1.0625rem;
  line-height: 1.72;
  font-weight: 300;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

img, picture, video { display: block; max-width: 100%; height: auto; }

h1, h2, h3, h4 {
  font-family: var(--font-display);
  font-weight: 400;
  text-transform: uppercase;
  letter-spacing: var(--tracking-display);
  line-height: 1.12;
  margin: 0;
  text-wrap: balance;
}

p { margin: 0 0 var(--space-6); max-width: var(--measure); }

a { color: var(--color-accent); text-decoration: none; }

::selection { background: var(--color-primary); color: var(--bone-white); }

:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: 3px;
  border-radius: var(--radius-sm);
}

.visually-hidden {
  position: absolute; width: 1px; height: 1px;
  padding: 0; margin: -1px; overflow: hidden;
  clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

.skip-link {
  position: absolute; left: var(--space-4); top: -100px; z-index: 200;
  background: var(--bg-raised); color: var(--text-on-dark);
  padding: var(--space-3) var(--space-6);
  font-family: var(--font-utility); font-size: 0.8rem;
  border-radius: var(--radius-sm);
  transition: top var(--dur-fast) var(--ease-out);
}
.skip-link:focus { top: var(--space-4); }

/* ══════════════════════════════════════════════════════════════
   2 · The dust — a fine grain over every section.
       The logo already carries the speckle; this leans into it.
   ══════════════════════════════════════════════════════════════ */

.dust::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  opacity: 0.055;
  mix-blend-mode: screen;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='220' height='220'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='220' height='220' filter='url(%23n)'/%3E%3C/svg%3E");
  z-index: 2;
}

/* A slower, larger drift of dust for the hero only. */
.dust-drift::before {
  content: "";
  position: absolute;
  inset: -18%;
  pointer-events: none;
  opacity: 0.10;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='500' height='500'%3E%3Cfilter id='m'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.012' numOctaves='4'/%3E%3C/filter%3E%3Crect width='500' height='500' filter='url(%23m)'/%3E%3C/svg%3E");
  animation: drift 68s linear infinite alternate;
  z-index: 2;
}

@keyframes drift {
  from { transform: translate3d(-3%, -2%, 0) scale(1.05); }
  to   { transform: translate3d(3%, 2%, 0) scale(1.14); }
}

/* ══════════════════════════════════════════════════════════════
   3 · Layout primitives
   ══════════════════════════════════════════════════════════════ */

.wrap {
  width: min(100% - var(--space-12), var(--container));
  margin-inline: auto;
}
.wrap-narrow {
  width: min(100% - var(--space-12), var(--container-narrow));
  margin-inline: auto;
}

section { position: relative; }

.section {
  padding-block: var(--space-32);
  position: relative;
  isolation: isolate;
}
.section-tight { padding-block: var(--space-24); }

/* The atmosphere between sections: the sunset gradient, faded. */
.section-veil {
  background:
    radial-gradient(120% 80% at 50% 0%,
      rgba(84, 48, 90, 0.55) 0%,
      rgba(53, 38, 73, 0.30) 45%,
      transparent 100%),
    var(--bg-primary);
}
.section-deep { background: var(--bg-secondary); }

.eyebrow {
  font-family: var(--font-utility);
  font-size: 0.75rem;
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
  color: var(--color-accent);
  margin: 0 0 var(--space-6);
  display: block;
}

.lede {
  font-size: clamp(1.15rem, 1.9vw, 1.4rem);
  line-height: 1.6;
  color: var(--text-on-dark);
}

.muted { color: var(--text-mid); }

/* A line inside muted copy that gets to be said out loud. */
.say-it { color: var(--text-on-dark); font-style: italic; }
.faint { color: var(--text-light); }

.aside-note {
  font-family: var(--font-utility);
  font-size: 0.82rem;
  letter-spacing: 0.06em;
  color: var(--text-light);
  font-style: normal;
}

/* A centered block. Body copy keeps its readable measure, but that measure has
 * to be CENTRED too — otherwise a max-width paragraph hugs the left edge of its
 * container and everything inside it (a button especially) reads as off-centre
 * even though the text itself is centred. */
.centered { text-align: center; }
.centered > p,
.centered > h1,
.centered > h2,
.centered > .lede,
.centered > .beliefs { margin-inline: auto; }
.centered .btn { margin-inline: auto; }
p.centered { text-align: center; margin-inline: auto; }

hr.rule {
  border: 0;
  height: 1px;
  background: linear-gradient(90deg, transparent, var(--rule-warm), transparent);
  margin-block: var(--space-16);
}

/* ══════════════════════════════════════════════════════════════
   4 · Buttons + links — the warm stage light on hover
   ══════════════════════════════════════════════════════════════ */

.btn {
  display: inline-block;
  font-family: var(--font-display);
  font-size: 0.95rem;
  text-transform: uppercase;
  letter-spacing: var(--tracking-display);
  padding: var(--space-4) var(--space-12);
  border: 1px solid var(--rule-warm);
  border-radius: var(--radius-full);
  color: var(--text-on-dark);
  background: transparent;
  cursor: pointer;
  transition:
    box-shadow var(--dur-mid) var(--ease-out),
    border-color var(--dur-mid) var(--ease-out),
    color var(--dur-mid) var(--ease-out),
    background var(--dur-mid) var(--ease-out);
}
.btn:hover, .btn:focus-visible {
  color: var(--color-accent);
  border-color: var(--color-accent);
  box-shadow: var(--glow-warm);
}

.btn-solid {
  background: var(--color-primary);
  border-color: var(--color-primary);
  color: var(--bone-white);
}
.btn-solid:hover, .btn-solid:focus-visible {
  background: var(--sunset-coral);
  border-color: var(--sunset-coral);
  color: var(--ink-black);
}

.btn-lg { font-size: 1.1rem; padding: var(--space-6) var(--space-16); }

/* Inline text links get a slow underline that lights up. */
.link {
  color: var(--color-accent);
  border-bottom: 1px solid var(--rule-warm);
  transition: border-color var(--dur-mid) var(--ease-out),
              text-shadow var(--dur-mid) var(--ease-out);
}
.link:hover, .link:focus-visible {
  border-color: var(--color-accent);
  text-shadow: 0 0 18px rgba(252, 201, 110, 0.55);
}

/* ══════════════════════════════════════════════════════════════
   5 · The loader — a cat, a horn, dust slowly settling
   ══════════════════════════════════════════════════════════════ */

.loader {
  position: fixed;
  inset: 0;
  z-index: 300;
  display: grid;
  place-items: center;
  background: var(--bg-primary);
  transition: opacity var(--dur-slow) var(--ease-out),
              visibility var(--dur-slow) var(--ease-out);
}
.loader[hidden] { display: grid; }         /* keep it painted while it fades */
.loader.is-done { opacity: 0; visibility: hidden; pointer-events: none; }

.loader-cat {
  width: clamp(112px, 16vw, 168px);
  -webkit-mask-image: radial-gradient(ellipse 56% 52% at 50% 46%,
                        #000 40%, rgba(0,0,0,0.55) 66%, transparent 90%);
          mask-image: radial-gradient(ellipse 56% 52% at 50% 46%,
                        #000 40%, rgba(0,0,0,0.55) 66%, transparent 90%);
  animation: catBreathe 3.4s var(--ease-out) infinite alternate;
}
@keyframes catBreathe {
  from { transform: scale(0.97); opacity: 0.82; }
  to   { transform: scale(1.02); opacity: 1; }
}

.loader-word {
  margin-top: var(--space-8);
  font-family: var(--font-utility);
  font-size: 0.72rem;
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
  color: var(--text-light);
}

/* ══════════════════════════════════════════════════════════════
   5b · The Whiteout — a dust front rolls through the hero
   ──────────────────────────────────────────────────────────────
   Playa dust is DENSITY, not motes. In a real whiteout you do not
   see particles drifting past; you see the world dissolve into pale
   amber and the club keep playing inside it. So this is fog, not a
   particle field — three fixed turbulence textures at different
   scales, tinted with brand colours and slid horizontally.

   Not canvas: hundreds of drifting dots read as snow or confetti,
   which is exactly the festival-site gimmick the brief warns about,
   and a rAF loop would be the largest script on the site by an order
   of magnitude. Not animated feTurbulence either — animating
   baseFrequency re-rasterises the filter on the CPU every frame and
   visibly chugs on a phone. Everything below is transform and
   opacity only, so it composites on the GPU.

   It deliberately whites the hero out at its peak. That IS the
   moment. It is confined to the hero, never touches body copy, and
   passes no pointer events so the CTA stays clickable throughout.
   ══════════════════════════════════════════════════════════════ */

.storm {
  /* FIXED and viewport-wide, not confined to the hero: weather does not stop at
   * a section boundary, and the storm now rolls through every page. It sits
   * below the header (100) so the nav stays legible, and above everything else. */
  position: fixed;
  inset: 0;
  z-index: 90;
  pointer-events: none;
  opacity: 0;
  visibility: hidden;
  /* the flat front: bone warmed with coral, the light inside a whiteout */
  background: linear-gradient(90deg,
    rgba(var(--rgb-sunset-coral), 0.50) 0%,
    rgba(var(--rgb-bone-white),   0.60) 55%,
    rgba(var(--rgb-sunset-coral), 0.45) 100%);
}

.storm-layer {
  position: absolute;
  inset: -25% -60%;           /* headroom so the slide never shows an edge */
  background-image:
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='500' height='500'%3E%3Cfilter id='m'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.012' numOctaves='4'/%3E%3C/filter%3E%3Crect width='500' height='500' filter='url(%23m)'/%3E%3C/svg%3E"),
    linear-gradient(90deg,
      rgba(var(--rgb-sunset-coral), 0.85),
      rgba(var(--rgb-bone-white),   0.75));
  background-blend-mode: multiply;   /* the noise carves puffs out of the tint */
  mix-blend-mode: screen;
}
.storm-layer--a { --drift: 10%; background-size: 900px 900px, 100% 100%; opacity: 0.55; }
.storm-layer--b { --drift: 22%; background-size: 480px 480px, 100% 100%; opacity: 0.40; }
.storm-layer--c { --drift: 38%; background-size: 240px 240px, 100% 100%; opacity: 0.30; }

/* One typed line, alone in the emptiness, at the peak. */
.storm-line {
  position: absolute;
  top: 44%;
  left: 0; right: 0;
  margin: 0;
  max-width: none;
  text-align: center;
  font-family: var(--font-utility);
  font-size: clamp(0.72rem, 1.7vw, 0.9rem);
  letter-spacing: var(--tracking-wide);
  text-indent: var(--tracking-wide);
  text-transform: uppercase;
  color: var(--ink-black);    /* dark type on the bright haze */
  opacity: 0;
}

/* Driven from <body> rather than the hero, because the storm now exists on
 * every page and most pages have no hero at all. */
@media (prefers-reduced-motion: no-preference) {
  body.is-storming .storm {
    visibility: visible;
    animation: stormDensity 12s var(--ease-out) both;
  }
  body.is-storming .storm-layer {
    animation: stormSlide 12s linear both;
    will-change: transform;   /* only while it runs; no permanent layer */
  }
  body.is-storming .storm-line { animation: stormLine 12s var(--ease-out) both; }

  /* The cat can whistle one up. Mouse-discoverable easter egg only — it is a
   * decorative image and gates nothing, so it stays out of the tab order. */
  .hero-cat { pointer-events: auto; cursor: pointer; }
}

/* ── The cat blows through ────────────────────────────────────
 * Clicking the cat summons a storm, and then the cat itself comes at you:
 * huge, warping through the spectrum, and gone.
 *
 * It is a SEPARATE fixed element rather than the hero's own cat, because
 * .hero has overflow:hidden — scaling the real one would clip it to the
 * section instead of letting it fill the screen. It sits above the storm
 * so it arrives THROUGH the dust.
 */
.cat-blow {
  position: fixed;
  left: 50%;
  top: 50%;
  translate: -50% -50%;       /* kept off `transform`, which the animation owns */
  width: min(40vw, 440px);
  z-index: 95;
  pointer-events: none;
  opacity: 0;
  visibility: hidden;
  /* the same feather the hero cat uses, so no rectangle appears */
  -webkit-mask-image: radial-gradient(ellipse 56% 52% at 50% 46%,
                        #000 40%, rgba(0,0,0,0.55) 66%, transparent 90%);
          mask-image: radial-gradient(ellipse 56% 52% at 50% 46%,
                        #000 40%, rgba(0,0,0,0.55) 66%, transparent 90%);
}

@media (prefers-reduced-motion: no-preference) {
  body.is-blowing .cat-blow {
    visibility: visible;
    animation: catBlow 5.6s var(--ease-out) both;
  }
}

/* A continuous zoom with the hue swept round the wheel — no flashing, so it
 * is trippy without tripping WCAG 2.3.1. */
@keyframes catBlow {
  0%   { opacity: 0;    transform: scale(0.55) rotate(0deg);
         filter: blur(10px) saturate(1)   hue-rotate(0deg); }
  10%  { opacity: 1;    transform: scale(1.05) rotate(-2deg);
         filter: blur(0)    saturate(1.15) hue-rotate(10deg); }
  40%  { opacity: 0.95; transform: scale(3.6) rotate(5deg);
         filter: blur(2px)  saturate(2.2) hue-rotate(110deg); }
  68%  { opacity: 0.72; transform: scale(8.5) rotate(-7deg);
         filter: blur(9px)  saturate(2.8) hue-rotate(235deg); }
  100% { opacity: 0;    transform: scale(17) rotate(10deg);
         filter: blur(28px) saturate(3)   hue-rotate(350deg); }
}

/* The header's nav is light type. While the hero whites out behind it, that
 * type would be near-invisible for the four seconds of the peak — so the bar
 * takes its stuck (dark, blurred) treatment for the duration. It also reads
 * right: the club interior stays dark while the outside disappears. */
body.is-storming .site-header {
  background: rgba(var(--rgb-ink-black), 0.86);
  backdrop-filter: blur(14px) saturate(1.2);
  border-bottom-color: var(--rule);
}

@keyframes stormDensity {
  0%   { opacity: 0; }
  18%  { opacity: 0.55; }
  40%  { opacity: 0.95; }     /* peak whiteout */
  55%  { opacity: 0.92; }
  80%  { opacity: 0.30; }
  100% { opacity: 0; }
}
@keyframes stormSlide {
  from { transform: translate3d(calc(var(--drift) * -1), 0.5%, 0); }
  to   { transform: translate3d(var(--drift), -0.5%, 0); }
}
@keyframes stormLine {
  0%, 34%   { opacity: 0; }
  46%, 62%  { opacity: 0.82; }
  76%, 100% { opacity: 0; }
}

/* ══════════════════════════════════════════════════════════════
   6 · Header + navigation
   ══════════════════════════════════════════════════════════════ */

.site-header {
  position: fixed;
  inset: 0 0 auto 0;
  z-index: 100;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-6);
  padding: var(--space-6) clamp(var(--space-6), 4vw, var(--space-16));
  transition: background var(--dur-mid) var(--ease-out),
              backdrop-filter var(--dur-mid) var(--ease-out),
              border-color var(--dur-mid) var(--ease-out);
  border-bottom: 1px solid transparent;
}
.site-header.is-stuck {
  background: rgba(16, 10, 19, 0.86);
  backdrop-filter: blur(14px) saturate(1.2);
  border-bottom-color: var(--rule);
}

.brand {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  color: var(--text-on-dark);
  font-family: var(--font-display);
  font-size: 0.9rem;
  letter-spacing: var(--tracking-display);
  text-transform: uppercase;
  white-space: nowrap;
}
.brand img {
  width: 38px; height: 38px;
  border-radius: var(--radius-full);
  object-fit: cover;
}
.brand:hover img { box-shadow: var(--glow-soft); }

.nav { display: flex; align-items: center; gap: clamp(var(--space-4), 2.2vw, var(--space-12)); }

.nav a {
  font-family: var(--font-utility);
  font-size: 0.78rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--text-mid);
  padding-block: var(--space-2);
  border-bottom: 1px solid transparent;
  transition: color var(--dur-fast) var(--ease-out),
              border-color var(--dur-fast) var(--ease-out),
              text-shadow var(--dur-fast) var(--ease-out);
}
.nav a:hover, .nav a:focus-visible {
  color: var(--color-accent);
  text-shadow: 0 0 16px rgba(252, 201, 110, 0.5);
}
.nav a[aria-current="page"] {
  color: var(--text-on-dark);
  border-bottom-color: var(--color-primary);
}

.nav-toggle {
  display: none;
  background: transparent;
  border: 1px solid var(--rule);
  border-radius: var(--radius-sm);
  color: var(--text-on-dark);
  font-family: var(--font-utility);
  font-size: 0.72rem;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  padding: var(--space-2) var(--space-4);
  cursor: pointer;
}

@media (max-width: 860px) {
  .nav-toggle { display: block; }
  .nav {
    position: fixed;
    inset: 0 0 auto 0;
    flex-direction: column;
    align-items: flex-start;
    gap: var(--space-6);
    padding: calc(var(--space-24)) clamp(var(--space-6), 6vw, var(--space-16)) var(--space-16);
    background: rgba(16, 10, 19, 0.97);
    backdrop-filter: blur(18px);
    border-bottom: 1px solid var(--rule);
    transform: translateY(-101%);
    transition: transform var(--dur-mid) var(--ease-out);
    z-index: -1;
  }
  .nav.is-open { transform: translateY(0); }
  .nav a { font-size: 1rem; }
}

/* ══════════════════════════════════════════════════════════════
   7 · Hero
   ══════════════════════════════════════════════════════════════ */

.hero {
  position: relative;
  min-height: 100svh;
  display: grid;
  place-items: center;
  text-align: center;
  overflow: hidden;
  isolation: isolate;
}

.hero-media {
  position: absolute;
  inset: 0;
  z-index: -2;
}
.hero-media img {
  width: 100%; height: 100%;
  object-fit: cover;
  /* Frame on the neon and the tents, not the empty playa floor below them. */
  object-position: 50% 34%;
  /* The photo is the room; the gradient is the sky over it. */
  filter: saturate(0.92) contrast(1.08) brightness(0.62);
  transform: scale(1.06);
  animation: heroDrift 42s var(--ease-out) infinite alternate;
}
@keyframes heroDrift {
  from { transform: scale(1.06) translate3d(0, 0, 0); }
  to   { transform: scale(1.14) translate3d(0, -1.6%, 0); }
}

/* The signature gradient, sitting over the photo exactly like the logo sky. */
.hero-veil {
  position: absolute;
  inset: 0;
  z-index: -1;
  background:
    /* a scrim under the lockup so the type never fights the neon behind it */
    radial-gradient(58% 46% at 50% 58%,
      rgba(16, 10, 19, 0.62) 0%,
      rgba(16, 10, 19, 0.28) 55%,
      transparent 78%),
    /* the signature sky: coral at the top, melting to ink at the floor */
    linear-gradient(180deg,
      rgba(250, 161, 102, 0.20) 0%,
      rgba(150, 59, 80, 0.18) 28%,
      rgba(84, 48, 90, 0.42) 60%,
      rgba(16, 10, 19, 0.90) 86%,
      var(--ink-black) 100%);
}

.hero-inner {
  position: relative;
  z-index: 3;
  padding: clamp(var(--space-24), 13vh, var(--space-32)) var(--space-6)
           clamp(var(--space-16), 9vh, var(--space-24));
  display: grid;
  justify-items: center;
  gap: var(--space-6);
}
.hero-inner .btn { margin-top: var(--space-6); }

/* The hero lockup: the mascot standing in the sunset, and the name set in
 * type beneath it. The logo artwork carries its own sky, so it is NOT laid
 * over the photograph as a square — its edges are feathered away and only
 * the cat is kept. The wordmark is set in Poiret One, which is the closest
 * face to the logo's own lettering (see DECISIONS.md).
 */
.hero-lockup { display: grid; justify-items: center; gap: var(--space-4); }

.hero-cat {
  width: clamp(190px, 25vw, 300px);
  /* The mask radii are deliberately SMALLER than the distance from the centre
   * to each edge. Anything larger leaves the mid-edges partly opaque, and a
   * partly opaque straight edge is exactly what makes the artwork read as a
   * pasted rectangle — the corners fading is not enough on its own.
   * The tail tip dissolving into the dust is the intended cost. */
  -webkit-mask-image: radial-gradient(ellipse 56% 52% at 50% 46%,
                        #000 40%, rgba(0,0,0,0.55) 66%, transparent 90%);
          mask-image: radial-gradient(ellipse 56% 52% at 50% 46%,
                        #000 40%, rgba(0,0,0,0.55) 66%, transparent 90%);
}

.hero-wordmark {
  font-family: var(--font-display);
  font-size: clamp(2.3rem, 8.4vw, 5.6rem);
  line-height: 0.98;
  text-transform: uppercase;
  color: var(--bone-white);
  margin: 0;
  max-width: none;
  display: grid;
  justify-items: center;
  text-shadow: 0 6px 50px rgba(16, 10, 19, 0.85);
}
/* Tracking adds a phantom margin on the right of the last glyph; the matching
 * text-indent pulls the line back to true optical centre. */
.hero-wordmark span { letter-spacing: 0.22em; text-indent: 0.22em; }
.hero-wordmark span + span { font-size: 0.78em; letter-spacing: 0.3em; text-indent: 0.3em; }

.hero-title {
  font-family: var(--font-body);
  font-size: clamp(1.05rem, 2.2vw, 1.4rem);
  font-style: italic;
  font-weight: 300;
  text-transform: none;
  letter-spacing: 0.02em;
  line-height: 1.5;
  max-width: 24ch;
  color: var(--text-mid);
  text-shadow: 0 4px 40px rgba(16, 10, 19, 0.9);
}

/* The scroll cue — one slow, quiet motion. A line of falling light, no words:
 * the hero is already carrying the name, the headline and the button, and on a
 * short viewport a labelled cue lands on top of the button. */
.scroll-cue {
  position: absolute;
  bottom: var(--space-8);
  left: 50%;
  transform: translateX(-50%);
  z-index: 3;
  width: 1px;
  height: 52px;
  background: linear-gradient(180deg, transparent, var(--rule-warm) 40%, transparent);
  animation: cue 4.2s var(--ease-out) infinite;
}
@keyframes cue {
  0%, 100% { opacity: 0.2; transform: translateX(-50%) scaleY(0.55); transform-origin: top; }
  50%      { opacity: 0.9; transform: translateX(-50%) scaleY(1);    transform-origin: top; }
}

/* Interior pages get a shorter hero. */
.page-hero {
  position: relative;
  padding: calc(var(--space-32) + var(--space-16)) 0 var(--space-24);
  text-align: center;
  overflow: hidden;
  isolation: isolate;
  background:
    radial-gradient(140% 100% at 50% 0%,
      rgba(150, 59, 80, 0.42) 0%,
      rgba(84, 48, 90, 0.34) 40%,
      transparent 78%),
    var(--bg-primary);
}
.page-hero h1 { font-size: clamp(1.9rem, 5vw, 3.4rem); margin-inline: auto; max-width: 20ch; }
.page-hero .lede { margin: var(--space-8) auto 0; max-width: 56ch; color: var(--text-mid); }

/* ══════════════════════════════════════════════════════════════
   8 · The neon sign — the site's one orchestrated moment
   ──────────────────────────────────────────────────────────────
   The brief allows exactly one spectacular beat, and this is it: the
   manifesto as a real sign on the wall of the club.

   Built from layered text-shadow rather than WebGL. Neon is not geometry,
   it is BLOOM — a hot near-white core inside a wide coloured halo — and a
   stack of shadows renders that more convincingly, more crisply at any
   size, and for ~2KB instead of a 600KB dependency. Poiret One is doing
   real work here too: its hairline strokes already read as glass tubing.

   Before it lights, the text sits in unlit-glass grey, so the sign is
   visibly a SIGN rather than absent text that fades in.

   ⚠ Flash safety (WCAG 2.3.1): every flicker below is spaced so no more
   than 3 flashes land in any one second. Do not tighten these keyframes.
   ══════════════════════════════════════════════════════════════ */

.neon {
  display: grid;
  justify-items: center;
  gap: var(--space-16);
}

.neon-sign {
  position: relative;
  width: min(100%, 880px);
  padding: clamp(var(--space-16), 7vw, var(--space-32))
           clamp(var(--space-6), 5vw, var(--space-16));
  text-align: center;
  border-radius: var(--radius-lg);
  background:
    radial-gradient(130% 100% at 50% 0%, rgba(var(--rgb-plum), 0.30), transparent 72%),
    linear-gradient(180deg, rgba(var(--rgb-ink-black), 0.86), rgba(var(--rgb-ink-black), 0.99));
  border: 1px solid rgba(var(--rgb-bone-white), 0.09);
  box-shadow:
    inset 0 0 0 1px rgba(var(--rgb-bone-white), 0.04),
    inset 0 0 110px rgba(0, 0, 0, 0.92),
    0 50px 100px -60px rgba(0, 0, 0, 1);
  isolation: isolate;
}

/* The light the sign throws back onto the wall behind it. */
.neon-sign::before {
  content: "";
  position: absolute;
  inset: -16% -10%;
  z-index: -1;
  pointer-events: none;
  background: radial-gradient(58% 54% at 50% 52%,
    rgba(var(--rgb-sunset-coral), 0.17), transparent 74%);
  opacity: 0;
  transition: opacity 2s var(--ease-out);
}
.neon-sign.is-on::before { opacity: 1; animation: neonHum 7s ease-in-out infinite; }

/* Spill on the floor under the sign. */
.neon-sign::after {
  content: "";
  position: absolute;
  left: 50%;
  bottom: -5%;
  transform: translateX(-50%);
  width: 70%;
  height: 46px;
  z-index: -1;
  pointer-events: none;
  background: radial-gradient(ellipse at center,
    rgba(var(--rgb-amber-gold), 0.20), transparent 70%);
  filter: blur(12px);
  opacity: 0;
  transition: opacity 2.4s var(--ease-out);
}
.neon-sign.is-on::after { opacity: 1; }

.neon-line {
  /* --tube is the hot core, --halo the colour of the glow around it. */
  --tube: var(--bone-white);
  --halo: var(--rgb-sunset-coral);
  --tube-glow:
    0 0 2px  rgba(var(--rgb-bone-white), 0.95),
    0 0 9px  rgba(var(--halo), 0.85),
    0 0 20px rgba(var(--halo), 0.65),
    0 0 42px rgba(var(--halo), 0.45),
    0 0 84px rgba(var(--halo), 0.28);

  font-family: var(--font-display);
  font-size: clamp(1.3rem, 4.5vw, 2.85rem);
  text-transform: uppercase;
  letter-spacing: var(--tracking-display);
  text-indent: var(--tracking-display);   /* cancel the phantom right margin */
  line-height: 1.36;
  margin: 0;
  max-width: none;

  /* unlit glass */
  color: rgba(var(--rgb-bone-white), 0.12);
  text-shadow: none;
}

/* The payoff line burns warmer, like a different gas in the tube. */
.neon-line-key { --halo: var(--rgb-amber-gold); }

.neon-line.is-lit {
  color: var(--tube);
  text-shadow: var(--tube-glow);
  animation: neonStrike 1.9s var(--ease-out) both;
}
/* The last line keeps a slow electrical buzz once it has settled. */
.neon-line-key.is-lit {
  animation: neonStrike 1.9s var(--ease-out) both,
             neonBuzz 9s 2.4s ease-in-out infinite;
}

/* Striking a tube: it catches, drops out, catches again, then holds.
   Flashes land at ~0.13s, 0.63s, 1.10s, 1.37s — never 3 within one second. */
@keyframes neonStrike {
  0%,   6%  { color: rgba(var(--rgb-bone-white), 0.12); text-shadow: none; }
  7%,  10%  { color: var(--tube); text-shadow: var(--tube-glow); }
  11%, 32%  { color: rgba(var(--rgb-bone-white), 0.16); text-shadow: none; }
  33%, 37%  { color: var(--tube); text-shadow: var(--tube-glow); }
  38%, 57%  { color: rgba(var(--rgb-bone-white), 0.22); text-shadow: none; }
  58%, 61%  { color: var(--tube); text-shadow: var(--tube-glow); }
  62%, 71%  { color: rgba(var(--rgb-bone-white), 0.30); text-shadow: none; }
  72%, 100% { color: var(--tube); text-shadow: var(--tube-glow); }
}

/* One dip per nine seconds. A hum, not a strobe. */
@keyframes neonBuzz {
  0%, 90%, 100% { opacity: 1; }
  92%           { opacity: 0.55; }
  94%           { opacity: 1; }
}

@keyframes neonHum {
  0%, 100% { opacity: 1; }
  50%      { opacity: 0.82; }
}

/* ── The alternating message ──────────────────────────────────
 * A real club sign does not say one thing forever: it holds, swaps to a
 * second message, flashes it, and swaps back. The stack of three declarations
 * is the resting state; the aside cuts in over it, flashes, and clears.
 *
 * It is absolutely positioned over the stack rather than sitting below it, so
 * the two genuinely ALTERNATE in the same panel instead of the sign growing a
 * fourth line. It is lit from the start and controlled purely by opacity.
 *
 * ⚠ Flash safety (WCAG 2.3.1): the three flickers below land ~0.63s apart,
 * so no one-second window ever contains more than two. Do not tighten them.
 */
.neon-flash {
  position: absolute;
  inset: 0;
  display: grid;
  place-content: center;
  padding-inline: clamp(var(--space-6), 5vw, var(--space-16));
  font-size: clamp(1.1rem, 3.2vw, 2.1rem);
  letter-spacing: 0.09em;
  text-indent: 0.09em;
  line-height: 1.45;
  text-wrap: balance;
  /* a warmer tube again, so the swap reads as a different message */
  --halo: var(--rgb-ember-terracotta);
  color: var(--tube);
  text-shadow: var(--tube-glow);
  opacity: 0;
}

/* Each cycle re-strikes the three tubes ONE AT A TIME, the way the sign first
 * came on. The sequence is the character of the thing, so it belongs in the
 * loop rather than only in the opening four seconds.
 *
 * Written as three explicit keyframe sets rather than one set staggered with
 * animation-delay: a delay shifts the WHOLE cycle, which would slide each
 * line's blackout out of step with the swap. Explicit is longer and correct.
 *
 * 16s cycle:
 *   0.00-1.76s   the tubes strike in sequence
 *   1.76-8.32s   all three hold (one buzz on the payoff line at ~4.9s)
 *   8.32-8.96s   all three go out
 *   9.60-11.36s  the aside flashes three times
 *   11.36-14.5s  the aside holds, then clears
 *   14.5-16.0s   dark, and the sign resets
 */
/* ⚠ Scoped through .neon-stack on purpose. `.neon-flash` also carries the class
 * `neon-line`, and it is the SECOND CHILD of .neon-sign — so the unscoped
 * `.neon-sign.is-cycling .neon-line:nth-child(2)` matched the aside too, at
 * (0,4,0) against its own rule's (0,3,0). The aside won signLine2 and appeared
 * on top of the three declarations for the whole hold. Keep the .neon-stack in
 * these selectors. */
.neon-sign.is-cycling .neon-stack .neon-line:nth-child(1) { animation: signLine1 16s linear infinite; }
.neon-sign.is-cycling .neon-stack .neon-line:nth-child(2) { animation: signLine2 16s linear infinite; }
.neon-sign.is-cycling .neon-stack .neon-line:nth-child(3) { animation: signLine3 16s linear infinite; }
.neon-sign.is-cycling .neon-flash                         { animation: signSwap  16s linear infinite; }

@keyframes signLine1 {
  0%        { opacity: 0; }
  3%,  52%  { opacity: 1; }
  56%, 100% { opacity: 0; }
}
@keyframes signLine2 {
  0%,  4%   { opacity: 0; }
  7%,  52%  { opacity: 1; }
  56%, 100% { opacity: 0; }
}
@keyframes signLine3 {
  0%,  8%   { opacity: 0; }
  11%, 30%  { opacity: 1; }
  31%       { opacity: 0.5; }   /* one buzz on the payoff line, ~4.9s in */
  32%, 52%  { opacity: 1; }
  56%, 100% { opacity: 0; }
}

/* Flashes at 9.60s, 10.48s, 11.36s — 0.88s apart, so no one-second window ever
 * holds more than two (WCAG 2.3.1). Do not tighten these. */
@keyframes signSwap {
  0%,  58%  { opacity: 0; }
  60%       { opacity: 1; }
  62%       { opacity: 0.15; }
  65.5%     { opacity: 1; }
  67.5%     { opacity: 0.18; }
  71%, 88%  { opacity: 1; }
  91%, 100% { opacity: 0; }
}

/* ══════════════════════════════════════════════════════════════
   9 · The Tonight Board — a hand-chalked club board.
       The signature element. It shows ONLY tonight.
   ══════════════════════════════════════════════════════════════ */

.board-shell { display: grid; justify-items: center; }

.board {
  position: relative;
  width: min(100%, 640px);
  padding: var(--space-12) var(--space-8) var(--space-8);
  text-align: center;
  border-radius: var(--radius-sm);
  /* A slate, not a card: deep, matte, chalk-dusted. */
  background:
    radial-gradient(120% 90% at 50% 12%,
      rgba(84, 48, 90, 0.55) 0%,
      rgba(16, 10, 19, 0.94) 68%),
    var(--ink-black);
  border: 1px solid var(--rule);
  box-shadow:
    inset 0 0 0 8px rgba(16, 10, 19, 0.55),
    inset 0 0 70px rgba(0, 0, 0, 0.75),
    0 30px 70px -30px rgba(0, 0, 0, 0.9);
  isolation: isolate;
  overflow: hidden;
}

/* The chalk frame drawn just inside the slate. */
.board::before {
  content: "";
  position: absolute;
  inset: var(--space-4);
  border: 1px solid var(--rule-warm);
  border-radius: 2px;
  opacity: 0.5;
  pointer-events: none;
}

.board-label {
  font-family: var(--font-display);
  font-size: clamp(1.7rem, 5vw, 2.6rem);
  letter-spacing: 0.34em;
  text-indent: 0.34em;          /* optical: tracking adds a phantom right margin */
  text-transform: uppercase;
  color: var(--color-accent);
  text-shadow: 0 0 34px rgba(252, 201, 110, 0.4);
  margin: 0 0 var(--space-3);
}

.board-date {
  font-family: var(--font-utility);
  font-size: 0.76rem;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--text-light);
  margin: 0 0 var(--space-8);
}

.board-lines { display: grid; gap: var(--space-4); margin-bottom: var(--space-8); }

.board-line {
  font-family: var(--font-utility);
  font-size: clamp(1rem, 2.4vw, 1.28rem);
  letter-spacing: 0.05em;
  color: var(--bone-white);
  margin: 0;
  max-width: none;
}
.board-line .board-time { color: var(--color-primary); }

/* The roster is the news; the countdown is the point. Give it air. */
.board-countdown { margin-top: var(--space-6); }

.board-foot {
  font-family: var(--font-body);
  font-style: italic;
  font-size: 0.95rem;
  color: var(--text-mid);
  margin: 0;
  max-width: none;
}

/* Dark-week state: the club is not standing. Honest, still in voice. */
.board.is-dark .board-label { color: var(--text-mid); text-shadow: none; }

/* The board writes itself in: each line wipes left-to-right like a hand
 * drawing chalk, rather than the whole slate fading up as one block. The
 * signature element should look CHALKED, not rendered.
 *
 * These selectors carry 3 classes, so they layer clip-path on top of the
 * generic `.reveal-stagger > *` opacity/transform rule rather than replacing it.
 */
.js .board-lines.reveal-stagger > .board-line {
  clip-path: inset(0 100% 0 0);
  transition:
    opacity var(--dur-slow) var(--ease-out),
    transform var(--dur-slow) var(--ease-out),
    clip-path calc(var(--dur-slow) * 1.15) var(--ease-out);
}
.js .board-lines.reveal-stagger.is-in > .board-line { clip-path: inset(0 0 0 0); }

/* A gaslamp breath on the label — one slow cycle, never a flash. */
@keyframes boardGlow {
  0%, 100% { text-shadow: 0 0 34px rgba(var(--rgb-amber-gold), 0.40); }
  50%      { text-shadow: 0 0 48px rgba(var(--rgb-amber-gold), 0.60); }
}
.board-label { animation: boardGlow 5.6s var(--ease-out) infinite; }
.board.is-dark .board-label { animation: none; }

/* ══════════════════════════════════════════════════════════════
   10 · Belief statements, cards, grids
   ══════════════════════════════════════════════════════════════ */

.beliefs { list-style: none; padding: 0; margin: 0; display: grid; gap: var(--space-8); }
.beliefs li {
  font-family: var(--font-display);
  font-size: clamp(1.1rem, 2.6vw, 1.7rem);
  text-transform: uppercase;
  letter-spacing: 0.1em;
  line-height: 1.4;
  color: var(--bone-white);
  padding-left: var(--space-8);
  border-left: 1px solid var(--rule-warm);
}

.cards {
  display: grid;
  gap: var(--space-8);
  grid-template-columns: repeat(auto-fit, minmax(288px, 1fr));
}

.card {
  position: relative;
  padding: var(--space-12) var(--space-8);
  background: linear-gradient(180deg,
    rgba(84, 48, 90, 0.42) 0%,
    rgba(53, 38, 73, 0.30) 100%);
  border: 1px solid var(--rule);
  border-radius: var(--radius-md);
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
  isolation: isolate;
  transition: border-color var(--dur-mid) var(--ease-out),
              box-shadow var(--dur-mid) var(--ease-out),
              transform var(--dur-mid) var(--ease-out);
}
.card:hover, .card:focus-within {
  border-color: var(--rule-warm);
  box-shadow: var(--glow-soft);
  transform: translateY(-4px);
}
.card h3 { font-size: 1.25rem; color: var(--bone-white); }
.card p { color: var(--text-mid); margin: 0; flex: 1; }
.card .btn {
  align-self: stretch;
  text-align: center;
  margin-top: var(--space-4);
  padding-inline: var(--space-4);   /* the card is narrow; a long label must not wrap */
}

/* ══════════════════════════════════════════════════════════════
   11 · Schedule
   ══════════════════════════════════════════════════════════════ */

.mode-tag {
  font-family: var(--font-utility);
  font-size: 0.78rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--color-accent);
  border: 1px solid var(--rule-warm);
  border-radius: var(--radius-full);
  padding: var(--space-2) var(--space-6);
  display: inline-block;
  margin-bottom: var(--space-8);
}

.sessions { display: grid; gap: 0; margin: 0; }

.session {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--space-3);
  padding-block: var(--space-8);
  border-top: 1px solid var(--rule);
  transition: background var(--dur-mid) var(--ease-out);
}
.session:last-child { border-bottom: 1px solid var(--rule); }
.session:hover { background: rgba(84, 48, 90, 0.18); }

.session-name {
  font-family: var(--font-display);
  font-size: clamp(1.15rem, 2.4vw, 1.6rem);
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--bone-white);
  margin: 0;
}
.session-time {
  font-family: var(--font-utility);
  font-size: 0.82rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--color-primary);
  margin: 0;
}
.session p.session-body { color: var(--text-mid); margin: 0; }

@media (min-width: 780px) {
  .session {
    grid-template-columns: 190px minmax(0, 1fr);
    column-gap: var(--space-12);
    align-items: baseline;
  }
  .session-time { grid-row: span 2; }
}

/* ══════════════════════════════════════════════════════════════
   12 · Gallery
   ══════════════════════════════════════════════════════════════ */

.gallery {
  display: grid;
  gap: var(--space-4);
  grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
}
.gallery figure { margin: 0; position: relative; overflow: hidden; border-radius: var(--radius-sm); }
.gallery img {
  width: 100%;
  aspect-ratio: 4 / 3;
  object-fit: cover;
  filter: saturate(0.9) brightness(0.88);
  transition: transform var(--dur-slow) var(--ease-out),
              filter var(--dur-slow) var(--ease-out);
}
.gallery figure:hover img { transform: scale(1.05); filter: saturate(1) brightness(1); }
.gallery figcaption {
  position: absolute;
  inset: auto 0 0 0;
  padding: var(--space-8) var(--space-6) var(--space-4);
  font-family: var(--font-utility);
  font-size: 0.74rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--bone-white);
  background: linear-gradient(180deg, transparent, rgba(16, 10, 19, 0.88));
}
.gallery .span-2 { grid-column: span 2; }
@media (max-width: 700px) { .gallery .span-2 { grid-column: span 1; } }

.full-bleed {
  position: relative;
  width: 100vw;
  margin-left: 50%;
  transform: translateX(-50%);
  overflow: hidden;
  isolation: isolate;
}
.full-bleed img {
  width: 100%;
  max-height: 72svh;
  object-fit: cover;
  filter: saturate(0.88) brightness(0.72);
}

/* ══════════════════════════════════════════════════════════════
   13 · Listen — the player frame, and its honest empty state
   ══════════════════════════════════════════════════════════════ */

.player {
  border: 1px solid var(--rule);
  border-radius: var(--radius-md);
  background: linear-gradient(180deg,
    rgba(53, 38, 73, 0.44), rgba(16, 10, 19, 0.6));
  padding: var(--space-16) var(--space-8);
  text-align: center;
  display: grid;
  justify-items: center;
  gap: var(--space-6);
}

/* A still waveform: the recording that is not here yet. */
.waveform {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 4px;
  height: 72px;
  width: min(100%, 460px);
  /* Base opacity matters: under reduced motion the breath below is switched
   * off and this is the value that actually ships. */
  opacity: 0.6;
}
/* The bars are FROZEN, not bouncing. A waveform that dances while no audio
 * exists reads as a broken player; a still one reads as a recording waiting to
 * be played. Each bar keeps its own height because the negative animation-delay
 * in the markup parks it at a different phase of the curve — paused, so the
 * variety survives and the motion does not. */
.waveform span {
  flex: 1;
  max-width: 5px;
  /* The bars are empty elements, so without an explicit height the flex
   * container sizes them to their content — zero — and scaleY has nothing to
   * scale. This is why the trace rendered as blank space. */
  height: 100%;
  border-radius: var(--radius-full);
  background: linear-gradient(180deg, var(--sunset-coral), var(--plum));
  /* --h is baked into the markup, one value per bar. No animation: the bars
   * hold their shape. */
  transform: scaleY(var(--h, 0.5));
}

/* The trace as a whole breathes very slowly — dormant, not dead, and never
 * pretending that something is playing. */
.player .waveform { animation: waveDormant 9s var(--ease-out) infinite; }
@keyframes waveDormant {
  0%, 100% { opacity: 0.5; }
  50%      { opacity: 0.72; }
}

.empty-note {
  font-family: var(--font-utility);
  font-size: 0.8rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--text-light);
  margin: 0;
}

/* ══════════════════════════════════════════════════════════════
   14 · Placement strip — the club has wandered the city
   ══════════════════════════════════════════════════════════════ */

.wander {
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-3) var(--space-6);
  font-family: var(--font-utility);
  font-size: 0.86rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--text-light);
}
.wander li { display: flex; align-items: center; gap: var(--space-6); }
.wander li:not(:last-child)::after {
  content: "";
  width: 26px; height: 1px;
  background: var(--rule-warm);
}
.wander .is-now { color: var(--color-accent); }

/* The placement callout on the home page. */
.placement {
  text-align: center;
  display: grid;
  justify-items: center;
  gap: var(--space-4);
}
.placement .where {
  font-family: var(--font-display);
  font-size: clamp(1.5rem, 4vw, 2.4rem);
  text-transform: uppercase;
  letter-spacing: var(--tracking-display);
  color: var(--color-accent);
  margin: 0;
}
.pin {
  width: 34px; height: 34px;
  color: var(--color-primary);
  /* Twice, then rest. A map pin that bobs forever is festival-template energy. */
  animation: pinPulse 4.2s var(--ease-out) 2;
}
@keyframes pinPulse {
  0%, 100% { opacity: 0.55; transform: translateY(0); }
  50%      { opacity: 1;    transform: translateY(-5px); }
}

/* ══════════════════════════════════════════════════════════════
   15 · Footer
   ══════════════════════════════════════════════════════════════ */

.site-footer {
  position: relative;
  padding-block: var(--space-24) var(--space-12);
  background: var(--bg-primary);
  border-top: 1px solid var(--rule);
  isolation: isolate;
}
.footer-grid {
  display: grid;
  gap: var(--space-16);
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  align-items: start;
}
.footer-brand { display: grid; gap: var(--space-4); justify-items: start; }
.footer-brand img { width: 108px; border-radius: var(--radius-full); }
.footer-title {
  font-family: var(--font-display);
  font-size: 1.05rem;
  text-transform: uppercase;
  letter-spacing: var(--tracking-display);
  color: var(--bone-white);
  margin: 0;
}
.footer-nav { display: grid; gap: var(--space-3); }
.footer-nav a {
  font-family: var(--font-utility);
  font-size: 0.8rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--text-mid);
  transition: color var(--dur-fast) var(--ease-out);
}
.footer-nav a:hover, .footer-nav a:focus-visible { color: var(--color-accent); }

.colophon {
  margin-top: var(--space-16);
  padding-top: var(--space-8);
  border-top: 1px solid var(--rule);
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-4) var(--space-8);
  justify-content: space-between;
  font-family: var(--font-utility);
  font-size: 0.72rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--text-light);
}
.colophon p { margin: 0; max-width: none; }

/* ══════════════════════════════════════════════════════════════
   16 · Reveal on scroll — slow, subtle, and off entirely when the
        visitor has asked for less motion.
   ══════════════════════════════════════════════════════════════ */

/* ⚠ Gated on `.js`, set by an inline script in <head>. Without that gate a
 * failed or blocked site.js leaves every section below the hero at opacity 0
 * — the whole site blank. The gate means no-JS simply shows everything. */
.js .reveal {
  opacity: 0;
  transform: translateY(22px);
  transition:
    opacity var(--dur-slow) var(--ease-out),
    transform var(--dur-slow) var(--ease-out);
}
.js .reveal.is-in { opacity: 1; transform: none; }

/* Staggered children (the belief list on The Camp). */
.js .reveal-stagger > * {
  opacity: 0;
  transform: translateY(18px);
  transition:
    opacity var(--dur-slow) var(--ease-out),
    transform var(--dur-slow) var(--ease-out);
}
.js .reveal-stagger.is-in > * { opacity: 1; transform: none; }
.reveal-stagger.is-in > *:nth-child(1) { transition-delay: 0ms; }
.reveal-stagger.is-in > *:nth-child(2) { transition-delay: 380ms; }
.reveal-stagger.is-in > *:nth-child(3) { transition-delay: 760ms; }
.reveal-stagger.is-in > *:nth-child(4) { transition-delay: 1040ms; }
.reveal-stagger.is-in > *:nth-child(5) { transition-delay: 1280ms; }

@media (prefers-reduced-motion: reduce) {
  /* The alternating message carries real copy. With the cycle switched off it
   * would never be shown at all, so it stops overlaying and simply sits under
   * the stack as a static line. */
  .neon-flash {
    position: static;
    opacity: 1;
    padding: 0;
    margin-top: var(--space-8);
  }
  .neon-sign.is-cycling .neon-line { opacity: 1; }

  .js .reveal, .js .reveal-stagger > * {
    opacity: 1 !important;
    transform: none !important;
    transition: none !important;
  }
  .js .board-lines.reveal-stagger > .board-line { clip-path: inset(0 0 0 0) !important; }
  .board-label,
  .player .waveform,
  .neon-sign.is-cycling .neon-line,
  .neon-sign.is-cycling .neon-flash,
  .neon-line.is-lit,
  .neon-line-key.is-lit,
  .neon-sign.is-on::before,
  .hero-media img,
  .dust-drift::before,
  .loader-cat,
  .waveform span,
  .scroll-cue,
  .pin { animation: none !important; }
}
