/* ==========================================================================
   UNLEASHED — HARD CHROME · Tension / Release.
   Chrome steel chains are the only graphic material; violet is light hitting
   metal, not a glow. Three voices: Clash Display (the shout), JetBrains Mono
   (data/HUD), system sans (quiet body).
   Structure: fonts → tokens → reset → a11y → cursor/grain → chrome →
   components → page kit → faq → guards.
   ========================================================================== */

/* --------------------------------------------------------------------------
   1. Self-hosted webfonts (GDPR: no third-party requests).
   -------------------------------------------------------------------------- */
@font-face {
  font-family: 'Clash Display';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url('/assets/fonts/clash-display-500.woff2') format('woff2');
}
@font-face {
  font-family: 'Clash Display';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url('/assets/fonts/clash-display-600.woff2') format('woff2');
}
@font-face {
  font-family: 'Clash Display';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url('/assets/fonts/clash-display-700.woff2') format('woff2');
}
@font-face {
  font-family: 'JetBrains Mono';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('/assets/fonts/jetbrains-mono-regular.woff2') format('woff2');
}
@font-face {
  font-family: 'JetBrains Mono';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url('/assets/fonts/jetbrains-mono-500.woff2') format('woff2');
}
@font-face {
  font-family: 'JetBrains Mono';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url('/assets/fonts/jetbrains-mono-700.woff2') format('woff2');
}

/* --------------------------------------------------------------------------
   2. Tokens.
   --violet (#7A00DF) is the BRAND color: fills, large display, graphics only —
   never small body text on dark (≈3:1). Small-text accents use --violet-glow
   or --violet-pale (AA on bg).
   RULE: no raw rem paddings/margins outside the --s-N / --space-N tokens.
   -------------------------------------------------------------------------- */
:root {
  --bg:          #070310;
  --bg-tint:     #0b0618;   /* flat raised surface — one tint, no gradients */
  --ink:         #f1ecfa;
  --ink-mid:     rgba(241, 236, 250, 0.78);
  /* Caption/meta tone. Raised from 0.56 (≈3:1, failed AA at small sizes) to
     0.74 so even 12-13px captions clear WCAG-AA 4.5:1 on the near-black bg.
     Nothing functional should fall below this; truly decorative-only marks use
     an explicit lower-alpha value inline. */
  --ink-faint:   rgba(241, 236, 250, 0.74);
  --rule:        rgba(241, 236, 250, 0.10);
  --rule-2:      rgba(241, 236, 250, 0.22);

  --steel:       #c9c9d4;   /* chrome highlight tone */
  --steel-dim:   rgba(201, 201, 212, 0.34);

  --violet:      #7A00DF;   /* brand — fills / large display / graphics only */
  --violet-hot:  #9B3DFF;
  --violet-glow: #BC7BFF;   /* small-text accent, AA on bg */
  --violet-pale: #E2CCFF;
  --violet-deep: #3D0070;
  --violet-soft: rgba(122, 0, 223, 0.16);
  --live:        #FF2D9B;   /* "NOW LIVE" only */

  /* Chrome gradient — the only "gradient" allowed: light over steel.
     Glyph-fill rule ("violet is light hitting metal"): --chrome (cold steel) for
     utility/footer glyphs and for interactive controls AT REST; --chrome-violet
     (lit) for decorative/inline accents + list markers, and for controls only on
     hover/focus. Never light a primary control at rest. */
  --chrome: linear-gradient(160deg, #f6f6f8 0%, #9a9aa3 52%, #3b3b44 100%);
  --chrome-violet: linear-gradient(160deg, #ffffff 0%, #BC7BFF 52%, #7A00DF 100%);

  /* Reusable focus ring (WCAG 1.4.11 — 3:1 vs bg and component). */
  --focus: 0 0 0 2px var(--bg), 0 0 0 4px var(--violet-glow);

  --display: 'Clash Display', ui-sans-serif, system-ui, -apple-system, sans-serif;
  --mono:    'JetBrains Mono', ui-monospace, 'SF Mono', Menlo, Consolas, monospace;
  --sans:    ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;

  /* Type scale. */
  --fs-hero:  clamp(3.5rem, 9vw, 7.5rem);
  --fs-h1:    clamp(2.4rem, 6vw, 4rem);
  --fs-h2:    clamp(1.5rem, 3vw, 2.1rem);
  --fs-h3:    clamp(1.1rem, 2vw, 1.3rem);
  --fs-lead:  clamp(1.1rem, 2vw, 1.3rem);
  --fs-body:  1.0625rem;
  --fs-small: 0.875rem;
  /* Functional mono-label baseline: 13px for legibility (was 0.72rem / 11.52px,
     then 0.75rem / 12px). Eyebrows/kickers keep their uppercase + tracking look
     but stay comfortably above the small-caps legibility floor (raters: eyebrows
     >=13px). */
  --fs-label: 0.8125rem;

  /* Spacing scale. */
  --s-1: 0.25rem;
  --s-2: 0.5rem;
  --s-3: 0.75rem;
  --s-4: 1rem;
  --s-5: 1.5rem;
  --s-6: 2.25rem;
  --s-7: 3.5rem;
  --space-section: clamp(4.5rem, 11vh, 8rem);
  --space-block:   clamp(2.25rem, 5vh, 3.75rem);
  --space-card:    clamp(1.5rem, 3vw, 2.25rem);

  --radius:  2px;
  --gutter:  clamp(1.2rem, 4vw, 2rem);
  --max-w:   1160px;   /* site chrome / hero */
  --prose-w: 760px;    /* .page__inner reading column */
  --head-h:  76px;

  /* Motion. One named curve + a small duration set, aliasing the shared
     --undr-ease contract (undr-base.css, loaded first) so the brand sheet stops
     repeating the cubic-bezier literal inline. --dur-press is the snappy
     ease-out used for button :active scale release. */
  --ease-out:  var(--undr-ease);   /* var(--ease-out) */
  --dur-press: 120ms;
  --dur-fast:  160ms;
  --dur-base:  200ms;
}

/* --undr-* canonical token contract → UNLEASHED values (see UNDR Core
   undr-base.css / docs/token-contract). Aliases the chrome/violet palette onto
   the shared vocabulary so structural CSS can migrate to var(--undr-*)
   incrementally. Additive — no rule consumes these yet, so nothing changes. */
:root {
  --undr-bg:          var(--bg);
  --undr-bg-tint:     var(--bg-tint);
  --undr-surface-1:   var(--bg-tint);
  --undr-ink:         var(--ink);
  --undr-ink-mid:     var(--ink-mid);
  --undr-ink-faint:   var(--ink-faint);
  --undr-rule:        var(--rule);
  --undr-rule-2:      var(--rule-2);
  --undr-accent:      var(--violet);
  --undr-accent-hot:  var(--violet-hot);
  --undr-accent-soft: var(--violet-soft);
  --undr-live:        var(--live);
  --undr-font-display: var(--display);
  --undr-font-sans:   var(--sans);
  --undr-font-mono:   var(--mono);
  --undr-fs-hero:     var(--fs-hero);
  --undr-fs-2xl:      var(--fs-h1);
  --undr-fs-xl:       var(--fs-h2);
  --undr-fs-lg:       var(--fs-h3);
  --undr-fs-md:       var(--fs-body);
  --undr-fs-sm:       var(--fs-small);
  --undr-fs-xs:       var(--fs-label);
  --undr-gutter:      var(--gutter);
  --undr-max-w:       var(--max-w);
  --undr-prose-w:     var(--prose-w);
  --undr-radius:      var(--radius);
  --undr-focus:       var(--focus);
}

/* --------------------------------------------------------------------------
   3. Reset / base.
   -------------------------------------------------------------------------- */
* { box-sizing: border-box; }

html, body {
  margin: 0;
  padding: 0;
  background: var(--bg);
  color: var(--ink);
  font-family: var(--sans);
  font-size: 16px;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}
/* Horizontal-scroll guard on BOTH html and body: overflow-x:clip keeps any
   intentional bleed (hero strands, footer chain band) from ever widening the
   document or producing a sideways scrollbar, and — unlike overflow:hidden —
   leaves the sticky header working. */
html { overflow-x: clip; }
body { min-height: 100dvh; overflow-x: clip; font-size: var(--fs-body); }
body.modal-open, body.nav-locked { overflow: hidden; }
a { color: inherit; }
img, svg, video { max-width: 100%; height: auto; }

/* --------------------------------------------------------------------------
   4. Accessibility primitives.
   -------------------------------------------------------------------------- */
:focus-visible {
  outline: 2px solid var(--violet-glow);
  outline-offset: 3px;
  border-radius: var(--radius);
}

.skip-link {
  position: fixed;
  top: var(--s-2);
  left: var(--s-2);
  z-index: 1000;
  background: var(--violet);
  color: var(--ink);
  padding: var(--s-3) var(--s-4);
  font-family: var(--mono);
  font-size: var(--fs-label);
  font-weight: 700;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  text-decoration: none;
  transform: translateY(-200%);
  transition: transform 160ms ease;
}
.skip-link:focus { transform: translateY(0); outline: 2px solid var(--ink); outline-offset: 2px; }

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

/* --------------------------------------------------------------------------
   5. Cursor chain canvas + film grain.
   #chain-cursor is created by chains.js (desktop, fine pointer, motion-ok).
   -------------------------------------------------------------------------- */
#chain-cursor {
  position: fixed;
  inset: 0;
  width: 100%;
  height: 100%;
  z-index: 80;
  pointer-events: none;
  display: block;
  opacity: 1;
  transition: opacity 700ms ease;
}
#chain-cursor.is-idle { opacity: 0; }

.grain {
  position: fixed;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  opacity: 0.04;
  mix-blend-mode: overlay;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='160' height='160'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='0.92' numOctaves='2' stitchTiles='stitch'/></filter><rect width='100%25' height='100%25' filter='url(%23n)'/></svg>");
}
/* The grain is a decorative GPU-blended overlay (mix-blend-mode + feTurbulence).
   It costs the most and helps the least on touch devices and under reduced motion
   — drop it there, mirroring how the cursor chain is gated. */
@media (hover: none), (prefers-reduced-motion: reduce) {
  .grain { display: none; }
}

/* Chain photographic divider — shared by .chain-rule / footer band / hr. */
.chain-rule {
  height: clamp(20px, 2.6vw, 28px);
  margin: 0 0 var(--space-block);
  border: 0;
  background: url('/assets/chains/divider.webp') repeat-x center / auto 100%;
  opacity: 0.5;
  -webkit-mask-image: linear-gradient(90deg, transparent 0, #000 7%, #000 93%, transparent 100%);
          mask-image: linear-gradient(90deg, transparent 0, #000 7%, #000 93%, transparent 100%);
}
/* Tighter on mobile — keep the identity, lose the vertical bulk. */
@media (max-width: 600px) {
  .chain-rule { height: 16px; margin-bottom: var(--space-block); }
}

/* --------------------------------------------------------------------------
   6. Header chrome — sticky bar, brand lockup, nav, lang switch.
   -------------------------------------------------------------------------- */
.site-head {
  position: sticky;
  top: 0;
  z-index: 40;
  background: rgba(7, 3, 16, 0.82);
  backdrop-filter: blur(14px) saturate(1.05);
  -webkit-backdrop-filter: blur(14px) saturate(1.05);
  border-bottom: 1px solid var(--rule);
}
.site-head__inner {
  max-width: var(--max-w);
  margin: 0 auto;
  padding: 0 var(--gutter);
  min-height: var(--head-h);
  display: flex;
  align-items: center;
  gap: var(--s-5);
}

.brand {
  display: inline-flex;
  flex: none;                    /* never squeeze the wordmark under the nav */
  align-items: center;
  gap: var(--s-3);
  text-decoration: none;
  padding: var(--s-2) 0;
  min-height: 44px;
}
/* Real two-tone handcuff mark, leading the wordmark (120×98 source, ~26px). */
.brand__mark {
  display: block;
  flex: none;
  width: auto;
  height: 26px;
  transition: filter 160ms ease, transform 200ms ease;
}
.brand:hover .brand__mark { filter: brightness(1.12); transform: rotate(-3deg); }
@media (max-width: 1180px) and (min-width: 961px) {
  .brand__mark { height: 22px; }
}
@media (max-width: 480px) {
  .brand__mark { height: 22px; }
}
/* The wordmark + sub sit on a shared baseline; the mark is centered to the pair. */
.brand__lockup {
  display: inline-flex;
  align-items: baseline;
  gap: var(--s-2);
}
/* Wordmark image (980×96 source — rendered ≤28px tall, retina-sharp). */
.brand__logo {
  display: block;
  flex: none;
  width: auto;
  height: 26px;
  transition: filter 160ms ease;
}
.brand:hover .brand__logo { filter: brightness(1.18); }
@media (max-width: 1180px) and (min-width: 961px) {
  .brand__logo { height: 22px; }
}
@media (max-width: 480px) {
  .brand__logo { height: 20px; }
}
/* ≤430px: the mark + wordmark + "by UNDR" + 44px toggle no longer fit inside
   the gutters on common phones (390–414px) — drop the sub-brand, keep the
   wordmark + mark untouched. */
@media (max-width: 430px) {
  .brand__sub { display: none; }
}
/* ≤360px: shrink the lockup itself so the mark + wordmark + toggle still clear. */
@media (max-width: 360px) {
  .brand__logo { height: 18px; }
  .brand__mark { height: 18px; }
}
.brand__sub {
  font-family: var(--mono);
  font-weight: 500;
  font-size: 0.6rem;
  letter-spacing: 0.3em;
  text-transform: uppercase;
  color: var(--ink-faint);
  white-space: nowrap;
}

.nav {
  display: flex;
  align-items: center;
  gap: var(--s-5);
  margin-left: auto;
}
.nav__list {
  display: flex;
  align-items: center;
  gap: var(--s-1);
  list-style: none;
  margin: 0;
  padding: 0;
}
.nav__link {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  padding: 0 var(--s-4);
  font-family: var(--mono);
  font-size: 0.78rem;
  font-weight: 500;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  white-space: nowrap;
  color: var(--ink);
  text-decoration: none;
  position: relative;
  transition: color 160ms ease;
}
/* Mid widths (961-1500px): same nav, tighter tracking so all 8 items fit
   next to the wordmark in both languages (DE labels run longer). */
@media (max-width: 1500px) and (min-width: 961px) {
  .nav__link { padding: 0 var(--s-3); letter-spacing: 0.1em; }
  .nav__link::after { left: var(--s-3); right: var(--s-3); }
  .nav { gap: var(--s-3); }
  .lang-switch { padding-left: var(--s-3); }
  .site-head__inner { gap: var(--s-4); }
}
.nav__link::after {
  content: '';
  position: absolute;
  left: var(--s-4);
  right: var(--s-4);
  bottom: 8px;
  height: 2px;
  background: var(--violet);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform 200ms ease;
}
.nav__link:hover { color: var(--ink); }
.nav__link:hover::after { transform: scaleX(1); }
.nav__link--active { color: var(--ink); }
.nav__link--active::after { transform: scaleX(1); }

.lang-switch {
  display: inline-flex;
  align-items: center;
  gap: var(--s-2);
  font-family: var(--mono);
  font-size: 0.78rem;
  letter-spacing: 0.1em;
  border-left: 1px solid var(--rule-2);
  padding-left: var(--s-5);
}
.lang-switch__sep { color: var(--rule-2); }
.lang-switch__link {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 44px;
  min-width: 44px;
  color: var(--ink-mid);
  text-decoration: none;
  padding: var(--s-3) var(--s-2);     /* >=44px effective target */
  transition: color 160ms ease;
}
.lang-switch__link:hover { color: var(--violet-glow); }
.lang-switch__link[aria-current='true'] { color: var(--ink); font-weight: 700; }

/* Hamburger — hidden on desktop. */
.nav-toggle {
  display: none;
  appearance: none;
  flex: none;               /* keep the full 44px tap target at 320px */
  margin-left: auto;
  width: 44px;
  height: 44px;
  background: transparent;
  border: 1px solid var(--rule-2);
  border-radius: var(--radius);
  cursor: pointer;
  align-items: center;
  justify-content: center;
  color: var(--ink);
  transition: border-color 160ms ease, background 160ms ease;
}
.nav-toggle:hover { border-color: var(--violet-hot); background: var(--violet-soft); }
.nav-toggle__bars,
.nav-toggle__bars::before,
.nav-toggle__bars::after {
  content: '';
  display: block;
  width: 18px;
  height: 2px;
  background: currentColor;
  position: relative;
  transition: transform 200ms ease, opacity 200ms ease;
}
.nav-toggle__bars::before { position: absolute; top: -6px; }
.nav-toggle__bars::after  { position: absolute; top: 6px; }
.site-head.nav-open .nav-toggle__bars { background: transparent; }
.site-head.nav-open .nav-toggle__bars::before { transform: translateY(6px) rotate(45deg); }
.site-head.nav-open .nav-toggle__bars::after  { transform: translateY(-6px) rotate(-45deg); }

/* Hamburger threshold 1120px: the dense 8-item horizontal nav (incl. Shop) +
   language switch + Buy-Tickets CTA only fits the 1160px header column from
   ~1121px up (the late nav-compaction block trims spacing + the brand there); so
   below 1120px we use the dropdown instead of clipping the CTA off-canvas. */
@media (max-width: 1120px) {
  .nav-toggle { display: inline-flex; }
  .nav {
    display: none;
    position: absolute;
    top: 100%;
    left: 0;
    right: 0;
    flex-direction: column;
    align-items: stretch;
    gap: 0;
    margin: 0;
    padding: var(--s-2) var(--gutter) var(--s-5);
    background: rgba(7, 3, 16, 0.97);
    backdrop-filter: blur(18px);
    -webkit-backdrop-filter: blur(18px);
    border-bottom: 1px solid var(--rule-2);
  }
  .site-head.nav-open .nav { display: flex; }
  .nav__list { flex-direction: column; align-items: stretch; gap: 0; }
  .nav__list li { border-bottom: 1px solid var(--rule); }
  .nav__link {
    width: 100%;
    min-height: 48px;
    padding: 0 var(--s-1);
    font-size: 0.78rem;
  }
  .nav__link::after { display: none; }
  .nav__link--active { color: var(--violet-glow); }
  .lang-switch {
    border-left: 0;
    padding: var(--s-3) var(--s-1) 0;
    margin: 0;
  }
}

/* --------------------------------------------------------------------------
   7. Footer chrome — photographic chain band as the divider.
   -------------------------------------------------------------------------- */
.site-foot {
  position: relative;
  z-index: 2;
  margin-top: var(--space-section);
  background: transparent;
}
.site-foot::before {
  content: '';
  display: block;
  /* Tighter chain band so the mechanical repeat reads as a sharp divider, not a
     long decorative strip (raters: chain band felt long/mechanical). */
  height: clamp(22px, 3vw, 32px);
  background: url('/assets/chains/divider.webp') repeat-x center / auto 100%;
  -webkit-mask-image: linear-gradient(90deg, transparent 0, #000 6%, #000 94%, transparent 100%);
          mask-image: linear-gradient(90deg, transparent 0, #000 6%, #000 94%, transparent 100%);
  filter: drop-shadow(0 10px 30px rgba(122, 0, 223, 0.28));
}
.site-foot__inner {
  max-width: var(--max-w);
  margin: 0 auto;
  padding: var(--space-block) var(--gutter) calc(env(safe-area-inset-bottom, 0px) + var(--s-6));
}

/* Three-zone band at ≥1024px: [newsletter | legal/nav | brand block]. Stacks to
   one column below. The newsletter cell carries no width cap — it fills its
   track and breathes. */
.site-foot__grid {
  display: grid;
  gap: var(--s-7) var(--s-6);
  grid-template-columns: 1fr;
}
@media (min-width: 1024px) {
  .site-foot__grid {
    grid-template-columns: minmax(0, 1.5fr) minmax(0, 0.7fr) minmax(0, 1.1fr);
    align-items: start;
    gap: var(--s-6) clamp(2rem, 5vw, 4rem);
  }
}

.foot-newsletter { max-width: none; }
.foot-newsletter__title {
  margin: 0 0 var(--s-2);
  font-family: var(--display);
  font-weight: 600;
  font-size: var(--fs-h2);
  letter-spacing: 0.01em;
  line-height: 1.1;
}
.foot-newsletter__hint {
  margin: 0 0 var(--s-5);
  color: var(--ink-mid);
  font-size: var(--fs-small);
}

/* Brand block: real BY UNDR vector above the social row. */
.foot-brand {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--s-5);
}
@media (min-width: 1024px) {
  .foot-brand { align-items: flex-end; text-align: right; }
}
.foot-brand__logo {
  display: inline-flex;
  text-decoration: none;
  transition: filter 160ms ease, opacity 160ms ease;
}
.foot-brand__logo img {
  display: block;
  width: auto;
  height: clamp(52px, 6vw, 64px);   /* real BY UNDR vector, bigger */
}
.foot-brand__logo:hover { filter: brightness(1.15); }
.foot-brand__logo:focus-visible {
  outline: none;
  box-shadow: var(--focus);
  border-radius: var(--radius);
}

/* Two grouped social rows: UNLEASHED + UNDR, each with its IG / Telegram. */
.foot-socials {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-5) clamp(1.5rem, 4vw, 2.5rem);
}
@media (min-width: 1024px) {
  .foot-brand { align-items: flex-end; text-align: right; }
  .foot-socials { justify-content: flex-end; }
  .foot-socials__group { align-items: flex-end; }
}
.foot-socials__group {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--s-1);
}
.foot-socials__label {
  font-family: var(--mono);
  /* 12px minimum (was 0.58rem / 9.28px) — keeps the uppercase mono-label look. */
  font-size: 0.76rem;
  font-weight: 700;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: var(--ink-mid);
  margin-bottom: var(--s-1);
}

.foot-social {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-2) var(--s-5);
}
.foot-social__link {
  font-family: var(--mono);
  /* 13px (was 0.78rem / 12.5px) + a >=44px tap target so adjacent footer links
     never collide on mobile (raters: sub-44px footer targets sitting close). */
  font-size: 0.8125rem;
  font-weight: 500;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--ink-mid);
  text-decoration: none;
  padding: var(--s-3) 0;
  min-height: 44px;
  display: inline-flex;
  align-items: center;
  gap: var(--s-2);
  transition: color 160ms ease;
}
.foot-social__link::before {
  content: '';
  flex: none;
  width: 19px;                   /* double-link glyph, 128:96 viewBox */
  height: 14px;
  background: var(--chrome);
  -webkit-mask: url('/assets/chains/link-closed.svg') center / contain no-repeat;
          mask: url('/assets/chains/link-closed.svg') center / contain no-repeat;
}
.foot-social__link:hover { color: var(--violet-glow); }

/* Legal/nav link column — stacked links in its own grid zone. */
.foot-legal {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-1) var(--s-5);
}
@media (min-width: 1024px) {
  .foot-legal { flex-direction: column; gap: 0; }
}
.foot-legal a {
  font-family: var(--mono);
  /* Legal/utility link: 13px (was 0.78rem / 12.5px) + a >=44px tap target so
     footer links are legible and never collide on mobile. */
  font-size: 0.8125rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-mid);
  text-decoration: none;
  padding: var(--s-3) var(--s-3) var(--s-3) 0;
  min-height: 44px;
  display: inline-flex;
  align-items: center;
  transition: color 160ms ease;
}
.foot-legal a:hover { color: var(--ink); }

/* --------------------------------------------------------------------------
   8. Buttons + forms. Flat steel, 2px radius, no outer glows.
   -------------------------------------------------------------------------- */
.btn {
  appearance: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--s-2);
  min-height: 48px;
  padding: var(--s-3) var(--s-6);
  font-family: var(--mono);
  font-size: var(--fs-label);
  font-weight: 700;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  text-decoration: none;
  color: var(--ink);
  background: var(--bg-tint);
  border: 1px solid var(--rule-2);
  border-radius: var(--radius);
  cursor: pointer;
  transition: color 200ms ease, border-color 200ms ease, background 200ms ease, transform var(--dur-press) var(--ease-out);
}
.btn:hover {
  border-color: var(--violet-hot);
  background: var(--violet-soft);
  color: var(--violet-pale);
}
/* Press feedback: a subtle squeeze (scales the label/icon with it) reads as
   "the interface heard you". ease-out front-loads so the press is instant while
   the release glides back over --dur-press. */
.btn:active { transform: scale(0.97); }
.btn:focus-visible { outline: 2px solid var(--ink); outline-offset: 3px; }

.btn--primary {
  background: var(--violet);
  border-color: var(--violet);
  color: var(--ink);
}
.btn--primary:hover {
  background: var(--violet-hot);
  border-color: var(--violet-hot);
  color: #0c0418;
}
/* Standardized ghost button (shared across rules/dresscode/about/contact bottom
   CTAs AND the tickets-page info buttons): transparent fill, a full-opacity
   violet-deep border (>=3:1) and a #C4A6F0+ label (>=4.5:1) so a secondary
   action never sinks below AA on near-black (raters #4/#30). */
.btn--ghost {
  background: transparent;
  /* Full-opacity brand-violet border (raters #4 asked for #7A00DF). The bright
     #E2CCFF label carries the AA affordance (13.9:1); hover lifts to the more
     luminous violet-hot for a clear 3:1 component state. */
  border-color: var(--violet);
  color: var(--violet-pale);
}
.btn--ghost:hover,
.btn--ghost:focus-visible {
  border-color: var(--violet-hot);
  background: var(--violet-soft);
  color: var(--violet-pale);
}

/* Mid-page sticky buy CTA for high-intent pages (dress code): floats bottom-
   right so a converted reader can act without scrolling to the header. Hidden
   on very small screens where the footer CTA is already close. */
.sticky-cta {
  position: fixed;
  right: var(--gutter);
  bottom: var(--gutter);
  z-index: 40;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 48px;
  padding: var(--s-3) var(--s-6);
  font-family: var(--mono);
  font-size: var(--fs-label);
  font-weight: 700;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  text-decoration: none;
  color: var(--ink);
  background: var(--violet);
  border: 1px solid var(--violet);
  border-radius: var(--radius);
  box-shadow: 0 6px 24px rgba(0, 0, 0, 0.5), 0 0 0 1px rgba(188, 123, 255, 0.25);
  transition: background 200ms ease, opacity 240ms var(--ease-out), transform 240ms var(--ease-out);
}
.sticky-cta:hover { background: var(--violet-hot); border-color: var(--violet-hot); }
.sticky-cta:focus-visible { outline: none; box-shadow: var(--focus); }
.sticky-cta:active { transform: scale(0.97); }
/* Stowed — JS hides the floating pill while the homepage hero's own Buy-Tickets
   is on screen, then reveals it once the hero is scrolled past, so the fixed pill
   never overlaps the hero CTAs. Other pages / no-JS: the class is never added, so
   the pill shows as before. */
.sticky-cta--stowed {
  opacity: 0;
  visibility: hidden;
  transform: translateY(140%);
  pointer-events: none;
}
@media (prefers-reduced-motion: reduce) {
  .sticky-cta { transition: background 200ms ease; }
}
/* Below the column breakpoint the floating pill overlapped the comparison card
   (it reserved no space). Dock it as a full-width translucent bar pinned to the
   bottom edge and reserve matching room on the page so nothing hides behind it. */
@media (max-width: 767px) {
  .sticky-cta {
    right: 0;
    left: 0;
    bottom: 0;
    border-radius: 0;
    border-left: none;
    border-right: none;
    border-bottom: none;
    border-top: 1px solid var(--violet-hot);
    min-height: 56px;
    background: color-mix(in srgb, var(--violet) 88%, transparent);
    -webkit-backdrop-filter: blur(8px) saturate(120%);
            backdrop-filter: blur(8px) saturate(120%);
    box-shadow: 0 -6px 24px rgba(0, 0, 0, 0.5);
    padding-bottom: calc(var(--s-3) + env(safe-area-inset-bottom));
  }
  /* The docked full-width bar hugs the screen edges; a scale squeeze would peel
     it away from them and read as a glitch. Feed back with the fill shift the
     (hoverless) bar can't otherwise show instead. */
  .sticky-cta:active { transform: none; background: var(--violet-hot); }
  /* Reserve flow space so the docked bar never covers the last card / CTAs.
     The footer is the last element on every page (and home uses .home, not
     .page__inner), so reserve room at the FOOTER too — otherwise the bar clips
     the final footer row. */
  body:has(.sticky-cta) .page__inner {
    padding-bottom: calc(64px + env(safe-area-inset-bottom));
  }
  body:has(.sticky-cta) .site-foot {
    padding-bottom: calc(72px + env(safe-area-inset-bottom));
  }
}
/* The global mobile ticket bar only exists where the header BUY TICKETS button
   is hidden (<=1120px). On desktop the header CTA is the persistent path, so the
   bar would be redundant — hide it there. */
@media (min-width: 1121px) {
  .sticky-cta--nav { display: none; }
}

/* Primary CTA — carries the snapped-link motif: the closed chrome link
   ::before snaps open on hover (tension / release). */
.btn-tickets {
  appearance: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--s-3);
  /* PRIMARY hero action: a solid violet fill is the loudest element above the
     fold (raters: BUY TICKETS read equal to MORE INFO). White label on #7A00DF
     clears AA (6.2:1); the ghost variant below demotes itself to an outline. */
  background: var(--violet);
  color: #fff;
  font-family: var(--mono);
  font-size: clamp(0.86rem, 1.4vw, 0.96rem);
  font-weight: 700;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  padding: var(--s-4) var(--s-7);
  min-height: 60px;
  cursor: pointer;
  border: 1px solid var(--violet);
  border-radius: var(--radius);
  text-decoration: none;
  transition: color 200ms ease, border-color 200ms ease, background 200ms ease, gap 220ms ease, transform var(--dur-press) var(--ease-out);
}
.btn-tickets::before {
  content: '';
  flex: none;
  width: 30px;                   /* double-link glyph, 128:96 viewBox */
  height: 22px;
  background: var(--chrome);
  -webkit-mask: url('/assets/chains/link-closed.svg') center / contain no-repeat;
          mask: url('/assets/chains/link-closed.svg') center / contain no-repeat;
  transition: transform 220ms var(--ease-out);
}
.btn-tickets:hover,
.btn-tickets:focus-visible {
  color: #fff;
  border-color: var(--violet-hot);
  background: var(--violet-hot);
  gap: var(--s-4);
}
.btn-tickets:hover::before,
.btn-tickets:focus-visible::before {
  -webkit-mask-image: url('/assets/chains/link-open.svg');
          mask-image: url('/assets/chains/link-open.svg');
  transform: rotate(10deg) scale(1.12);
}
/* High-contrast 2px focus ring, offset off the fill so it reads on any bg. */
.btn-tickets:focus-visible { outline: 2px solid var(--ink); outline-offset: 2px; box-shadow: 0 0 0 4px rgba(7, 3, 16, 0.9); }
/* Squeeze the whole CTA; the chain-link ::before scales with it (children scale
   under transform) and composes with its own hover rotate/scale tension motif. */
.btn-tickets:active { transform: scale(0.97); }

/* Secondary variant of the CTA — demoted to a quiet outline so the solid
   primary clearly wins the hierarchy. Transparent fill, ghost border, muted
   label that still clears AA. */
.btn-tickets--ghost {
  background: transparent;
  color: var(--ink-mid);
  border-color: var(--rule-2);
}
.btn-tickets--ghost::before { background: var(--steel-dim); }
.btn-tickets--ghost:hover,
.btn-tickets--ghost:focus-visible {
  background: var(--violet-soft);
  border-color: var(--violet-glow);
  color: var(--violet-pale);
}
.btn-tickets--ghost:hover::before,
.btn-tickets--ghost:focus-visible::before { background: var(--chrome-violet); }

/* Newsletter / generic inputs. */
.nl-form { display: grid; gap: var(--s-2); }
.nl-form__row {
  display: flex;
  gap: var(--s-3);
  align-items: flex-end;
  flex-wrap: wrap;
}
/* First/last name pair: two columns on desktop, stacked on small screens. */
.nl-form__row--names {
  display: grid;
  grid-template-columns: 1fr 1fr;
  align-items: end;
}
@media (max-width: 540px) {
  .nl-form__row--names { grid-template-columns: 1fr; }
}
.nl-form__field { display: grid; gap: var(--s-1); flex: 1 1 240px; }
.nl-form__hint {
  margin: var(--s-1) 0 0;
  font-family: var(--mono);
  font-size: 0.68rem;
  letter-spacing: 0.05em;
  color: var(--ink-faint);
}
.nl-form__label {
  font-family: var(--mono);
  /* Functional label: 12px minimum for legibility (kept uppercase + tracking
     for the mono-label look). Was 0.62rem / 9.92px. */
  font-size: 0.76rem;
  font-weight: 500;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--violet-glow);
}
.nl-input {
  width: 100%;
  min-height: 48px;
  padding: var(--s-3) var(--s-4);
  font-family: var(--mono);
  font-size: 0.95rem;
  color: var(--ink);
  background: var(--bg-tint);
  /* Border >=3:1 vs bg so the field reads as an interactive control, not a
     near-invisible box on near-black (WCAG 1.4.11). */
  border: 1.5px solid rgba(201, 201, 212, 0.55);
  border-radius: var(--radius);
  color-scheme: dark;            /* native date picker + indicator in dark */
  transition: border-color 160ms ease, box-shadow 160ms ease;
}
.nl-input::placeholder { color: var(--ink-faint); }
.nl-input:hover { border-color: var(--steel); }
.nl-input:focus,
.nl-input:focus-visible {
  outline: none;
  border-color: var(--violet-hot);
  box-shadow: 0 0 0 2px var(--bg), 0 0 0 4px var(--violet-glow);
}
.nl-form__submit { flex: none; }
.nl-form__consent {
  margin: var(--s-1) 0 0;
  font-size: 0.8rem;
  color: var(--ink-mid);          /* AA secondary grey (>=4.5:1), was sub-AA faint */
}
.nl-form__consent a { color: var(--violet-glow); text-decoration: underline; text-underline-offset: 3px; }
.nl-form__consent a:hover { color: var(--violet-pale); }
.nl-form__msg {
  margin: 0;
  min-height: 1.2em;
  font-family: var(--mono);
  font-size: 0.78rem;
  letter-spacing: 0.04em;
  color: var(--violet-glow);
}
/* Honeypot: hidden from users + a11y tree without going off-screen-left
   (a large negative offset trips horizontal-overflow detectors). */
.nl-form__hp { position: absolute; width: 0; height: 0; padding: 0; margin: 0; overflow: hidden; border: 0; opacity: 0; }
.nl-form--page { max-width: 560px; position: relative; }

/* Newsletter — two columns on desktop so the page is not a half-empty left rail:
   value-prop + form left, a tall past-night visual right (the visual replaces the
   old faint chain-strand that left the right column reading empty). One column,
   visual hidden, on narrow screens so the form is never pushed down. */
.nl-layout {
  display: grid;
  grid-template-columns: minmax(0, 1.3fr) minmax(0, 1fr);
  gap: clamp(2rem, 5vw, 4rem);
  align-items: start;
  margin-top: var(--s-4);
}
.nl-layout__main { min-width: 0; }
.nl-aside-media {
  margin: 0;
  position: relative;
  border: 1px solid var(--steel-dim);
  border-top: 2px solid var(--violet);
  border-radius: var(--radius);
  overflow: hidden;
  aspect-ratio: 3 / 4;
}
.nl-aside-media img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: 50% 42%;
  filter: brightness(1.4) contrast(1.06) saturate(1.12);
}
.nl-aside-media__cap {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  margin: 0;
  padding: var(--s-4);
  font-family: var(--display);
  font-weight: 600;
  font-size: clamp(1.05rem, 1.6vw, 1.3rem);
  line-height: 1.2;
  color: #fff;
  background: linear-gradient(to top, rgba(7, 3, 16, 0.82) 0%, transparent 100%);
}
@media (max-width: 860px) {
  .nl-layout { grid-template-columns: 1fr; gap: 0; }
  .nl-layout__aside { display: none; }
}
/* Mobile: the Subscribe button matches the full-width input instead of sitting
   half-width and left-aligned — maximises the page's primary tap target. */
@media (max-width: 540px) {
  .nl-form__submit { flex: 1 1 100%; width: 100%; }
  .nl-form__submit .btn,
  .nl-form__submit button { width: 100%; }
}

/* --------------------------------------------------------------------------
   9. Homepage — hero. Layered chrome chains + huge Clash date.
   -------------------------------------------------------------------------- */
.home {
  position: relative;
  z-index: 2;
  /* The hero wash (.hero::before, inset -10% x) and the corner strands
     intentionally overshoot the viewport; clip the x-axis at the page level so
     they never widen document scrollWidth. y stays visible (sticky header
     overlap). Old engines ignore `clip` → body overflow-x guard still holds. */
  overflow-x: clip;
}

.hero {
  position: relative;
  max-width: var(--max-w);
  margin: 0 auto;
  /* Bottom 0: the next section's padding-top owns the inter-section gap, so the
     whole homepage shares one rhythm (space-section gap → hairline → space-block). */
  padding: var(--space-section) var(--gutter) 0;
}
/* Radial violet wash — light falling on the scene, behind everything. */
.hero::before {
  content: '';
  position: absolute;
  inset: -20% -10% 0;
  pointer-events: none;
  background: radial-gradient(55% 65% at 30% 30%, rgba(122, 0, 223, 0.14), transparent 70%);
  z-index: 0;
}
.hero__inner {
  position: relative;
  z-index: 1;
  display: grid;
  grid-template-columns: minmax(0, 1.04fr) minmax(0, 0.96fr);
  gap: clamp(2rem, 5vw, 4.5rem);
  align-items: center;
}
@media (max-width: 960px) {
  .hero__inner { grid-template-columns: 1fr; gap: var(--s-6); }
}

/* Corner strands. Both use the single diagonal strand asset whose two cut
   ends sit exactly ON the image edges; rotation points those edges at the
   viewport corners, and negative offsets push them past the viewport — so a
   strand always runs edge-to-edge and never stops mid-air, at any breakpoint
   or scroll offset (parallax only slides the layers up/down slightly; the
   ends stay outside the top/right/left viewport edges). Both layers sit at
   z0, BEHIND the copy and the flyer (.hero__inner is z1). */
.hero-chains {
  position: absolute;
  top: 0;
  bottom: 0;
  left: calc(50% - 50vw);   /* full-bleed: reach the real viewport edges */
  right: calc(50% - 50vw);
  pointer-events: none;
  z-index: 0;
  /* The strands intentionally overshoot the viewport edges; clip the x-axis so
     they never widen document scrollWidth (overflow-y stays visible — the top
     overshoot tucks under the sticky header). Old engines ignore `clip` and
     fall back to the body overflow-x guard. */
  overflow-x: clip;
}
.hero-chains__tr,
.hero-chains__tl {
  position: absolute;
  will-change: transform;
}
/* Top-right: enters via the top edge, exits via the right edge, passing
   behind the flyer on the way down. */
.hero-chains__tr {
  top: clamp(-90px, -6vw, -44px);
  right: clamp(-90px, -6vw, -44px);
  width: clamp(320px, 46vw, 680px);
  opacity: 0.55;
  filter: drop-shadow(-12px 20px 34px rgba(122, 0, 223, 0.38));
}
.hero-chains__tr img { transform: rotate(180deg); }
/* Top-left balance strand: enters via the top edge, exits via the left edge,
   low opacity behind the copy. */
.hero-chains__tl {
  top: clamp(-80px, -5vw, -40px);
  left: clamp(-80px, -5vw, -40px);
  width: clamp(280px, 36vw, 540px);
  opacity: 0.22;
}
.hero-chains__tl img { transform: rotate(90deg); }
.hero-chains img { display: block; width: 100%; height: auto; }
@media (max-width: 700px) {
  .hero-chains__tl { display: none; }
  .hero-chains__tr { opacity: 0.4; }
}

/* Soft bg halo keeps hero copy legible where chains pass underneath. */
.hero__copy .hero-date,
.hero__copy .hero-loc,
.hero__copy .hero-kicker,
.hero-cd,
.event-tags li {
  text-shadow:
    0 0 8px  rgba(7, 3, 16, 0.95),
    0 0 22px rgba(7, 3, 16, 0.75);
}

/* Handcuff mark — the real two-tone (white + violet) distressed cuffs. */
.hero-mark {
  position: relative;
  width: clamp(110px, 12vw, 170px);
  margin-bottom: var(--s-5);
}
.hero-mark__img {
  display: block;
  width: 100%;
  height: auto;
  filter: drop-shadow(0 0 26px rgba(122, 0, 223, 0.4));
}

.hero-kicker {
  margin: 0 0 var(--s-4);
  font-family: var(--mono);
  font-size: var(--fs-label);
  font-weight: 500;
  letter-spacing: 0.38em;
  text-transform: uppercase;
  color: var(--violet-glow);
  display: flex;
  align-items: center;
  gap: var(--s-3);
}
/* Tension tick — short violet bar, not a gimmick ring. */
.cuff-dot {
  flex: none;
  width: 22px;
  height: 2px;
  border: 0;
  border-radius: 0;
  background: var(--violet);
  box-shadow: none;
}
.cuff-dot::after { display: none; }

.hero-date {
  margin: 0 0 var(--s-3);
  font-family: var(--display);
  font-weight: 700;
  font-size: var(--fs-hero);
  line-height: 0.95;
  letter-spacing: -0.02em;
  color: var(--ink);
  font-variant-numeric: tabular-nums;
}
.hero-date small {
  display: block;
  font-family: var(--mono);
  /* Day-of-week is decision-relevant (a Saturday reads very differently from a
     Tuesday) — given real presence now that no kicker sits above it. */
  font-size: clamp(1rem, 1.8vw, 1.45rem);
  font-weight: 700;
  letter-spacing: 0.34em;
  text-transform: uppercase;
  color: var(--violet-glow);
  margin-bottom: var(--s-4);
}
/* Event name shares the weekday eyebrow line ("SAT · KINKY TECHNO PARTY"); dimmer
   + tighter tracking so the weekday still leads and the line fits comfortably. */
.hero-date__event { color: var(--ink-mid); letter-spacing: 0.2em; }
/* Day+month and year sit on their own controlled lines so the display date
   never collapses into a run-together "27 JUN2026" token at any breakpoint. */
.hero-date__dm { display: block; white-space: nowrap; }
.hero-date__yr {
  display: block;
  color: var(--ink-mid);
  letter-spacing: 0.01em;
}

.hero-loc {
  margin: 0 0 var(--s-4);
  font-family: var(--mono);
  /* Venue/doors meta: >=14px off-white, AA-verified (>=4.5:1 on bg). */
  font-size: clamp(0.875rem, 1.3vw, 0.95rem);
  font-weight: 500;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--ink);
  line-height: 1.8;
}
.hero-loc .sep { color: var(--violet-hot); padding: 0 0.35em; }

.event-tags {
  list-style: none;
  margin: 0 0 var(--s-5);
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-2);
}
.event-tags li {
  font-family: var(--mono);
  /* Genre chips: lifted off the ~11px floor to a legible 12.5px (raters: dense
     hero microtype below AA). Off-white at 0.78 alpha clears AA on the bg. */
  font-size: clamp(0.72rem, 0.95vw, 0.78rem);
  font-weight: 500;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--ink-mid);
  border: 1px solid var(--rule-2);
  background: transparent;
  padding: var(--s-1) var(--s-3);
  border-radius: var(--radius);
}

/* One-line "what this is" — concise scene-setter under the event meta.
   Hero sub-headline: >=16px at #C9B8E0+ luminance, AA-verified on bg. */
.hero-what {
  max-width: 52ch;
  margin: 0 0 var(--s-5);
  font-family: var(--sans);
  font-size: clamp(1rem, 1.2vw, 1.0625rem);
  line-height: 1.6;
  color: var(--ink);
}

/* Countdown block — HUD. */
.hero-cd {
  margin: 0 0 var(--s-6);
  border-top: 1px solid var(--rule);
  border-bottom: 1px solid var(--rule);
  padding: var(--s-3) 0;
  display: flex;
  flex-direction: column;
  gap: var(--s-1);
}
.hero-cd__label {
  font-family: var(--mono);
  /* Was 0.6rem / 9.6px — below the legibility floor. 12px + AA-clearing tone. */
  font-size: 0.75rem;
  font-weight: 500;
  letter-spacing: 0.28em;
  text-transform: uppercase;
  color: var(--ink-mid);
}
.hero-cd__value {
  font-family: var(--mono);
  font-size: clamp(1.25rem, 2.8vw, 1.8rem);
  font-weight: 700;
  letter-spacing: 0.14em;
  color: var(--violet-glow);
  font-variant-numeric: tabular-nums;
}
[data-countdown].is-live { color: var(--live); }

.cta-row {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-3);
  align-items: center;
}

/* "Next event coming soon" graceful state — the third home of the snapped
   link: held tension with nothing announced yet. */
.hero--empty { text-align: center; }
.hero--empty .hero-mark { margin-left: auto; margin-right: auto; }
.hero--empty .hero-kicker { justify-content: center; }
.hero-soon {
  margin: var(--s-4) 0 0;
  font-family: var(--display);
  font-weight: 600;
  font-size: var(--fs-h2);
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--ink);
}
.hero-soon::before {
  content: '';
  display: block;
  width: 56px;                   /* double-link glyph, 128:96 viewBox */
  height: 42px;
  margin: 0 auto var(--s-4);
  background: var(--chrome);
  -webkit-mask: url('/assets/chains/link-open.svg') center / contain no-repeat;
          mask: url('/assets/chains/link-open.svg') center / contain no-repeat;
}

/* Flyer — steel frame, violet light along the top edge. The whole flyer is a
   button that opens the event dossier (events.js [data-open-info]). */
.hero__visual { min-width: 0; position: relative; z-index: 1; }
.hero-flyer {
  position: relative;
  margin: 0;
  border: 1px solid var(--steel-dim);
  border-top: 2px solid var(--violet);
  border-radius: var(--radius);
  background: #000;
  overflow: hidden;
  aspect-ratio: 1920 / 1080;
  box-shadow: 0 30px 90px rgba(0, 0, 0, 0.55);
  transition: border-color 200ms ease, transform 220ms var(--ease-out),
              box-shadow 220ms ease;
}
/* Keyboard focus always lifts the flyer. Pointer hover only lifts where hover
   is real — on touch the tap opens the modal, so a stuck lift would just flash
   behind it. */
.hero-flyer:has(.hero-flyer__btn:focus-visible) {
  border-color: var(--violet-hot);
  transform: translateY(-3px);
  box-shadow: 0 34px 90px rgba(0, 0, 0, 0.6), 0 12px 40px rgba(122, 0, 223, 0.22);
}
@media (hover: hover) and (pointer: fine) {
  .hero-flyer:has(.hero-flyer__btn:hover) {
    border-color: var(--violet-hot);
    transform: translateY(-3px);
    box-shadow: 0 34px 90px rgba(0, 0, 0, 0.6), 0 12px 40px rgba(122, 0, 223, 0.22);
  }
}
.hero-flyer__btn {
  display: block;
  width: 100%;
  height: 100%;
  margin: 0;
  padding: 0;
  border: 0;
  background: #000;
  cursor: pointer;
  appearance: none;
}
.hero-flyer__btn:focus-visible {
  outline: 2px solid var(--violet-glow);
  outline-offset: -4px;          /* inside — the figure clips overflow */
}
.hero-flyer__btn picture { display: block; width: 100%; height: 100%; }
.hero-flyer img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.hero-flyer__caption {
  margin-top: var(--s-3);
  font-family: var(--mono);
  font-size: 0.62rem;
  font-weight: 500;
  letter-spacing: 0.3em;
  text-transform: uppercase;
  color: var(--ink-faint);
  display: flex;
  justify-content: space-between;
  gap: var(--s-4);
}
.hero-flyer__caption b { color: var(--violet-glow); font-weight: 500; }

/* Scannable lineup — real HTML text (no longer baked into the flyer). The
   headliner cohort is set large; the rest is billed below in a mono row. */
.hero-lineup { margin: 0 0 var(--s-5); }
.hero-lineup__label {
  margin: 0 0 var(--s-2);
  font-family: var(--mono);
  font-size: var(--fs-label);
  font-weight: 500;
  letter-spacing: 0.34em;
  text-transform: uppercase;
  color: var(--violet-glow);
}
/* Structured headliner bill — artist + set/closing time per row so the hero
   reads as a real lineup, not plain text (raters: "is the night real" signal). */
.hero-lineup__bill {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--s-1);
}
.hero-lineup__act {
  display: flex;
  align-items: baseline;
  gap: var(--s-3);
}
.hero-lineup__act .hero-lineup__head {
  font-family: var(--display);
  font-weight: 700;
  font-size: clamp(1.3rem, 2.6vw, 1.8rem);
  line-height: 1.1;
  letter-spacing: -0.01em;
  color: var(--ink);
  text-transform: uppercase;
}
.hero-lineup__head { white-space: nowrap; }
.hero-lineup__time {
  font-family: var(--mono);
  font-size: 0.8rem;
  font-weight: 500;
  letter-spacing: 0.1em;
  color: var(--violet-glow);
  font-variant-numeric: tabular-nums;
}
.hero-lineup__rest {
  margin: var(--s-3) 0 0;
  font-family: var(--mono);
  font-size: 0.82rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink-mid);
}
.hero-lineup__tba { color: var(--violet-glow); font-weight: 700; }

/* "What to expect" — three scannable tiles: floor 1 / floor 2 / play areas. */
.hero-expect {
  list-style: none;
  margin: 0 0 var(--s-6);
  padding: 0;
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--s-3);
}
.hero-expect li {
  display: flex;
  flex-direction: column;
  gap: var(--s-1);
  padding: var(--s-3) var(--s-4);
  border: 1px solid var(--rule-2);
  border-top: 2px solid var(--violet);
  border-radius: var(--radius);
  background: rgba(11, 6, 24, 0.5);
}
.hero-expect b {
  font-family: var(--mono);
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--violet-glow);
}
.hero-expect span {
  font-family: var(--sans);
  font-size: 0.9rem;
  line-height: 1.3;
  color: var(--ink);
}
@media (max-width: 560px) {
  .hero-expect { grid-template-columns: 1fr; }
  .hero-expect li { flex-direction: row; align-items: baseline; gap: var(--s-3); }
  .hero-expect b { flex: none; min-width: 9.5em; }
}

/* Hero scarcity strip — active phase / early-bird delta / next price. */
.hero-phase { margin: 0 0 var(--s-5); }

/* Shared ticket-phase strip (hero + ticket rows). Active phase is bright; the
   "ending soon" badge is the only filled element. AA-verified throughout. */
.phase-strip {
  display: inline-flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s-2) var(--s-3);
  font-family: var(--mono);
  font-size: 0.72rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}
.phase-strip__now { color: #fff; font-weight: 700; }
.phase-strip__badge {
  color: #fff;
  background: var(--violet);
  padding: 0.18em 0.6em;
  border-radius: var(--radius);
  font-weight: 700;
  letter-spacing: 0.14em;
  animation: phase-pulse 2.4s ease-in-out infinite;
}
/* ease-in-out is deliberate here: this is a symmetric breathing loop (0→peak→0),
   so it should decelerate into both the peak and the rest — not the ease-out we
   use for one-shot enter/exit motion. Don't "correct" it to var(--ease-out). */
@keyframes phase-pulse {
  0%, 100% { box-shadow: 0 0 0 0 rgba(122, 0, 223, 0); }
  50%      { box-shadow: 0 0 0 3px rgba(122, 0, 223, 0.28); }
}
@media (prefers-reduced-motion: reduce) {
  .phase-strip__badge { animation: none; }
}
.phase-strip__save { color: var(--violet-glow); font-weight: 700; }
.phase-strip__next { color: var(--ink-mid); }
.phase-strip__next::before { content: '→ '; color: var(--violet-glow); }
.phase-strip__cd { color: var(--ink-mid); display: inline-flex; flex-wrap: wrap; gap: 0 var(--s-2); align-items: baseline; }
/* Label stays whole; the value's number+unit pairs are nbsp-bound in site.js, so
   on narrow screens the strip breaks between "Preis steigt in" and the value, or
   between whole units — never mid-unit ("…6 / STD…"). */
.phase-strip__cd-label { color: var(--ink-faint); white-space: nowrap; }
.phase-strip__cd [data-countdown] { color: var(--violet-glow); font-weight: 700; }

/* Thin neutral rule — replaces the over-used chain divider mid-page so the
   chain motif stays special (kept only at the footer band + page-title end). */
.thin-rule {
  height: 0;
  margin: 0 0 var(--space-block);
  border: 0;
  border-top: 1px solid var(--rule-2);
}

/* --------------------------------------------------------------------------
   10-pre. Homepage — proof of night (crowd photo + track record).
   -------------------------------------------------------------------------- */
.proof {
  position: relative;
  z-index: 2;
  max-width: var(--max-w);
  margin: 0 auto;
  padding: var(--space-section) var(--gutter) 0;
  display: grid;
  grid-template-columns: minmax(0, 1.15fr) minmax(0, 1fr);
  align-items: stretch;
  gap: clamp(1.5rem, 4vw, 3rem);
}
/* Section-divider hairline spans the full proof grid (it's a grid child, not a
   flow block, since .proof is a grid); the grid row-gap is its gap to content. */
.proof > .thin-rule { grid-column: 1 / -1; margin: 0; }
/* WebP <picture> wrappers must not change layout: the wrapper spans the media
   box so the inner <img> keeps inheriting the container's sizing rules
   (mirrors .hero-flyer__btn picture). */
.proof__media picture,
.atmo__media picture,
.nl-aside-media picture { display: block; width: 100%; height: 100%; }
/* Team cards must NOT force the picture to 100% height — when the grid stretches
   the two cards to equal height, height:100% makes the picture fill the card and
   the bio caption gets clipped by overflow:hidden. Let the image size naturally
   and the caption flow below. */
.team-card picture { display: block; width: 100%; }
.team-grid { align-items: start; }
.proof__media {
  margin: 0;
  position: relative;
  border: 1px solid var(--steel-dim);
  border-top: 2px solid var(--violet);
  border-radius: var(--radius);
  overflow: hidden;
  aspect-ratio: 3 / 2;   /* matches the 3:2 crowd photos so heads aren't cropped off */
  /* Never a black void: an explicit min-height plus a low-light brand wash means
     the frame reads as a dim room even before/if the crowd image resolves (the
     single most important FOMO asset must never render as an empty box). */
  min-height: 220px;
  background:
    radial-gradient(120% 120% at 50% 30%, #1a1030 0%, #0b0618 70%);
}
.proof__media img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  /* The vivid magenta stage light is perceptually bright but LOW luminance
     (~67/255), so a <1 brightness made the frame read as a near-black box.
     Lift it and bias the crop up to the light beams so the floor reads as a
     packed, lit room at a glance — still raw, never blown-out/stocky. */
  object-position: 50% 42%;
  filter: brightness(1.42) contrast(1.06) saturate(1.14);
}
/* Soft edge vignette over the crowd shot — keeps it raw, not stock, without
   blacking out the frame (kept light so the brightened image still reads). */
.proof__media::after {
  content: '';
  position: absolute;
  inset: 0;
  pointer-events: none;
  background:
    linear-gradient(to top, rgba(7, 3, 16, 0.55) 0%, transparent 42%),
    radial-gradient(120% 100% at 50% 38%, transparent 66%, rgba(7, 3, 16, 0.32) 100%);
}
/* Caption strip — names the room so the proof shot is unmistakably "us, packed",
   not a stock crowd. Sits on the bottom gradient, mono, understated. */
.proof__caption {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  margin: 0;
  padding: var(--s-3) var(--s-4);
  font-family: var(--mono);
  font-size: 0.7rem;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: rgba(241, 236, 250, 0.92);
  display: flex;
  align-items: center;
  gap: var(--s-3);
  z-index: 1;
}
.proof__caption .cuff-dot { flex: none; }
/* Crowd-photo slider — fills the media frame; slides cross-fade (site.js sets a
   random start + auto-advances). Sits BELOW the ::after vignette + caption (both
   later in paint order / z-index). The per-frame `img` filter + object-position
   from .proof__media img / .atmo__media img still apply to the slides. */
.crowd-slider { position: absolute; inset: 0; }
.crowd-slider__slide {
  position: absolute;
  inset: 0;
  opacity: 0;
  transition: opacity 900ms var(--ease-out);
}
.crowd-slider__slide.is-active { opacity: 1; }
/* Two-class selector so it beats .proof__media img / .atmo__media img (whose
   brightness filters were tuned for the old dark crowd shot and blow out the new,
   correctly-exposed photos). object-position centred for varied framing. */
.crowd-slider .crowd-slider__slide img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center 30%;   /* bias up so heads stay in frame on the taller (5:4) shots */
  filter: none;
}
@media (prefers-reduced-motion: reduce) {
  .crowd-slider__slide { transition: none; }
}
.proof__copy { display: flex; flex-direction: column; justify-content: center; gap: var(--s-3); }
.proof__kicker {
  margin: 0;
  display: flex;
  align-items: center;
  gap: var(--s-3);
  font-family: var(--mono);
  font-size: var(--fs-label);
  font-weight: 500;
  letter-spacing: 0.34em;
  text-transform: uppercase;
  color: var(--violet-glow);
}
.proof__stat { margin: 0; display: flex; flex-direction: column; gap: var(--s-1); }
.proof__stat-n {
  font-family: var(--display);
  font-weight: 700;
  font-size: clamp(2.2rem, 5vw, 3.4rem);
  line-height: 0.98;
  letter-spacing: -0.02em;
  color: var(--ink);
}
.proof__stat-label {
  font-family: var(--mono);
  font-size: 0.78rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink-mid);
}
.proof__line { margin: 0; max-width: 42ch; color: var(--ink-mid); line-height: 1.6; }
.proof__cta {
  align-self: start;
  display: inline-flex;
  align-items: center;
  min-height: 24px;   /* WCAG 2.5.8 AA target size; grows downward only, no desktop shift */
  margin-top: var(--s-2);
  font-family: var(--mono);
  font-size: 0.74rem;
  font-weight: 700;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--violet-glow);
  text-decoration: none;
}
.proof__cta:hover { color: var(--violet-pale); }
@media (max-width: 760px) {
  .proof { grid-template-columns: 1fr; gap: var(--s-5); }
  .proof__media { aspect-ratio: 3 / 2; }
}

/* Content-page atmosphere / social-proof strip (About, FAQ) — same raw, dark,
   privacy-safe crowd treatment as the homepage proof, scoped to the page column
   with a stat + first-timer pull-quote. */
.atmo {
  margin: var(--space-block) 0;
  display: grid;
  grid-template-columns: minmax(0, 1.1fr) minmax(0, 1fr);
  align-items: stretch;
  gap: clamp(1.25rem, 3.5vw, 2.5rem);
}
.atmo__media {
  margin: 0;
  position: relative;
  border: 1px solid var(--steel-dim);
  border-top: 2px solid var(--violet);
  border-radius: var(--radius);
  overflow: hidden;
  aspect-ratio: 3 / 2;   /* matches the 3:2 crowd photos so heads aren't cropped off */
  min-height: 200px;
  /* Low-light brand wash fallback so the frame is a dim room, never a void. */
  background:
    radial-gradient(120% 120% at 50% 30%, #1a1030 0%, #0b0618 70%);
}
.atmo__media img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  filter: brightness(0.9) contrast(1.05) saturate(1.04);
}
.atmo__media::after {
  content: '';
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: radial-gradient(120% 100% at 50% 38%, transparent 55%, rgba(7, 3, 16, 0.42) 100%);
}
.atmo__copy { display: flex; flex-direction: column; justify-content: center; gap: var(--s-3); }
.atmo__kicker {
  margin: 0;
  display: flex;
  align-items: center;
  gap: var(--s-3);
  font-family: var(--mono);
  font-size: var(--fs-label);
  font-weight: 500;
  letter-spacing: 0.32em;
  text-transform: uppercase;
  color: var(--violet-glow);
}
.atmo__stat { margin: 0; display: flex; flex-direction: column; gap: var(--s-1); }
.atmo__stat-n {
  font-family: var(--display);
  font-weight: 700;
  font-size: clamp(2rem, 4.5vw, 3rem);
  line-height: 0.98;
  letter-spacing: -0.02em;
  color: var(--ink);
}
.atmo__stat-label {
  font-family: var(--mono);
  font-size: 0.76rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink-mid);
}
.atmo__quote {
  margin: var(--s-2) 0 0;
  padding-left: var(--s-4);
  border-left: 2px solid var(--violet);
}
.atmo__quote p {
  margin: 0 0 var(--s-2);
  font-family: var(--sans);
  font-size: 1.05rem;
  line-height: 1.5;
  color: var(--ink);
}
.atmo__quote cite {
  font-family: var(--mono);
  font-style: normal;
  /* Attribution line lifted above the 11px floor (was 10.88px) + a slightly
     stronger tone so it's a legible source, not a faint footnote. */
  font-size: 0.72rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink-mid);
}
@media (max-width: 760px) {
  .atmo { grid-template-columns: 1fr; gap: var(--s-5); }
  .atmo__media { aspect-ratio: 3 / 2; }
}

/* Mid-page conversion band — a violet-edged strip so a converted reader can act
   from the point of conviction, not only at the very bottom of a long column. */
.mid-cta {
  margin: var(--space-block) 0;
  padding: var(--space-card);
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-5);
  background:
    radial-gradient(120% 140% at 0% 0%, rgba(122, 0, 223, 0.14), transparent 55%),
    var(--bg-tint);
  border: 1px solid var(--violet-deep);
  border-left: 4px solid var(--violet);
  border-radius: var(--radius);
}
.mid-cta__copy { display: flex; flex-direction: column; gap: var(--s-2); min-width: 0; }
.mid-cta__title {
  margin: 0;
  font-family: var(--display);
  font-weight: 600;
  font-size: var(--fs-h3);
  line-height: 1.2;
  color: var(--ink);
}
.mid-cta__p { margin: 0; max-width: 52ch; color: var(--ink-mid); line-height: 1.55; }
@media (max-width: 560px) {
  .mid-cta__title { font-size: 1.15rem; }
  .mid-cta .btn { width: 100%; }
}

/* --------------------------------------------------------------------------
   10. Homepage — upcoming editions.
   -------------------------------------------------------------------------- */
.upcoming {
  position: relative;
  z-index: 2;
  max-width: var(--max-w);
  margin: 0 auto;
  /* Consistent homepage rhythm: space-section above the rule, no bottom padding
     (the next section's top padding owns the gap, down to the footer). */
  padding: var(--space-section) var(--gutter) 0;
}

.upcoming__title {
  margin: 0 0 var(--s-2);
  padding: 0 0 var(--s-3);
  border-bottom: 1px solid var(--rule);
  font-family: var(--mono);
  font-size: 0.62rem;
  font-weight: 500;
  letter-spacing: 0.36em;
  text-transform: uppercase;
  color: var(--ink-faint);
  display: flex;
  align-items: center;
  gap: var(--s-3);
}
.upcoming__title::before {
  content: '';
  width: 22px;
  height: 2px;
  background: var(--violet);
}

.upcoming__list { list-style: none; margin: 0; padding: 0; }
.upcoming__row { border-bottom: 1px solid var(--rule); transition: border-color 200ms ease; }
.upcoming__row:hover, .upcoming__row:focus-within { border-bottom-color: var(--violet); }

.upcoming__btn {
  position: relative;
  display: grid;
  grid-template-columns: 7.5em minmax(0, 1fr) auto auto auto;
  align-items: center;
  gap: var(--s-1) var(--s-5);
  width: 100%;
  background: transparent;
  border: 0;
  color: var(--ink);
  font-family: var(--mono);
  font-size: 0.76rem;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  /* Tighter vertical rhythm (~28% shorter rows) so the list reads dense and
     actionable instead of a sparse half-empty column on desktop. */
  padding: var(--s-3) var(--s-3) var(--s-3) 0;
  min-height: 54px;
  text-align: left;
  cursor: pointer;
  border-radius: var(--radius);
  transition: color 180ms ease, background 180ms ease, padding-left 220ms var(--ease-out);
}
/* Whole-row hover/active wash so each date reads as a tappable, live target. */
.upcoming__btn:hover,
.upcoming__btn:focus-visible {
  background: linear-gradient(90deg, var(--violet-soft), transparent 70%);
}
/* Double chrome link slides in from the left rail on hover. */
.upcoming__btn::before {
  content: '';
  position: absolute;
  left: 0;
  top: 50%;
  width: 27px;
  height: 20px;
  margin-top: -10px;
  background: url('/assets/chains/link-closed.svg') center / contain no-repeat;
  opacity: 0;
  transform: translateX(-10px);
  transition: opacity 200ms ease, transform 220ms var(--ease-out);
}
.upcoming__btn:hover, .upcoming__btn:focus-visible {
  color: var(--violet-glow);
  padding-left: calc(27px + var(--s-3));
}
.upcoming__btn:hover::before, .upcoming__btn:focus-visible::before {
  opacity: 1;
  transform: translateX(0);
}
.upcoming__btn:focus-visible {
  outline: 2px solid var(--violet-glow);
  outline-offset: -2px;
}
.upcoming__date {
  font-family: var(--display);
  font-weight: 600;
  font-size: clamp(1.15rem, 2vw, 1.4rem);
  letter-spacing: 0.01em;
  font-variant-numeric: tabular-nums;
}
.upcoming__name  { color: var(--ink-mid); font-weight: 500; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.upcoming__venue { color: var(--ink-faint); font-size: 0.66rem; letter-spacing: 0.22em; }
/* Per-row price / presale micro-tag — makes each date feel actionable. */
.upcoming__tag {
  justify-self: end;
  font-family: var(--mono);
  font-size: 0.6rem;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  white-space: nowrap;
  padding: 0.2em 0.55em;
  border-radius: var(--radius);
}
.upcoming__tag--live {
  color: var(--violet-pale);
  background: var(--violet-soft);
  border: 1px solid var(--violet-deep);
}
.upcoming__tag--soon {
  color: var(--ink-faint);
  border: 1px dashed var(--rule-2);
}
.upcoming__cta   { color: var(--violet-glow); font-weight: 700; }

@media (max-width: 600px) {
  .upcoming__btn { grid-template-columns: 1fr auto auto; gap: var(--s-1) var(--s-4); }
  .upcoming__name  { grid-column: 1 / -1; white-space: normal; font-size: 0.68rem; }
  .upcoming__venue { display: none; }
}

.upcoming__row--current .upcoming__date { color: var(--violet-glow); }

/* "Show more" disclosure — reveals the hidden overflow rows (see site.js). */
.upcoming__more {
  display: flex;
  justify-content: center;
  margin-top: var(--s-6);
}
.upcoming__more-btn[hidden] { display: none; }

.upcoming__badge {
  display: inline-block;
  margin-left: var(--s-2);
  padding: var(--s-1) var(--s-2);
  font-style: normal;
  font-size: 0.55rem;
  letter-spacing: 0.28em;
  font-weight: 700;
  color: var(--ink);
  background: var(--violet);
  border-radius: var(--radius);
  vertical-align: 0.12em;
}

/* --------------------------------------------------------------------------
   10b. /buy-tickets/ event rows — same data language as .upcoming: big Clash
   dates over steel rules, mono meta, price right-aligned, CTA at the end.
   -------------------------------------------------------------------------- */
.tix-list {
  list-style: none;
  margin: 0 0 var(--space-block);
  padding: 0;
  border-top: 1px solid var(--rule);
}
/* "Future dates" disclosure — keeps the not-yet-on-sale rows out of the way so
   the buyable set leads, while staying one tap from the full season. A quiet
   full-width control with a violet rail, not a competing CTA. */
.tix-future { margin: calc(var(--s-3) * -1) 0 var(--space-block); }
.tix-future__toggle {
  display: flex;
  flex-direction: column;
  gap: 0.25em;
  width: 100%;
  padding: var(--s-4);
  text-align: left;
  background: rgba(122, 0, 223, 0.06);
  border: 1px solid var(--rule);
  border-left: 3px solid var(--violet);
  border-radius: var(--radius);
  cursor: pointer;
  transition: background 180ms ease, border-color 180ms ease, transform var(--dur-press) var(--ease-out);
}
.tix-future__toggle:hover { background: rgba(122, 0, 223, 0.12); border-color: var(--violet); }
.tix-future__toggle:active { transform: scale(0.97); }
.tix-future__toggle:focus-visible {
  outline: 2px solid var(--violet-glow);
  outline-offset: 2px;
}
.tix-future__toggle-label {
  display: flex;
  align-items: center;
  gap: var(--s-2);
  font-family: var(--mono);
  font-size: 0.8rem;
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--violet-glow);
}
.tix-future__chev {
  font-family: var(--display);
  font-weight: 700;
  line-height: 1;
}
.tix-future__toggle-sub {
  font-family: var(--mono);
  font-size: 0.72rem;
  letter-spacing: 0.04em;
  color: var(--ink-mid);
}
.tix-list--future {
  margin-top: var(--s-4);
  border-top: 1px solid var(--rule-2);
}
.tix-row {
  display: grid;
  grid-template-columns: auto 7.5em minmax(0, 1fr) auto auto;
  grid-template-areas:
    'flyer date name  price cluster'
    'flyer date venue price cluster';
  align-items: center;
  gap: var(--s-1) var(--s-5);
  padding: var(--s-5) 0;
  border-bottom: 1px solid var(--rule);
  font-family: var(--mono);
  font-size: 0.76rem;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  transition: border-color 200ms ease;
}
.tix-row--noflyer { grid-template-columns: 7.5em minmax(0, 1fr) auto auto; }
.tix-row--noflyer {
  grid-template-areas:
    'date name  price cluster'
    'date venue price cluster';
}
.tix-row:hover,
.tix-row:focus-within { border-bottom-color: var(--violet); }

/* Leading flyer thumbnail — steel-framed 16:9 crop, ~120px wide. */
.tix-row__flyer {
  grid-area: flyer;
  display: block;
  width: 120px;
  aspect-ratio: 16 / 9;
  overflow: hidden;
  background: #000;
  border: 1px solid var(--rule-2);
  border-radius: var(--radius);
}
.tix-row__flyer img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
/* Branded violet date-tile for events without bespoke flyer art yet — uniform
   with the real-flyer rows so every row reads as a real upcoming night, never a
   repeated wordmark or an empty black box. Per-month variation (--ph-month,
   1-12) rotates the violet glow origin + chain motif so a run of tiles never
   reads as one repeated template — all within the violet identity, no hue drift. */
.tix-row__flyer--tile {
  --ph-month: 1;
  --ph-x: calc(10% + (var(--ph-month) - 1) * 7%);   /* glow sweeps L→R by month */
  --ph-ang: calc(118deg + (var(--ph-month) - 1) * 11deg);
  position: relative;
  display: grid;
  place-items: center;
  background:
    radial-gradient(120% 150% at var(--ph-x) 0%, rgba(122, 0, 223, 0.58) 0%, rgba(122, 0, 223, 0) 60%),
    linear-gradient(var(--ph-ang), #1a0730 0%, #0b0420 55%, #070310 100%);
  border-color: var(--violet-deep);
}
/* Chrome chain-link motif behind the date — tile offset shifts per month so no
   two tiles align identically. Decorative, sits under the readable date text. */
.tix-row__flyer--tile::before {
  content: '';
  position: absolute;
  inset: 0;
  background: url('/assets/chains/link-closed.svg') no-repeat
    calc(78% + var(--ph-month) * 1.4%) calc(22% + var(--ph-month) * 2%) / 26px;
  opacity: 0.12;
  filter: grayscale(0.3) brightness(1.4);
  pointer-events: none;
}
.tix-tile {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0.12em;
  text-align: center;
  line-height: 1.02;
  padding: 0 6%;
}
.tix-tile__brand {
  font-family: var(--mono);
  font-size: 0.46rem;
  font-weight: 700;
  letter-spacing: 0.26em;
  color: var(--violet-pale);
  text-transform: uppercase;
}
.tix-tile__dm {
  font-family: var(--display);
  font-weight: 600;
  font-size: clamp(0.82rem, 1.5vw, 1rem);
  letter-spacing: 0.02em;
  color: #fff;
  white-space: nowrap;
}
.tix-tile__yr {
  font-family: var(--mono);
  font-size: 0.52rem;
  font-weight: 500;
  letter-spacing: 0.22em;
  color: var(--violet-pale);
}

/* CTA cluster: ghost "More info" + primary "Buy tickets". */
.tix-row__cluster {
  grid-area: cluster;
  justify-self: end;
  display: inline-flex;
  align-items: center;
  gap: var(--s-3);
}
/* Fixed two-line date block (day-month over year) — every row is identical
   height regardless of month-name length. */
.tix-row__date {
  grid-area: date;
  display: flex;
  flex-direction: column;
  line-height: 1.05;
  color: var(--ink);
  font-variant-numeric: tabular-nums;
}
.tix-row__date-dm {
  font-family: var(--display);
  font-weight: 700;
  /* Date is the primary row anchor (raters #15) — enlarged over the repeated
     series subtitle. */
  font-size: clamp(1.2rem, 2vw, 1.5rem);
  letter-spacing: 0.01em;
  color: var(--ink);
}
.tix-row__date-yr {
  font-family: var(--mono);
  font-size: 0.7rem;
  font-weight: 500;
  letter-spacing: 0.2em;
  color: var(--ink-mid);
  margin-top: 0.2em;
}
.tix-row__name {
  grid-area: name;
  color: var(--ink);
  font-weight: 500;
  letter-spacing: 0.12em;
  /* Full lineup wraps so every DJ is listed (no ellipsis truncation). */
  line-height: 1.4;
}
.tix-row__venue {
  grid-area: venue;
  color: var(--ink-mid);
  font-size: 0.68rem;
  letter-spacing: 0.12em;
}
/* Price — the single most decision-relevant token: near-white for max
   legibility/contrast (AA), with the small "from/ab" label kept in accent
   violet so the brand colour stays but never carries the readable number. */
.tix-row__price {
  grid-area: price;
  justify-self: end;
  text-align: right;
  color: var(--ink);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.tix-row__price b {
  font-weight: 700;
  color: var(--violet-glow);
  letter-spacing: 0.16em;
  margin-right: 0.45em;
}
.tix-row__cta {
  appearance: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 44px;
  padding: var(--s-2) var(--s-5);
  font-family: var(--mono);
  /* Actionable row labels held above the 11px legibility floor (was 10.88px);
     tracking tightened so the wider type still fits the row CTAs. */
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  text-decoration: none;
  color: var(--ink);
  background: transparent;
  border: 1px solid var(--rule-2);
  border-radius: var(--radius);
  white-space: nowrap;
  cursor: pointer;
  transition: color 200ms ease, border-color 200ms ease, background 200ms ease;
}
/* A clear 2px outline that sits OUTSIDE the control (offset) with a dark halo,
   so keyboard focus is unmistakable against the busy event rows — was a 1px
   inset shadow that disappeared into the row. */
.tix-row__cta:focus-visible {
  outline: 2px solid var(--violet-pale);
  outline-offset: 2px;
  box-shadow: 0 0 0 4px rgba(7, 3, 16, 0.92);
}
/* Primary "Buy tickets" — SOLID violet fill on EVERY row that sells, so the
   buy action is the unmissable element of each row (white text on #7A00DF is a
   large/bold element, AA-safe). */
.tix-row__cta--buy {
  color: #fff;
  background: var(--violet);
  border-color: var(--violet);
}
.tix-row__cta--buy:hover,
.tix-row__cta--buy:focus-visible {
  background: var(--violet-hot);
  border-color: var(--violet-hot);
  color: #fff;
}
/* Quiet secondary "More info" — defers to the primary Buy CTA but uses the same
   standardized ghost treatment as page bottom-CTA ghosts: full-opacity violet
   border + a #C4A6F0+ label so it never sinks into the near-black row (#4/#30). */
.tix-row__cta--info {
  border-color: var(--violet);
  color: var(--violet-pale);
  background: transparent;
}
.tix-row__cta--info:hover,
.tix-row__cta--info:focus-visible {
  color: var(--violet-pale);
  border-color: var(--violet-hot);
  background: var(--violet-soft);
}
/* Status pill — explicit state where no buy CTA exists yet (presale soon /
   door only), styled to read as intentional, not a missing button. */
.tix-row__status {
  display: inline-flex;
  align-items: center;
  gap: var(--s-2);
  min-height: 44px;
  padding: var(--s-2) var(--s-5);
  font-family: var(--mono);
  font-size: 0.64rem;
  font-weight: 600;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--ink-faint);
  border: 1px dashed var(--rule-2);
  border-radius: var(--radius);
  white-space: nowrap;
}
.tix-row__status-dot {
  width: 6px;
  height: 6px;
  flex: none;
  border-radius: 50%;
  background: var(--ink-faint);
}
/* Coming-soon rows recede so the live buy rows above read as the action. The
   whole row dims; the dashed "soon" pill + muted date make it unmistakably a
   not-yet state without hiding the date (SEO + crawlers still see it). */
/* Coming-soon rows recede only slightly so every date stays legible (raters:
   future dates read as broken; DE rows near-illegible). The dashed "soon" pill
   + Remind capture make the not-yet state explicit, not a dead disabled button. */
.tix-row--soon { opacity: 0.82; }
.tix-row--soon:hover,
.tix-row--soon:focus-within { opacity: 1; }
.tix-row--soon .tix-row__date-dm { color: var(--ink); }

/* Top-of-page reassurance — one line that sets the "ticket ≠ entry" expectation
   before the ladder, AA contrast, violet inline link. */
.tix-reassure {
  display: flex;
  align-items: flex-start;
  gap: var(--s-3);
  margin: 0 0 var(--space-block);
  max-width: 72ch;
  font-size: var(--fs-small);
  line-height: 1.6;
  color: var(--ink);
}
.tix-reassure__mark {
  flex: none;
  width: 18px;
  height: 14px;
  margin-top: 0.3em;
  background: var(--chrome-violet);
  -webkit-mask: url('/assets/chains/link-closed.svg') center / contain no-repeat;
          mask: url('/assets/chains/link-closed.svg') center / contain no-repeat;
}
.tix-reassure__text { display: inline; min-width: 0; }
.tix-reassure a {
  display: inline;
  color: var(--violet-glow);
  text-decoration: underline;
  text-underline-offset: 3px;
  font-weight: 600;
}
.tix-reassure a:hover { color: var(--violet-pale); }

/* Featured (next) row — slightly larger flyer + a violet spine so the next
   night reads as the headline action of the ladder. */
/* Featured (next) event — a big card leading the list: a large flyer on the left
   with the date / lineup / price / CTAs enlarged on the right. Every other date
   stays a compact row, so the soonest night always reads as the headline. */
.tix-row.tix-row--featured {
  grid-template-columns: minmax(0, 360px) minmax(0, 1fr);
  grid-template-areas:
    'flyer date'
    'flyer name'
    'flyer venue'
    'flyer price'
    'flyer cluster';
  align-items: center;
  column-gap: var(--space-card);
  row-gap: var(--s-2);
  margin-bottom: var(--s-6);
  padding: var(--space-card);
  border: 1px solid var(--rule-2);
  border-top: 2px solid var(--violet);
  border-radius: var(--radius);
  background:
    radial-gradient(130% 130% at 0% 0%, rgba(122, 0, 223, 0.16), transparent 55%),
    var(--bg-tint);
}
.tix-row--featured .tix-row__flyer {
  grid-row: 1 / -1;
  width: 100%;
  aspect-ratio: 16 / 9;   /* show the whole flyer (no side-crop), centred vertically */
  align-self: center;
}
.tix-row--featured .tix-row__tag { align-self: flex-start; }   /* compact badge, not a full-width bar */
.tix-row--featured .tix-row__date-dm { font-size: clamp(2rem, 3.6vw, 2.8rem); line-height: 1; }
.tix-row--featured .tix-row__date-yr { font-size: 0.78rem; }
.tix-row--featured .tix-row__name {
  font-family: var(--display);
  font-weight: 600;
  font-size: clamp(1.05rem, 1.8vw, 1.35rem);
  letter-spacing: 0.01em;
  text-transform: none;
  white-space: normal;
  color: var(--ink);
}
.tix-row--featured .tix-row__venue { font-size: 0.72rem; }
.tix-row--featured .tix-row__price { justify-self: start; text-align: left; font-size: 1.05rem; }
.tix-row--featured .tix-row__cluster { justify-self: start; flex-wrap: wrap; }
.tix-row--featured .tix-row__cta { min-height: 52px; }
@media (max-width: 720px) {
  .tix-row.tix-row--featured {
    grid-template-columns: 1fr;
    grid-template-areas: 'flyer' 'date' 'name' 'venue' 'price' 'cluster';
    row-gap: var(--s-3);
  }
  .tix-row--featured .tix-row__flyer {
    grid-row: auto;
    width: 100%;
    height: auto;
    min-height: 0;
    aspect-ratio: 16 / 9;
  }
  .tix-row--featured .tix-row__cluster { justify-self: stretch; }
}
.tix-row__tag {
  display: inline-block;
  margin-bottom: 0.3em;
  font-family: var(--mono);
  font-size: 0.62rem;
  font-weight: 700;
  letter-spacing: 0.24em;
  color: #fff;
  background: var(--violet);
  padding: 0.18em 0.55em;
  border-radius: var(--radius);
}
/* Concrete scarcity cue on the soonest date — a live early-bird-ends nudge so
   urgency is real, not a flat "EARLY SALE" (raters #20). */
.tix-row__scarcity {
  display: flex;
  align-items: center;
  gap: var(--s-2);
  margin-bottom: 0.35em;
  font-family: var(--mono);
  font-size: 0.62rem;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--violet-glow);
}
.tix-row__scarcity-dot {
  flex: none;
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--live);
  animation: phase-pulse 2.4s ease-in-out infinite;
}
@media (prefers-reduced-motion: reduce) {
  .tix-row__scarcity-dot { animation: none; }
}
/* Per-row phase pill in the date column — differentiates each non-featured row
   ("Presale live" vs "Presale opens soon") so the list isn't a flat ladder of
   identical rows. Only ever rendered with text (no empty badge spans). */
.tix-row__rowstatus {
  display: inline-block;
  width: fit-content;
  margin-bottom: 0.35em;
  font-family: var(--mono);
  /* Functional status text — held at >=11px (0.72rem) so "Presale opens soon",
     the one line answering "when can I buy?", stays legible. */
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  padding: 0.18em 0.5em;
  border-radius: var(--radius);
}
.tix-row__rowstatus--live {
  color: var(--violet-pale);
  background: var(--violet-soft);
  border: 1px solid var(--violet-deep);
}
.tix-row__rowstatus--soon {
  /* Legible scheduled state — solid >=4.5:1 grey, not faint (raters: DE
     disabled/presale rows near-illegible). */
  color: #bdbac6;
  border: 1px dashed var(--steel-dim);
}
/* Non-featured price recedes — identical "from 25 EUR" on every row flattens
   perceived value, so only the featured row carries the loud price. */
.tix-row__price--muted {
  font-weight: 500;
  color: var(--ink-mid);
}
.tix-row__price--muted b { color: var(--ink-faint); }
/* Per-row phase strip under the event name (not uppercase-inherited). */
.tix-row__phase {
  display: block;
  margin-top: var(--s-2);
  text-transform: none;
  letter-spacing: 0;
}
/* Door reference next to presale — dramatises the early-bird delta. */
.tix-row__door {
  display: block;
  margin-top: 0.2em;
  font-size: 0.6rem;
  font-weight: 500;
  letter-spacing: 0.1em;
  color: var(--ink-faint);
  text-decoration: line-through;
  text-decoration-color: var(--rule-2);
}
/* "Remind me" CTA for not-yet-on-sale rows — ghost styling so it reads as an
   intentional action, not dead space. A two-line label spells out exactly what
   happens on tap ("Remind me" / "Email when presale opens") so the outline
   button is never ambiguous or inert. */
.tix-row__cta--remind {
  flex-direction: column;
  gap: 0.15em;
  padding-top: var(--s-2);
  padding-bottom: var(--s-2);
  line-height: 1.1;
  color: var(--violet-glow);
  border-color: var(--violet-glow);
  text-decoration: none;
  text-align: center;
}
.tix-row__cta--remind .tix-row__status-dot {
  position: absolute;
  top: 0.55em;
  left: 0.7em;
  background: var(--violet-glow);
  animation: phase-pulse 2.4s ease-in-out infinite;
}
.tix-row__cta--remind { position: relative; }
.tix-row__remind-main {
  font-size: 0.68rem;
  font-weight: 700;
  letter-spacing: 0.2em;
}
.tix-row__remind-sub {
  /* The only label explaining the REMIND button — keep it readable (>=11px),
     not a 8.6px whisper. */
  font-size: 0.7rem;
  font-weight: 500;
  letter-spacing: 0.04em;
  text-transform: none;
  color: var(--violet-pale);
}
.tix-row__cta--remind:hover,
.tix-row__cta--remind:focus-visible {
  color: var(--violet-pale);
  border-color: var(--violet-pale);
  background: var(--violet-soft);
}

/* Door-policy callout — promoted from a thin note to a legible AA panel. */
.door-callout {
  margin: var(--space-block) 0 0;
  padding: var(--space-card);
  background: var(--bg-tint);
  border: 1px solid var(--rule-2);
  border-left: 3px solid var(--violet);
  border-radius: var(--radius);
}
.door-callout__lead {
  margin: 0;
  display: flex;
  gap: var(--s-3);
  align-items: flex-start;
  color: var(--ink);
  font-size: var(--fs-body);
  line-height: 1.6;
}
.door-callout__mark {
  flex: none;
  width: 20px;
  height: 16px;
  margin-top: 0.25em;
  background: var(--chrome-violet);
  -webkit-mask: url('/assets/chains/link-closed.svg') center / contain no-repeat;
          mask: url('/assets/chains/link-closed.svg') center / contain no-repeat;
}
/* Lead text lives in its own flex child so the sentence flows as continuous
   prose; the inline link must stay inline (never blockified by the flex row). */
.door-callout__text { display: inline; min-width: 0; }
.door-callout__lead a,
.door-callout__text a {
  display: inline;
  color: var(--violet-glow);
  text-decoration: underline;
  text-underline-offset: 3px;
  font-weight: 600;
}
.door-callout__lead a:hover { color: var(--violet-pale); }
/* The standalone link row below stays a wrap-friendly flex row (display:block
   reserved only for those genuine standalone link rows, not the inline callout). */
.door-callout__links a { display: inline-block; }
.door-callout__links {
  margin: var(--s-4) 0 0;
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-3);
  align-items: center;
  font-family: var(--mono);
  font-size: 0.74rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-faint);
}
@media (max-width: 720px) {
  .tix-row,
  .tix-row--noflyer {
    grid-template-columns: auto minmax(0, 1fr) auto;
    grid-template-areas:
      'flyer date    price'
      'flyer name    name'
      'flyer venue   venue'
      'cluster cluster cluster';
    padding: var(--s-4) 0;
    column-gap: var(--s-4);
  }
  .tix-row--noflyer {
    grid-template-columns: minmax(0, 1fr) auto;
    grid-template-areas:
      'date    price'
      'name    name'
      'venue   venue'
      'cluster cluster';
  }
  .tix-row__flyer { width: 84px; align-self: start; margin-top: var(--s-1); }
  /* Every row keeps its thumbnail on mobile too (bespoke flyer OR violet
     date-tile) so the list never mixes a "has thumb" row with a "no thumb
     column" row — all 8 dates read as real upcoming nights. */
  .tix-row__name { white-space: normal; }
  .tix-row__cluster {
    justify-self: stretch;
    margin-top: var(--s-3);
    display: flex;
    gap: var(--s-3);
  }
  /* Buy is the dominant full-width element; secondary info defers. */
  .tix-row__cta--buy,
  .tix-row__status { flex: 2 1 0; min-height: 48px; }
  .tix-row__cta--info { flex: 1 1 0; min-height: 48px; }
}
/* Below ~430px two mono CTAs (esp. German "Tickets kaufen") no longer fit
   side by side — stack them, buy on top. */
@media (max-width: 430px) {
  .tix-row__cluster { flex-direction: column; }
  .tix-row__cta,
  .tix-row__status { width: 100%; flex: none; }
  .tix-row__cta--buy { order: -1; }   /* buy first when stacked */
}

/* --------------------------------------------------------------------------
   10c. "More from UNDR" teaser — compact sibling-brand rows, filled live by
   undr-more.js (section stays hidden unless rows exist).
   -------------------------------------------------------------------------- */
.undr-more {
  position: relative;
  z-index: 2;
  max-width: var(--max-w);
  margin: 0 auto;
  padding: var(--space-section) var(--gutter) 0;
}
.undr-more[hidden] { display: none; }
.undr-more__title {
  margin: 0 0 var(--s-2);
  padding: 0 0 var(--s-3);
  border-bottom: 1px solid var(--rule);
  font-family: var(--mono);
  font-size: 0.62rem;
  font-weight: 500;
  letter-spacing: 0.36em;
  text-transform: uppercase;
  color: var(--ink-faint);
  display: flex;
  align-items: center;
  gap: var(--s-3);
}
.undr-more__title::before {
  content: '';
  width: 22px;
  height: 2px;
  background: var(--violet);
}
.undr-more__list { list-style: none; margin: 0; padding: 0; }
.undr-more__item { border-bottom: 1px solid var(--rule); transition: border-color 200ms ease; }
.undr-more__item:hover,
.undr-more__item:focus-within { border-bottom-color: var(--violet); }
/* Rows mirror the UNLEASHED upcoming list above so both read as one system. */
.undr-more__link {
  display: grid;
  grid-template-columns: 7.5em minmax(0, 1fr) auto;
  align-items: center;
  gap: var(--s-1) var(--s-5);
  min-height: 54px;
  padding: var(--s-3) 0;
  text-decoration: none;
  color: var(--ink);
  font-family: var(--mono);
  font-size: 0.76rem;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  transition: color 180ms ease;
}
.undr-more__link:hover,
.undr-more__link:focus-visible { color: var(--violet-glow); }
.undr-more__link:focus-visible { outline: 2px solid var(--violet-glow); outline-offset: -2px; }
.undr-more__brand {
  font-family: var(--display);
  font-weight: 600;
  font-size: clamp(1.15rem, 2vw, 1.4rem);
  letter-spacing: 0.01em;
}
.undr-more__event {
  color: var(--ink-mid);
  font-weight: 500;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.undr-more__arrow {
  font-family: var(--mono);
  font-weight: 700;
  color: var(--violet-glow);
  transition: transform 220ms var(--ease-out);
}
.undr-more__link:focus-visible .undr-more__arrow { transform: translateX(5px); }
@media (hover: hover) and (pointer: fine) {
  .undr-more__link:hover .undr-more__arrow { transform: translateX(5px); }
}
@media (max-width: 600px) {
  .undr-more__link { grid-template-columns: minmax(0, 1fr) auto; }
  .undr-more__event { grid-column: 1 / -1; grid-row: 2; white-space: normal; font-size: 0.62rem; }
  .undr-more__arrow { grid-row: 1; grid-column: 2; }
}

/* --------------------------------------------------------------------------
   11. Modals (shared shell) — consumed by events.js + tickets-modal.js.
   -------------------------------------------------------------------------- */
.modal[hidden] { display: none; }
.modal {
  position: fixed;
  inset: 0;
  z-index: 50;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 4vh 4vw;
  opacity: 0;
  transition: opacity 200ms ease;
}
.modal.is-open { opacity: 1; }
.modal__backdrop {
  position: absolute;
  inset: 0;
  background: rgba(2, 0, 8, 0.86);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
}
.modal__dialog {
  position: relative;
  background: var(--bg-tint);
  border: 1px solid var(--rule-2);
  border-radius: var(--radius);
  width: min(960px, 100%);
  min-width: 0;
  max-width: calc(100vw - 8vw);
  max-height: min(92vh, 920px);
  overflow: auto;
  padding: var(--s-7) var(--space-card) var(--space-card);
  font-family: var(--sans);
  font-weight: 400;
  letter-spacing: normal;
  color: var(--ink);
}
.modal__close {
  position: absolute;
  top: var(--s-3);
  right: var(--s-3);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  appearance: none;
  background: transparent;
  border: 1px solid var(--rule-2);
  border-radius: var(--radius);
  color: var(--ink);
  cursor: pointer;
  width: 44px;
  height: 44px;
  padding: 0;
  transition: color 160ms ease, border-color 160ms ease, background 160ms ease, transform var(--dur-press) var(--ease-out);
  z-index: 2;
}
.modal__close:active { transform: scale(0.92); }
.modal__close svg { display: block; pointer-events: none; }
.modal__close:hover, .modal__close:focus-visible {
  color: var(--violet-pale);
  border-color: var(--violet-hot);
  background: var(--violet-soft);
}
.modal__body { min-height: 200px; min-width: 0; overflow: hidden; }

/* heatBurst() compat — chains.js pulses the dialog when content (re)loads. */
.is-burst { animation: unl-burst 560ms var(--ease-out); }
@keyframes unl-burst {
  0%   { box-shadow: 0 0 0 0 rgba(155, 61, 255, 0.5); }
  100% { box-shadow: 0 0 0 26px rgba(155, 61, 255, 0); }
}

#tickets-modal { z-index: 60; }

.tickets-header {
  margin: calc(-1 * var(--s-5)) 0 var(--s-2);
  padding: 0 var(--s-7) var(--s-2) 0;
  border-bottom: 1px solid var(--rule);
  font-family: var(--mono);
  text-align: left;
}
.tickets-header__kicker {
  font-size: 0.74rem;             /* was 0.6rem / 9.6px */
  letter-spacing: 0.26em;
  text-transform: uppercase;
  color: var(--violet-glow);
  font-weight: 600;
}
.tickets-header__line {
  margin: 0;
  font-size: var(--fs-label);
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--ink-mid);
  font-weight: 500;
  line-height: 1.5;
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--s-1) var(--s-2);
}
.tickets-header__line b { color: var(--ink); font-weight: 700; letter-spacing: 0.22em; }
.tickets-header__sep { color: var(--violet-hot); }
@media (max-width: 540px) {
  .tickets-header__line { font-size: 0.62rem; gap: var(--s-1) var(--s-2); }
}
#tickets-widget-slot {
  width: 100%;
  min-width: 0;
  max-width: 100%;
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
}
.modal__body iframe,
.modal__body > div,
#tickets-widget-slot iframe,
#tickets-widget-slot > div {
  width: 100% !important;
  min-width: 0 !important;
  max-width: 100% !important;
}

.tickets-fallback {
  display: inline-block;
  margin: var(--s-3) 0 var(--s-1);
  font-family: var(--mono);
  font-size: var(--fs-label);
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--violet-glow);
  text-decoration: underline;
  text-underline-offset: 3px;
  padding: var(--s-2) 0;
}
.tickets-fallback:hover, .tickets-fallback:focus-visible { color: var(--violet-pale); }

.tickets-alts {
  margin: var(--s-4) 0 var(--s-1);
  padding: var(--s-3) 0 0;
  border-top: 1px solid var(--rule);
  font-family: var(--mono);
  font-size: 0.66rem;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--ink-faint);
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--s-2) var(--s-3);
}
.tickets-alts[hidden] { display: none; }
.tickets-alts__label { font-weight: 500; }
.tickets-alts__list { display: inline-flex; flex-wrap: wrap; gap: var(--s-1) var(--s-2); align-items: baseline; }
.tickets-alts__sep  { color: var(--violet-hot); }
.tickets-alts__link {
  color: var(--ink);
  text-decoration: underline;
  text-underline-offset: 3px;
  font-weight: 600;
  letter-spacing: 0.22em;
  padding: var(--s-1) 0;
}
.tickets-alts__link:hover, .tickets-alts__link:focus-visible { color: var(--violet-pale); }

/* --------------------------------------------------------------------------
   12. Info modal — full-bleed event dossier. Flat ground, steel frame.
   -------------------------------------------------------------------------- */
.modal--info { padding: 0; }
@media (min-width: 1500px) {
  .modal--info { padding: clamp(2rem, 4vh, 3.5rem) clamp(2rem, 6vw, 6rem); }
  .modal--info .modal__dialog,
  .modal--info .modal__dialog--wide {
    height: calc(100dvh - clamp(4rem, 8vh, 7rem)) !important;
    max-width: 1650px !important;
    width: 100% !important;
    margin: 0 auto;
    border: 1px solid var(--steel-dim) !important;
    box-shadow: 0 30px 80px rgba(0, 0, 0, 0.6);
  }
}
.modal--info .modal__backdrop {
  background: rgba(2, 0, 8, 0.92);
  backdrop-filter: blur(12px) saturate(0.85);
  -webkit-backdrop-filter: blur(12px) saturate(0.85);
}
.modal--info .modal__dialog,
.modal--info .modal__dialog--wide {
  width: 100%;
  height: 100dvh;
  max-width: none;
  max-height: none;
  border: none;
  border-radius: 0;
  padding: 0;
  background: #090514;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  transform: translateY(28px) scale(0.99);
  opacity: 0;
  /* This resting state is also the EXIT target. undr-modal.js hard-hides the
     dialog 180ms after removing .is-open, so the exit transition must finish
     inside that window — otherwise the dialog vanished at ~40% of a 420ms slide.
     Fast, clean exit here; the slower, showier entrance lives on .is-open below. */
  transition: transform 180ms var(--ease-out), opacity 160ms ease;
  font-family: var(--mono);
}
/* The dialog body is the ONE scroll container of the dossier. Its track is
   hidden (the owner flagged visible tracks) — wheel / touch / keyboard
   scrolling all still work. */
.modal--info .modal__body {
  flex: 1;
  min-height: 0;
  padding: 0;
  display: block;
  overflow-y: auto;
  overflow-x: hidden;
  scrollbar-width: none;
}
.modal--info .modal__body::-webkit-scrollbar { display: none; }
.modal--info .event-detail { display: block; }
/* Command bar stays pinned while the body scrolls beneath it. */
.modal--info .event-detail__bar {
  position: sticky;
  top: 0;
  z-index: 3;
}
.modal--info.is-open .modal__dialog,
.modal--info.is-open .modal__dialog--wide {
  transform: none;
  opacity: 1;
  /* Enter is slower than exit (deliberate in, snappy out). 360ms reads as a
     confident slide-up without dragging; exit is the 180ms base transition. */
  transition: transform 360ms var(--ease-out), opacity 280ms ease;
}
.modal--info .modal__close {
  position: absolute;
  top: 8px;
  right: 12px;
  z-index: 5;
  width: 44px;
  height: 44px;
}

/* Command bar. */
.event-detail__bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-4);
  min-height: 60px;
  padding: var(--s-2) clamp(1rem, 4vw, 2rem);
  padding-right: calc(44px + var(--s-5));
  border-bottom: 1px solid var(--rule);
  font-family: var(--mono);
  font-size: 0.7rem;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: var(--ink-mid);
  background: var(--bg-tint);
  position: relative;
  z-index: 2;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.event-detail__bar__title {
  display: inline-flex;
  align-items: center;
  gap: var(--s-2);
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
}
.event-detail__bar__title b { color: var(--ink); font-weight: 700; letter-spacing: 0.22em; }
.event-detail__bar__sep { color: var(--violet-hot); }
.event-detail__bar__cd {
  font-variant-numeric: tabular-nums;
  color: var(--ink);
  font-weight: 500;
  display: inline-flex;
  align-items: center;
  gap: var(--s-2);
  flex: none;
}
@media (max-width: 720px) {
  .event-detail__bar { font-size: 0.62rem; }
  .event-detail__bar__title { gap: var(--s-2); }
}
@media (max-width: 540px) {
  .event-detail__bar__title { display: none; }
  .event-detail__bar { padding-right: calc(44px + var(--s-4)); }
}

/* Left meta panel — flat, with a grayscale strand resting at the bottom. */
.event-detail__meta {
  position: relative;
  padding: var(--space-card);
  border-right: 1px solid var(--rule);
  background: transparent;
}
.event-detail__meta::after {
  content: '';
  position: absolute;
  left: -12%;
  bottom: -6%;
  width: 120%;
  aspect-ratio: 900 / 907;
  background: url('/assets/chains/strand-diag-left.webp') center / contain no-repeat;
  filter: grayscale(1);
  opacity: 0.07;
  pointer-events: none;
  z-index: -1;
}

/* Layout: ≥1100px two columns — the meta panel has no scroll container of its
   own; it sticks to the top of the dialog scroller (align-items:start). Only
   when the viewport is too short for the full panel does it scroll
   internally, with hidden tracks (wheel/keyboard still scroll). Below 1100px
   everything stacks in one flow inside the dialog scroller. */
@media (min-width: 1100px) {
  .event-detail__layout {
    display: grid;
    grid-template-columns: minmax(320px, 380px) minmax(0, 1fr);
    align-items: start;
  }
  .event-detail__meta {
    position: sticky;
    top: 0;
    max-height: calc(100dvh - 60px);     /* dialog minus command bar */
    overflow-y: auto;                    /* engages only at short viewports */
    scrollbar-width: none;               /* …and never shows a track */
    border-right: none;
  }
  .event-detail__meta::-webkit-scrollbar { display: none; }
  /* Keep the decorative strand inside the panel so it can't create
     sideways scrollable overflow. */
  .event-detail__meta::after { left: 0; bottom: 0; width: 100%; }
  /* The column rule lives on the (taller) body so it spans full height. */
  .event-detail__body { border-left: 1px solid var(--rule); }
}
@media (min-width: 1500px) {
  .event-detail__meta { max-height: calc(100dvh - clamp(4rem, 8vh, 7rem) - 60px); }
}
@media (max-width: 1099.98px) {
  .event-detail__layout {
    display: flex;
    flex-direction: column;
    padding-bottom: clamp(2rem, 4vw, 3rem);
  }
  .event-detail__body { display: contents; }
  .ev-section { order: 3; }
  .event-detail__meta + .ev-section,
  .ev-section:first-of-type { margin-top: var(--space-card); }
  .event-detail__meta {
    order: 2;
    overflow: visible;
    border-right: none;
    border-top: 1px solid var(--rule);
    display: flex;
    flex-direction: column;
  }
  .event-detail__tags         { order: 1; margin: 0 0 var(--s-3); }
  .event-detail__assertions   { order: 2; margin: 0 0 var(--s-5); }
  .event-detail__brand        { order: 3; }
  .event-detail__date-display { order: 4; }
  .event-detail__name         { order: 5; }
  .event-detail__tagline      { order: 6; }
  .event-detail__stats        { order: 7; }
  .event-detail__cta,
  .event-detail__cta-soon     { order: 8; }
}
@media (max-width: 540px) {
  .event-detail__brand { display: none; }
  .event-detail__stats__age { display: none; }
  .event-detail__date-display,
  .event-detail__name,
  .event-detail__tagline { display: none; }
  .event-detail__stats { margin-top: 0; }
  .event-detail__meta { padding-top: var(--s-4); }
}

/* Short viewports (e.g. 1440×620, 1280×700): even at desktop widths the sticky
   meta column can't be taller than the dialog without scrolling internally —
   which the owner flagged. Collapse to the same single-flow stack the <1100px
   layout uses so the meta flows with the dossier body in the one dialog
   scroller. The threshold (720px) is set so the two flagged sizes (×620, ×700)
   both unstack while full-height monitors keep the two-column dossier. Source
   order + equal specificity lets this win over the min-width:1100px rules. */
@media (min-width: 1100px) and (max-height: 720px) {
  .event-detail__layout {
    display: flex;
    flex-direction: column;
    padding-bottom: clamp(2rem, 4vw, 3rem);
  }
  .event-detail__body {
    display: contents;
    border-left: none;
  }
  .ev-section { order: 3; }
  .event-detail__meta + .ev-section,
  .ev-section:first-of-type { margin-top: var(--space-card); }
  .event-detail__meta {
    order: 2;
    position: static;
    max-height: none;
    overflow: visible;
    border-right: none;
    border-top: 1px solid var(--rule);
    display: flex;
    flex-direction: column;
  }
  .event-detail__tags         { order: 1; margin: 0 0 var(--s-3); }
  .event-detail__assertions   { order: 2; margin: 0 0 var(--s-3); }
  .event-detail__date-display { order: 4; }
  .event-detail__name         { order: 5; }
  .event-detail__tagline      { order: 6; }
  .event-detail__stats        { order: 7; }
  .event-detail__cta,
  .event-detail__cta-soon     { order: 8; }
}

/* Brand kicker dropped from the dossier meta — the command bar + footer already
   carry "UNLEASHED · BY UNDR", so this line was pure vertical cost (owner asked
   the meta to fit without internal scroll). Kept in markup for older clones. */
.event-detail__brand { display: none; }

.event-detail__date-display {
  font-family: var(--display);
  font-size: clamp(1.8rem, 3.6vw, 2.4rem);  /* ~20% smaller — frees panel height */
  font-weight: 700;
  letter-spacing: -0.015em;
  margin: 0 0 var(--s-2);
  color: var(--ink);
  line-height: 1;
  font-variant-numeric: tabular-nums;
}
.event-detail__date-display .day {
  display: block;
  font-family: var(--mono);
  font-size: 0.36em;
  letter-spacing: 0.42em;
  margin-bottom: var(--s-3);
  font-weight: 600;
  color: var(--violet-glow);
}

.event-detail__name {
  font-family: var(--display);
  font-size: var(--fs-h3);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink);
  margin: var(--s-3) 0 var(--s-2);
  font-weight: 600;
}
.event-detail__name span {
  color: var(--violet-glow);
  font-weight: 500;
  letter-spacing: 0.1em;
  display: inline-block;
  margin-left: var(--s-2);
}
.event-detail__tagline {
  font-family: var(--mono);
  font-size: 0.78rem;
  font-style: italic;
  color: var(--ink-mid);
  margin: 0 0 var(--s-3);
  line-height: 1.6;
}

.event-detail__stats {
  list-style: none;
  margin: var(--s-4) 0;
  padding: 0;
  border-top: 1px solid var(--rule);
  font-family: var(--mono);
}
.event-detail__stats > div {
  display: grid;
  grid-template-columns: 5em 1fr;
  gap: var(--s-3);
  align-items: baseline;
  padding: var(--s-2) 0;          /* tighter rows — meta must fit without scroll */
  border-bottom: 1px solid var(--rule);
}
.event-detail__stats dt {
  color: var(--violet-glow);
  font-size: 0.6rem;
  letter-spacing: 0.32em;
  text-transform: uppercase;
  font-weight: 500;
  margin: 0;
}
.event-detail__stats dd {
  color: var(--ink);
  margin: 0;
  font-size: 0.88rem;
  font-weight: 500;
  letter-spacing: 0.02em;
}
@media (max-width: 540px) {
  .event-detail__stats > .event-detail__stats__age { display: none; }
}

.event-detail__tags {
  display: flex; flex-wrap: wrap; gap: var(--s-2);
  list-style: none; margin: var(--s-2) 0; padding: 0;
}
.event-detail__tags li {
  font-family: var(--mono);
  font-size: 0.58rem;
  letter-spacing: 0.3em;
  text-transform: uppercase;
  color: var(--ink-mid);
  border: 1px solid var(--rule-2);
  background: transparent;
  padding: var(--s-1) var(--s-3);
  border-radius: var(--radius);
  font-weight: 600;
}

.event-detail__assertions {
  font-family: var(--mono);
  font-size: 0.6rem;
  letter-spacing: 0.32em;
  text-transform: uppercase;
  color: var(--ink-faint);
  margin: 0 0 var(--s-2);
  font-weight: 500;
}

.event-detail__cta-soon {
  margin-top: var(--s-5);
  padding-top: var(--s-5);
  border-top: 1px solid var(--rule);
  font-family: var(--mono);
  font-size: 0.66rem;
  letter-spacing: 0.3em;
  text-transform: uppercase;
  color: var(--ink-faint);
  text-align: center;
}

.event-detail__cta {
  margin-top: var(--s-4);
  padding-top: var(--s-4);
  border-top: 1px solid var(--rule);
}
@media (max-width: 1099.98px) {
  .event-detail__cta,
  .event-detail__cta-soon {
    margin-top: var(--s-3);
    padding-top: 0;
    border-top: 0;
  }
  .event-detail__stats { margin-bottom: var(--s-3); }
}
.event-detail__cta__btn {
  display: inline-flex;
  width: 100%;
  justify-content: center;
}

/* Right column. */
.event-detail__body {
  padding: var(--space-card) 0 var(--s-7);
  font-family: var(--sans);
  color: var(--ink-mid);
  line-height: 1.75;
  font-size: var(--fs-body);
}

.event-detail__hero-flyer {
  margin: calc(-1 * var(--space-card)) 0 var(--space-card);
  background: #000;
  position: relative;
  border-bottom: 1px solid var(--rule);
  aspect-ratio: 1920 / 1080;
  width: 100%;
  overflow: hidden;
}
.event-detail__hero-flyer img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: contain;
  object-position: center;
}
@media (max-width: 1099.98px) {
  .event-detail__hero-flyer {
    order: 1;
    margin: 0;
    flex: 0 0 auto;
  }
}

/* Promo-video play — one quiet steel ring, violet on hover. */
.hero-flyer__play {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: rgba(7, 3, 16, 0.55);
  border: 1px solid rgba(241, 236, 250, 0.7);
  border-radius: 50%;
  width: 88px;
  height: 88px;
  padding: 0;
  cursor: pointer;
  z-index: 2;
  color: #ffffff;
  -webkit-tap-highlight-color: transparent;
  transition: border-color 200ms ease, background 200ms ease, transform 200ms ease;
}
.hero-flyer__play:hover,
.hero-flyer__play:focus-visible {
  border-color: var(--violet-glow);
  background: rgba(61, 0, 112, 0.5);
  transform: translate(-50%, -50%) scale(1.05);
}
.hero-flyer__play:focus-visible {
  outline: 2px solid var(--violet-glow);
  outline-offset: 6px;
}
.hero-flyer__play__reticle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
.hero-flyer__play__icon {
  display: block;
  width: 34px;
  height: 34px;
  transform: translateX(3px);
}
.event-detail__hero-flyer.is-playing .hero-flyer__play { display: none; }

@media (max-width: 560px) {
  .hero-flyer__play { width: 68px; height: 68px; }
  .hero-flyer__play__icon { width: 26px; height: 26px; }
}

@media (forced-colors: active) {
  .hero-flyer__play { border-color: CanvasText; color: ButtonText; }
}

.hero-flyer__video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  z-index: 3;
  background: #000;
  object-fit: contain;
}

/* Numbered narrative sections. */
.ev-section {
  position: relative;
  padding: 0 var(--space-card);
  margin: 0 0 var(--space-card);
}
.ev-section + .ev-section { padding-top: var(--s-2); }
.ev-section__title {
  font-family: var(--mono);
  font-size: 0.74rem;
  letter-spacing: 0.32em;
  text-transform: uppercase;
  color: var(--ink);
  margin: 0 0 var(--s-4);
  padding-left: 0;
  font-weight: 700;
  display: flex;
  align-items: baseline;
  gap: var(--s-3);
  position: relative;
}
.ev-section__title::before {
  content: '';
  display: inline-block;
  width: 2px;
  align-self: stretch;
  background: var(--violet);
  flex: none;
}
.ev-section__title .num {
  font-weight: 600;
  font-size: 0.92em;
  letter-spacing: 0.22em;
  color: var(--violet-glow);
}
.ev-section__prose {
  color: var(--ink-mid);
  line-height: 1.75;
  max-width: 70ch;
}
.ev-section__prose p { margin: 0 0 var(--s-4); }
.ev-section__prose p:last-child { margin-bottom: 0; }

/* Facts list — steel dash markers. */
.ev-list {
  list-style: none; padding: 0; margin: 0;
  border-top: 1px solid var(--rule);
  max-width: 75ch;
}
.ev-list li {
  position: relative;
  padding: var(--s-3) 0 var(--s-3) var(--s-5);
  color: var(--ink-mid);
  border-bottom: 1px solid var(--rule);
  line-height: 1.65;
  font-size: 0.96rem;
}
.ev-list li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 1.35em;
  width: 12px;
  height: 2px;
  background: var(--violet);
}

/* Tiers. */
.ev-tiers {
  list-style: none; margin: 0; padding: 0;
  font-family: var(--mono);
  border-top: 1px solid var(--rule);
}
.ev-tiers li {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  padding: var(--s-3) 0;
  border-bottom: 1px solid var(--rule);
  font-size: 0.92rem;
}
.ev-tiers__name {
  letter-spacing: 0.22em;
  text-transform: uppercase;
  font-weight: 600;
  color: var(--ink);
  display: inline-flex;
  align-items: baseline;
  gap: var(--s-3);
}
.ev-tiers__sold { display: none; }
.ev-tiers li:has(.ev-tiers__sold) {
  text-decoration: line-through;
  text-decoration-color: var(--live);
  text-decoration-thickness: 1px;
  color: var(--ink-faint);
}
.ev-tiers li:has(.ev-tiers__sold) .ev-tiers__price {
  color: var(--ink-faint);
}
.ev-section__cta {
  margin-top: var(--s-5);
  display: flex;
  justify-content: flex-start;
}
.ev-section__cta .btn-tickets { width: 100%; max-width: 360px; justify-content: center; }
@media (max-width: 540px) {
  .ev-section__cta .btn-tickets { max-width: none; }
}
.ev-tiers__price {
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  font-size: 1.05em;
  color: var(--violet-glow);
}
.ev-tiers__price em {
  font-style: normal;
  color: var(--ink-faint);
  font-weight: 400;
  font-size: 0.78em;
  margin-left: var(--s-2);
}

/* Lineup — flat floor panels, violet light on the left edge. */
.ev-section--lineup .lineup {
  display: grid;
  gap: var(--s-4);
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
}
.lineup__floor {
  position: relative;
  padding: var(--space-card);
  border: 1px solid var(--rule);
  border-left: 2px solid var(--violet);
  border-radius: var(--radius);
  background: var(--bg-tint);
  overflow: hidden;
  font-family: var(--mono);
}
.lineup__head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--s-3);
  margin: 0 0 var(--s-4);
  padding-bottom: var(--s-3);
  border-bottom: 1px solid var(--rule);
}
.lineup__floor-name {
  font-size: 0.7rem;
  letter-spacing: 0.36em;
  text-transform: uppercase;
  color: var(--ink);
  font-weight: 700;
}
.lineup__floor-music {
  font-size: 0.6rem;
  letter-spacing: 0.32em;
  text-transform: uppercase;
  color: var(--violet-glow);
  font-weight: 500;
}
.lineup__djs {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--s-2);
}
.lineup__djs li {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-3);
  padding: var(--s-2) 0;
  font-family: var(--mono);
}
.lineup__dj {
  font-size: 1.1rem;
  letter-spacing: 0.02em;
  font-weight: 600;
  color: var(--ink);
  text-transform: none;
  font-family: var(--display);
}
.lineup__artist { display: contents; }
.lineup__djs li.lineup__set--b2b {
  justify-content: flex-start;
  flex-wrap: wrap;
  row-gap: var(--s-2);
}
.lineup__set--b2b .lineup__artist {
  display: inline-flex;
  align-items: center;
  gap: var(--s-2);
}
.lineup__b2b {
  font-size: 0.7rem;
  letter-spacing: 0.18em;
  font-weight: 700;
  text-transform: uppercase;     /* billing separator always reads B2B */
  color: var(--violet-glow);
}
.lineup__links { display: inline-flex; gap: var(--s-1); flex: none; flex-wrap: wrap; }
.lineup__link {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 0.55rem;
  letter-spacing: 0.24em;
  font-weight: 700;
  border: 1px solid var(--rule-2);
  min-width: 28px;
  min-height: 24px;
  padding: 0 var(--s-2);
  color: var(--violet-glow);
  background: transparent;
  text-decoration: none;
  border-radius: var(--radius);
  transition: color 140ms ease, border-color 140ms ease, background 140ms ease;
}
.lineup__link:hover {
  color: var(--ink);
  background: var(--violet);
  border-color: var(--violet);
}

/* --------------------------------------------------------------------------
   13. Generic page kit (contract for content pages).
   -------------------------------------------------------------------------- */
.page {
  position: relative;
  z-index: 2;
  /* Bottom gap is owned by the footer's own margin-top (--space-section) — the
     page no longer adds a second --space-block on top, which stacked into a
     large dead void before the chain divider (esp. news desktop). A small flush
     keeps the last section from kissing the divider. */
  padding: var(--space-section) 0 var(--s-5);
}
/* Content pages share the header's column: same max width, same gutter.
   TEXT blocks cap themselves at ~70ch (.prose/.page-lead/.notice/.faq-item__a);
   grids, galleries and lists run the full column. */
.page__inner {
  max-width: var(--max-w);
  margin: 0 auto;
  padding: 0 var(--gutter);
}
.page-kicker {
  margin: 0 0 var(--s-4);
  font-family: var(--mono);
  font-size: var(--fs-label);
  font-weight: 500;
  letter-spacing: 0.38em;
  text-transform: uppercase;
  color: var(--violet-glow);
  display: flex;
  align-items: center;
  gap: var(--s-3);
}
.page-title {
  margin: 0 0 var(--s-4);
  font-family: var(--display);
  font-weight: 700;
  font-size: var(--fs-h1);
  line-height: 1.02;
  letter-spacing: -0.015em;
  /* Tight tracking can swallow the inter-word gap on multi-word H1s
     ("Get in touch" → "Getin touch"); restore an even word gap. */
  word-spacing: 0.12em;
  color: var(--ink);
}
/* Title underline: 2px violet bar with the double-link glyph at its end
   (the photographic divider goes muddy below ~30px tall). */
.page-title::after {
  content: '';
  display: block;
  width: 152px;
  height: 22px;
  margin-top: var(--s-5);
  background:
    url('/assets/chains/link-closed.svg') right center / 29px 22px no-repeat,
    linear-gradient(var(--violet), var(--violet)) left center / 120px 2px no-repeat;
}
.page-lead,
.page-intro {
  margin: 0 0 var(--space-block);
  font-size: var(--fs-lead);
  line-height: 1.65;
  color: var(--ink-mid);
  max-width: 70ch;
}
/* Small mono metadata line (e.g. a legal "last updated" date) — reads as a
   deliberate data label, not an orphan italic stub inside the prose. */
.page-meta {
  margin: var(--s-3) 0;
  font-family: var(--mono);
  font-size: var(--fs-small);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink-faint);
}

/* Typographic rhythm for long-form content. */
.prose {
  color: var(--ink-mid);
  line-height: 1.75;
  font-size: var(--fs-body);
  max-width: 70ch;
}
.prose p { margin: 0 0 var(--s-4); }
.prose h2 {
  margin: var(--s-6) 0 var(--s-3);
  font-family: var(--display);
  font-weight: 600;
  font-size: var(--fs-h2);
  letter-spacing: 0;
  line-height: 1.15;
  color: var(--ink);
}
.prose h3 {
  margin: var(--s-6) 0 var(--s-3);
  font-family: var(--mono);
  font-weight: 700;
  font-size: 0.82rem;
  letter-spacing: 0.26em;
  text-transform: uppercase;
  color: var(--violet-glow);
}
.prose ul, .prose ol { margin: 0 0 var(--s-4); padding-left: var(--s-5); }
.prose li { margin: 0 0 var(--s-2); }
.prose ul li::marker { color: var(--violet-hot); }
.prose ol li::marker { color: var(--violet-glow); font-family: var(--mono); font-weight: 700; }
.prose a {
  color: var(--ink);
  text-decoration: underline;
  text-decoration-color: rgba(155, 61, 255, 0.6);
  text-underline-offset: 3px;
  transition: color 140ms ease, text-decoration-color 140ms ease;
}
.prose a:hover { color: var(--violet-glow); text-decoration-color: var(--violet-glow); }
.prose blockquote {
  margin: var(--s-5) 0;
  padding: var(--s-2) 0 var(--s-2) var(--s-5);
  border-left: 2px solid var(--violet);
  color: var(--ink);
  font-style: italic;
}
.prose figure { margin: var(--s-6) 0; }
.prose figcaption {
  margin-top: var(--s-2);
  font-family: var(--mono);
  font-size: 0.68rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink-faint);
}
.prose img,
.prose video {
  display: block;
  width: 100%;
  border-radius: var(--radius);
  border: 1px solid var(--rule);
}
.prose hr {
  border: 0;
  height: clamp(16px, 2vw, 22px);
  margin: var(--space-block) 0;
  background: url('/assets/chains/divider.webp') repeat-x center / auto 100%;
  opacity: 0.4;
  -webkit-mask-image: linear-gradient(90deg, transparent 0, #000 10%, #000 90%, transparent 100%);
          mask-image: linear-gradient(90deg, transparent 0, #000 10%, #000 90%, transparent 100%);
}
.prose strong { color: var(--ink); }
.prose code {
  font-family: var(--mono);
  font-size: 0.9em;
  background: var(--bg-tint);
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  padding: 0.1em 0.35em;
}

/* News cards — flat, hover lives in the border only. */
.card-grid {
  display: grid;
  /* min(100%, 340px) so the track can never exceed the content box: on 360–420px
     phones the 340px floor would otherwise clip card padding (the body overflow
     guard hides the scrollbar but content still got nudged). */
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 340px), 1fr));
  gap: var(--s-5);
  margin: var(--space-block) 0;
}
@media (max-width: 420px) {
  .card-grid { grid-template-columns: 1fr; }
}
/* Feature tiles — ONE shared modifier for Rules "conduct in the room", About
   "what to expect" and Dress-code "build your look": a violet top-edge + a
   chrome-lit chain-link icon so the items read as branded, separated tiles, not
   a wall of grey text (raters). Replaced the duplicate .card--house/.card--expect
   pair so the three grids can never drift apart. */
.card--feature {
  border-color: var(--rule-2);
  border-top: 2px solid var(--violet);
}
.card--feature__title {
  display: flex;
  align-items: center;
  gap: var(--s-3);
  color: var(--ink);
  font-weight: 700;
}
.card--feature__icon {
  flex: none;
  width: 20px;
  height: 15px;
  background: var(--chrome-violet);
  -webkit-mask: url('/assets/chains/link-closed.svg') center / contain no-repeat;
          mask: url('/assets/chains/link-closed.svg') center / contain no-repeat;
}
.card--feature > p { color: var(--ink-mid); }

.card {
  position: relative;
  display: flex;
  flex-direction: column;
  background: var(--bg-tint);
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  overflow: hidden;
  transition: border-color 200ms ease;
}
.card:hover { border-color: var(--steel-dim); }
.card__media {
  position: relative;
  aspect-ratio: 16 / 9;
  overflow: hidden;
  border-bottom: 1px solid var(--rule);
  background: var(--bg-tint);
}
.card__media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
/* Past / upcoming status badge on news cards — date-aware proof that the site
   is alive (no 2025 post pretends to be buyable). */
.card__badge,
.post__badge {
  font-family: var(--mono);
  font-size: 0.58rem;
  font-weight: 700;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  padding: 0.28em 0.6em;
  border-radius: var(--radius);
}
.card__badge {
  position: absolute;
  top: var(--s-3);
  left: var(--s-3);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
}
.card__badge--past,
.post__badge--past {
  color: var(--ink);
  background: rgba(11, 6, 24, 0.72);
  border: 1px solid var(--rule-2);
}
.card__badge--upcoming,
.post__badge--upcoming {
  color: #fff;
  background: var(--violet);
  border: 1px solid var(--violet);
}
/* Award: a trophy/recognition chip — violet-outlined, lit text — distinct from
   both the neutral PAST and the solid UPCOMING so an award reads as an award. */
.card__badge--award,
.post__badge--award {
  color: var(--violet-glow);
  background: rgba(11, 6, 24, 0.72);
  border: 1px solid var(--violet);
}
/* Note: behind-the-scenes / editorial — same neutral chrome as PAST but with a
   muted ink so it reads as commentary, not a dated night. */
.card__badge--note,
.post__badge--note {
  color: var(--ink-2, rgba(241, 236, 250, 0.74));
  background: rgba(11, 6, 24, 0.72);
  border: 1px solid var(--rule-2);
}
.post__badge { display: inline-block; margin-right: var(--s-3); }
/* Past-event posts: never surface an inline ticket CTA into a dead checkout. */
.is-past-post .post-cta-inline { display: none; }

/* News index subhead — larger, brighter and pulled closer to the title so it
   reads as one unit (was small light-grey + a tiny chain accent). */
.page-intro--news {
  margin-top: calc(var(--s-5) * -1 + var(--s-2));
  max-width: 60ch;
  font-size: clamp(1.15rem, 2vw, 1.35rem);
  color: var(--ink);
}

/* Pinned "Next party" callout above the news grid — the only ticket path on
   the editorial index. Squared, hairline border, single violet fill (the CTA). */
.next-party {
  margin: 0 0 var(--s-5);
  padding: var(--space-card);
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  grid-template-areas:
    'tag    tag'
    'main   action';
  align-items: center;
  gap: var(--s-4) var(--s-6);
  /* Elevated above the recap grid: a brighter violet wash + full accent border
     + glow so the next party never reads as just another recap row. */
  background:
    radial-gradient(140% 120% at 0% 0%, rgba(122, 0, 223, 0.16), transparent 55%),
    var(--bg-tint);
  border: 1px solid var(--violet-deep);
  border-left: 4px solid var(--violet);
  border-radius: var(--radius);
  box-shadow: 0 0 0 1px rgba(122, 0, 223, 0.12), 0 8px 30px rgba(0, 0, 0, 0.35);
}
/* Section divider that splits the LIVE next-party card from the PAST recap grid.
   Generous top gap (~96-120px) so the eye gets a clear break before the dense
   3-col grid, with a violet-accented eyebrow + full-width rule (raters: gap was
   tight, eyebrow tiny/low-contrast). */
.next-party__recap-label {
  margin: clamp(4rem, 9vw, 7rem) 0 var(--s-5);
  display: flex;
  align-items: center;
  gap: var(--s-3);
  font-family: var(--mono);
  font-size: 0.875rem;            /* 14px */
  font-weight: 700;
  letter-spacing: 0.28em;
  text-transform: uppercase;
  color: var(--ink);
  padding-bottom: var(--s-3);
  border-bottom: 1px solid var(--rule-2);
}
.next-party__recap-label::before {
  content: '';
  flex: none;
  width: 22px;
  height: 2px;
  background: var(--violet);
}
.next-party__tag {
  grid-area: tag;
  margin: 0;
  display: flex;
  align-items: center;
  gap: var(--s-3);
  font-family: var(--mono);
  font-size: var(--fs-label);
  font-weight: 700;
  letter-spacing: 0.32em;
  text-transform: uppercase;
  color: var(--violet-glow);
}
.next-party__main { grid-area: main; }
.next-party__date {
  margin: 0 0 var(--s-1);
  font-family: var(--mono);
  font-size: 0.74rem;
  font-weight: 600;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: var(--violet-glow);
}
.next-party__name {
  margin: 0 0 var(--s-1);
  font-family: var(--display);
  font-weight: 700;
  font-size: clamp(1.5rem, 3.5vw, 2.2rem);
  line-height: 1.05;
  color: var(--ink);
}
.next-party__meta {
  margin: 0 0 var(--s-3);
  font-family: var(--mono);
  font-size: 0.74rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink-mid);
}
/* Persuasive body copy stays in the brand body sans (not mono) — mono is for
   small ALL-CAPS labels only across the site. */
.next-party__fomo {
  margin: 0;
  max-width: 48ch;
  font-family: var(--sans);
  color: var(--ink-mid);
  line-height: 1.55;
}
.next-party__tag-sep { color: var(--rule-2); }
.next-party__countdown {
  color: #fff;
  font-variant-numeric: tabular-nums;
}
.next-party__action {
  grid-area: action;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: var(--s-3);
  text-align: right;
}
/* Presale-live / soon micro-tag above the buy button — turns the card into an
   actionable, urgent unit rather than a recap. */
.next-party__urgent {
  display: inline-flex;
  align-items: center;
  gap: var(--s-2);
  font-family: var(--mono);
  font-size: 0.66rem;
  font-weight: 700;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--violet-glow);
}
.next-party__urgent-dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--violet-glow);
  animation: phase-pulse 2.4s ease-in-out infinite;
}
.next-party__phase { display: block; }
@media (max-width: 620px) {
  .next-party {
    grid-template-columns: 1fr;
    grid-template-areas: 'tag' 'main' 'action';
  }
  .next-party__action { align-items: stretch; text-align: left; }
  .next-party__action .btn { width: 100%; }
}
.card__body { padding: var(--space-card); display: flex; flex-direction: column; gap: var(--s-2); flex: 1; }
/* Bare cards (rules/dresscode/tickets info cards): title + paragraph live
   directly inside .card — give them the card inset themselves. */
.card > .card__title { margin: var(--space-card) var(--space-card) var(--s-2); }
.card > p {
  margin: 0 var(--space-card) var(--space-card);
  /* Bare-card body (rules "Conduct in the room", about "What to expect",
     dress-code guide): 15px at a >=4.5:1 light tone (raters: ~12px low-contrast
     body). --ink-mid (0.78 alpha) clears AA on the bg. */
  font-size: 0.9375rem;
  color: var(--ink-mid);
  line-height: 1.6;
}
/* "Where we sell" vendor cards — underground texture: a chrome top-edge,
   an oversized index number watermark and a chain-link tick, so they don't
   read like a generic SaaS feature grid. */
.vendor-card {
  padding: var(--space-card);
  gap: var(--s-3);
}
.vendor-card__edge {
  position: absolute;
  inset: 0 0 auto 0;
  height: 2px;
  background: var(--chrome);
  opacity: 0.85;
}
.vendor-card:hover .vendor-card__edge { background: var(--chrome-violet); opacity: 1; }
.vendor-card__no {
  position: absolute;
  top: var(--s-2);
  right: var(--s-3);
  margin: 0;
  font-family: var(--display);
  font-weight: 600;
  font-size: 2.6rem;
  line-height: 1;
  color: var(--violet-soft);
  pointer-events: none;
  user-select: none;
}
.vendor-card__title {
  display: flex;
  align-items: flex-start;
  gap: var(--s-3);
  min-height: 2.5em;   /* reserve 2 lines so all three card bodies start at one Y */
}
.vendor-card__title::before {
  content: '';
  flex: none;
  width: 24px;
  height: 18px;
  background: var(--chrome-violet);
  -webkit-mask: url('/assets/chains/link-closed.svg') center / contain no-repeat;
          mask: url('/assets/chains/link-closed.svg') center / contain no-repeat;
}
/* Seller name is now an outbound link — violet + underline, focusable. */
.vendor-card__link {
  /* Inline-block so the ↗ flows right after the seller name even when the name
     wraps to 2 lines (inline-flex floated the arrow to the vertical centre). The
     h3 line-height already clears the 24px WCAG 2.5.8 tap-target floor. */
  display: inline-block;
  min-height: 24px;
  color: var(--violet-glow);
  text-decoration: underline;
  text-decoration-color: rgba(188, 123, 255, 0.5);
  text-underline-offset: 3px;
  transition: color 140ms ease, text-decoration-color 140ms ease;
}
.vendor-card__link:hover { color: var(--violet-pale); text-decoration-color: var(--violet-pale); }
.vendor-card__ext { font-size: 0.8em; }
.vendor-card__text {
  margin: 0;
  /* 15px at a >=4.5:1 tone — same body treatment EN + DE so the denser German
     provider copy reads as legibly as English (raters #31). */
  font-size: 0.9375rem;
  color: var(--ink-mid);
  line-height: 1.6;
}
.card__date {
  font-family: var(--mono);
  font-size: 0.75rem;             /* >=12px — was sub-12px violet */
  font-weight: 500;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--ink-mid);          /* light on dark, AA; violet reserved for category labels */
}
.card__title {
  margin: 0;
  font-family: var(--display);
  font-weight: 600;
  font-size: var(--fs-h3);
  line-height: 1.25;
  color: var(--ink);
}
.card__title a { text-decoration: none; }
.card__title a::after { content: ''; position: absolute; inset: 0; }   /* whole-card link */
.card__excerpt { margin: 0; font-size: var(--fs-small); color: var(--ink-mid); line-height: 1.6; }
.card__link {
  margin-top: auto;
  padding-top: var(--s-3);
  font-family: var(--mono);
  font-size: 0.66rem;
  font-weight: 700;
  letter-spacing: 0.26em;
  text-transform: uppercase;
  color: var(--violet-glow);
  text-decoration: none;
}
.card__link::after { content: ' →'; }

/* Blog post page. */
/* Full content width like every other page: head, hero and prose all align to
   the left edge of .page__inner and the hero image spans the full column. The
   prose keeps a ~75ch measure but stays LEFT-aligned (not centered) so it reads
   as the same layout system as the rest of the site. */
.post { position: relative; }
/* Body fills the full content width to match the hero / CTA / read-next (owner
   wants the blog full-width). Generous size + line-height keep the longer
   measure legible; solid high-contrast tone (raters: muted-gray body marginal). */
.post .prose {
  max-width: none;
  font-size: 1.09rem;             /* ~17.5px */
  line-height: 1.7;
  color: #D6D0E0;                 /* solid, >=10:1 on bg — no low-alpha body */
}
.post .prose p { margin: 0 0 var(--s-5); }
.post__head { margin: 0 0 var(--space-block); }
.post__kicker {
  margin: 0 0 var(--s-3);
  font-family: var(--mono);
  font-size: 0.76rem;             /* was 0.64rem / 10.24px */
  font-weight: 500;
  letter-spacing: 0.28em;
  text-transform: uppercase;
  color: var(--violet-glow);
}
/* Back-to-news link: 44px tap target without bloating the kicker visually. */
.post__kicker a { display: inline-flex; align-items: center; min-height: 44px; margin: calc(var(--s-2) * -1) 0; }
.post__date {
  display: block;
  margin: var(--s-3) 0 0;
  font-family: var(--mono);
  /* Was 0.68rem / 10.9px at faint alpha — lifted to 13px at a solid >=4.5:1 tone
     (raters: meta below AA / cramped). */
  font-size: 0.8125rem;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--ink-mid);
}
/* Share row under the title — small mono labels + tappable chips. */
.post-share {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s-3);
  margin: var(--s-5) 0 0;
}
.post-share__label {
  font-family: var(--mono);
  font-size: var(--fs-label);
  font-weight: 600;
  letter-spacing: 0.26em;
  text-transform: uppercase;
  color: var(--ink-faint);
}
.post-share__btn {
  appearance: none;
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  padding: var(--s-2) var(--s-4);
  font-family: var(--mono);
  font-size: 0.7rem;
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  text-decoration: none;
  color: var(--ink);
  background: transparent;
  border: 1px solid var(--rule-2);
  border-radius: var(--radius);
  cursor: pointer;
  transition: color 160ms ease, border-color 160ms ease, background 160ms ease, transform var(--dur-press) var(--ease-out);
}
.post-share__btn:hover,
.post-share__btn:focus-visible {
  color: var(--violet-pale);
  border-color: var(--violet-glow);
  background: var(--violet-soft);
}
.post-share__btn:active { transform: scale(0.97); }
.post-share__btn:focus-visible { outline: none; box-shadow: var(--focus); }
.post-share__btn.is-copied {
  color: #fff;
  background: var(--violet);
  border-color: var(--violet);
}
/* In-body section headings promoted from <p><strong>…</strong></p>. */
.post .prose .post__h2 {
  margin: var(--space-block) 0 var(--s-4);
  font-family: var(--display);
  font-weight: 600;
  font-size: var(--fs-h3);
  line-height: 1.2;
  letter-spacing: 0.01em;
  color: var(--ink);
}
.post__hero {
  position: relative;
  margin: var(--space-block) 0;
  border-radius: var(--radius);
  overflow: hidden;
  border: 1px solid var(--rule-2);
  /* Dark brand-tinted wash fills the frame until the eager hero paints, so the
     reserved box is never an empty bordered void on first impression. */
  background:
    radial-gradient(120% 120% at 20% 0%, rgba(122, 0, 223, 0.22), transparent 60%),
    linear-gradient(160deg, #160627 0%, #0a0418 70%, #070310 100%);
}
.post__hero img { display: block; width: 100%; height: 100%; object-fit: cover; }
/* End-of-post conversion block — turns the easy-to-miss inline ticket mention
   into a prominent violet CTA. */
/* Closing ticket CTA breaks out of the 66ch reading measure to the full content
   width (aligning with the read-next grid below) so the primary conversion block
   commands attention instead of sitting in a narrow column beside a dead gutter.
   On desktop it becomes a band: copy left, button(s) right. */
.post-cta {
  max-width: none;
  margin: var(--space-block) 0 0;
  padding: var(--space-card);
  background: var(--bg-tint);
  border: 1px solid var(--rule);
  border-left: 3px solid var(--violet);
  border-radius: var(--radius);
}
.post-cta__copy > :last-child { margin-bottom: 0; }
@media (min-width: 760px) {
  .post-cta {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: clamp(1.5rem, 4vw, 3rem);
    flex-wrap: wrap;
  }
  .post-cta__copy { flex: 1 1 28rem; min-width: 0; }
  .post-cta__btn,
  .post-cta__row { flex: none; }
}
.post-cta__line,
.post-cta__kicker {
  margin: 0 0 var(--s-2);
  font-family: var(--display);
  font-weight: 600;
  font-size: var(--fs-h3);
  line-height: 1.3;
  color: var(--ink);
}
/* Concrete commitment hook under the FOMO kicker: real date · venue · price in
   the mono HUD voice with a violet date so it never reads as just energy. */
.post-cta__next {
  margin: 0 0 var(--s-4);
  font-family: var(--mono);
  font-size: 0.9rem;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--violet-glow);
}
.post-cta__btn { align-self: flex-start; max-width: 100%; }
.post-cta__row { display: flex; flex-wrap: wrap; gap: var(--s-3); max-width: 100%; }
/* Mid-post inline CTA (after a body price/details list). On phones it goes
   full-width so the ticket action is impossible to scroll past in the long
   single-column body. */
.post-cta-inline { margin: var(--s-6) 0; }
@media (max-width: 600px) {
  .post-cta { padding: var(--s-5); }
  .post-cta__btn,
  .post-cta__row .btn,
  .post-cta-inline .btn { display: flex; width: 100%; }
}
/* Read-next rail — the /news/ card grid, scoped tighter so the article exits
   into more nights, never a dead end. */
.post-readnext { margin: var(--space-section) 0 0; }
.post-readnext__title {
  margin: 0 0 var(--s-5);
  padding-bottom: var(--s-3);
  border-bottom: 1px solid var(--rule);
  font-family: var(--mono);
  font-size: var(--fs-label);
  font-weight: 600;
  letter-spacing: 0.34em;
  text-transform: uppercase;
  color: var(--ink-faint);
}
.card-grid--readnext {
  grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
}

/* Gallery — clean uniform responsive grid for the ~30 dresscode photos. */
.gallery {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(clamp(140px, 22vw, 200px), 1fr));
  gap: var(--s-3);
  margin: var(--space-block) 0;
}
/* Each cell is a <picture> carrying the LQIP blur-up as its own background, so
   the box is never an empty black tile before the WebP paints (aspect-ratio
   reserves the space; the cover background fills it during load/decode). */
.gallery__cell {
  display: block;
  width: 100%;
  aspect-ratio: 4 / 5;
  overflow: hidden;
  border-radius: var(--radius);
  border: 1px solid var(--rule);
  background-color: var(--bg-tint);
  background-size: cover;
  background-position: center;
  transition: border-color 200ms ease, transform 200ms ease;
}
/* Decorative zoom only where a real pointer can hover. The cells are
   non-interactive <picture>s, so on touch a tap would otherwise leave one stuck
   scaled-up with nothing to dismiss it. */
@media (hover: hover) and (pointer: fine) {
  .gallery__cell:hover { border-color: var(--violet-glow); transform: scale(1.02); }
}
.gallery img,
.gallery__cell img {
  width: 100%;
  height: 100%;
  aspect-ratio: 4 / 5;
  object-fit: cover;
  display: block;
}

/* No-JS "show more" disclosure for the inspiration gallery — caps the visible
   height so the page never opens with a multi-screen scroll of tiles. */
.gallery-more { margin: 0 0 var(--space-block); }
.gallery-more > .gallery { margin-top: var(--s-3); }
.gallery-more__toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 44px;
  padding: var(--s-2) var(--s-6);
  font-family: var(--mono);
  font-size: var(--fs-label);
  font-weight: 700;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--ink);
  background: transparent;
  border: 1px solid var(--rule-2);
  border-radius: var(--radius);
  cursor: pointer;
  list-style: none;
  transition: border-color 200ms ease, color 200ms ease, background 200ms ease;
}
.gallery-more__toggle::-webkit-details-marker { display: none; }
.gallery-more__toggle:hover,
.gallery-more__toggle:focus-visible {
  color: var(--violet-pale);
  border-color: var(--violet-glow);
  background: var(--violet-soft);
}
.gallery-more__toggle:focus-visible { outline: none; box-shadow: var(--focus); }
.gallery-more__close { display: none; }
.gallery-more[open] .gallery-more__open { display: none; }
.gallery-more[open] .gallery-more__close { display: inline; }

@media (max-width: 720px) {
  /* Tighter mobile rhythm: 2 columns, smaller gap, less vertical air, so the
     bottom ticket CTA is reachable without a long black-gap scroll. */
  .gallery {
    grid-template-columns: repeat(3, 1fr);
    gap: var(--s-2);
    margin: var(--s-5) 0;
  }
}

/* Team — founder cards (figure > img + figcaption). */
/* About — opening band: prose left, a manifesto pull-quote + track-record stat
   rail right, so the page opens on proof instead of a half-empty canvas (the old
   single ~890px column left ~40% of a 1440 viewport dead). One column on mobile. */
.about-intro {
  display: grid;
  grid-template-columns: minmax(0, 1.55fr) minmax(0, 1fr);
  gap: clamp(2rem, 5vw, 4.5rem);
  align-items: start;
  margin: var(--space-block) 0;
}
.about-intro__story { max-width: none; }   /* fill the grid column, not prose's own cap */
.about-intro__aside {
  display: flex;
  flex-direction: column;
  gap: var(--s-6);
}
.about-manifesto {
  margin: 0;
  padding: var(--s-1) 0 var(--s-1) var(--s-4);
  border-left: 3px solid var(--violet);
}
.about-manifesto p {
  margin: 0;
  font-family: var(--display);
  font-weight: 600;
  font-size: clamp(1.4rem, 2.6vw, 2rem);
  line-height: 1.16;
  letter-spacing: -0.01em;
  color: var(--ink);
}
.about-stats {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: var(--s-4);
}
.about-stats__item {
  display: flex;
  flex-direction: column;
  gap: 0.1em;
  padding-bottom: var(--s-4);
  border-bottom: 1px solid var(--rule);
}
.about-stats__item:last-child { border-bottom: 0; padding-bottom: 0; }
.about-stats__n {
  font-family: var(--display);
  font-weight: 700;
  font-size: clamp(1.8rem, 3.4vw, 2.6rem);
  line-height: 0.98;
  letter-spacing: -0.02em;
  color: var(--violet-glow);
}
.about-stats__l {
  font-family: var(--mono);
  font-size: 0.72rem;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--ink-mid);
}
@media (max-width: 860px) {
  .about-intro { grid-template-columns: 1fr; gap: var(--s-6); }
  .about-stats { grid-auto-flow: column; grid-auto-columns: 1fr; }
  .about-stats__item { border-bottom: 0; padding-bottom: 0; }
}

.team-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(250px, 420px));
  gap: var(--s-5);
  margin: var(--space-block) 0;
}
.team-card {
  margin: 0;                     /* figure default */
  background: var(--bg-tint);
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  overflow: hidden;
  transition: border-color 200ms ease;
}
.team-card:hover { border-color: var(--steel-dim); }
.team-card img {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 4 / 5;
  object-fit: cover;
  border-bottom: 1px solid var(--rule);
}
.team-card figcaption { padding: var(--space-card); }
.team-card__name {
  display: block;
  margin: 0 0 var(--s-1);
  font-family: var(--display);
  font-weight: 600;
  font-size: var(--fs-h3);
  color: var(--ink);
}
.team-card__role {
  display: block;
  margin: 0 0 var(--s-2);
  font-family: var(--mono);
  font-size: 0.62rem;
  font-weight: 500;
  letter-spacing: 0.3em;
  text-transform: uppercase;
  color: var(--violet-glow);
}
.team-card__bio { margin: 0; font-size: var(--fs-small); color: var(--ink-mid); line-height: 1.6; }
/* Mobile: the 4:5 portraits stacked into two tall blocks and shoved the bios
   far down the page. Crop them to a shorter landscape ratio and pair name+role
   as a compact caption strip directly under each image so the bio sits closer
   to the fold. */
@media (max-width: 600px) {
  .team-grid { gap: var(--s-4); }
  .team-card img { aspect-ratio: 16 / 10; object-position: center 28%; }
  .team-card figcaption { padding: var(--s-4); }
  .team-card__name { margin: 0; font-size: 1.15rem; }
  .team-card__role { margin: 0.15em 0 var(--s-3); }
}

/* Numbered rule cards. */
.rule-list {
  list-style: none;
  counter-reset: rules;
  margin: var(--space-block) 0;
  padding: 0;
  display: grid;
  gap: var(--s-4);
}
.rule-list > li {
  counter-increment: rules;
  position: relative;
  padding: var(--space-card);
  padding-left: calc(var(--space-card) + var(--s-7) + var(--s-2));
  background: var(--bg-tint);
  border: 1px solid var(--rule);
  /* Signature violet spine — was #3D0070 (near-invisible on near-black). Brand
     violet at 3px so the rail reads as a deliberate anchor, not a faint edge. */
  border-left: 3px solid var(--violet);
  border-radius: var(--radius);
  color: var(--ink-mid);
  line-height: 1.65;
  transition: border-color 200ms ease;
}
.rule-list > li:hover { border-color: var(--steel-dim); border-left-color: var(--violet-hot); }
/* Oversized leading numeral + hairline tick so the five CORE rules read as an
   editorial sequence rather than five identical stacked panels. */
.rule-list > li::before {
  content: counter(rules, decimal-leading-zero);
  position: absolute;
  left: var(--space-card);
  top: calc(var(--space-card) - 0.12em);
  font-family: var(--display);
  font-size: 1.9rem;
  font-weight: 600;
  letter-spacing: 0.01em;
  color: var(--violet-glow);
  line-height: 1;
}
.rule-list > li::after {
  content: '';
  position: absolute;
  left: calc(var(--space-card) + 0.1em);
  top: calc(var(--space-card) + 2.3em);
  width: var(--s-5);
  height: 1px;
  background: var(--violet-deep);
}
.rule-list > li b,
.rule-list > li strong,
.rule-list__title {
  display: block;
  margin: 0 0 var(--s-1);
  font-family: var(--display);
  font-weight: 600;
  font-size: var(--fs-h3);
  letter-spacing: 0.01em;
  line-height: 1.3;
  color: var(--ink);
}
.rule-list > li > p { margin: 0; }

/* "Next: UNLEASHED <date>" line above a page's bottom CTA cluster — gives a
   reassured reader a concrete event-specific path back to the next party. */
.next-line {
  margin: var(--space-block) 0 var(--s-4);
  font-family: var(--mono);
  font-size: 0.95rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--ink-mid);
}
.next-line b { color: var(--ink); font-weight: 700; }
.next-line__label { color: var(--violet-glow); font-weight: 700; }

/* Newsletter closing reassurance — persuasive body copy, so brand body sans
   (mono is reserved for small ALL-CAPS labels only, not running prose). */
.nl-reassure {
  /* Promoted: sits right under Subscribe as a larger, full-opacity reassurance
     paired with the double-opt-in note — no longer a faint line trailing into
     empty space before the footer (raters). */
  margin: var(--s-5) 0 var(--s-3);
  max-width: 60ch;
  font-family: var(--sans);
  font-size: var(--fs-lead);
  font-weight: 500;
  line-height: 1.55;
  color: var(--ink);
}

/* Notice / callout. Full container width on every page (was capped at 70ch,
   which left the boxes narrower than the content around them). */
.notice {
  margin: var(--s-6) 0;
  max-width: none;
  padding: var(--space-card);
  background: var(--bg-tint);
  border: 1px solid var(--rule);
  border-left: 2px solid var(--violet);
  border-radius: var(--radius);
  color: var(--ink);
  font-size: var(--fs-small);
  line-height: 1.65;
}
.notice--mono {
  font-family: var(--mono);
  font-size: 0.78rem;
  letter-spacing: 0.08em;
}
.notice--wide { max-width: none; }   /* full container width (buy-tickets footer note) */
.notice--tight { margin-top: var(--s-3); }   /* pair the opt-in note with the reassurance above it */
/* Inline links inside callouts: violet + underline so a critical reference
   (e.g. the dress code on /rules/) never hides as plain body text. */
.notice a {
  color: var(--violet-glow);
  text-decoration: underline;
  text-decoration-color: rgba(188, 123, 255, 0.55);
  text-underline-offset: 3px;
  font-weight: 600;
  transition: color 140ms ease, text-decoration-color 140ms ease;
}
.notice a:hover { color: var(--violet-pale); text-decoration-color: var(--violet-pale); }
/* Cross-reference line (Rules → FAQ): quiet body text, not a boxed callout —
   keeps Rules focused on conduct while pointing operational detail to the FAQ. */
.rules-ref {
  margin: var(--s-5) 0 0;
  max-width: 70ch;
  font-size: var(--fs-small);
  line-height: 1.6;
  color: var(--ink-mid);
}
.rules-ref a {
  color: var(--violet-glow);
  text-decoration: underline;
  text-underline-offset: 3px;
  font-weight: 600;
}
.rules-ref a:hover { color: var(--violet-pale); }

/* Contact page — two huge stacked tap targets (Instagram / email). */
.contact-links {
  margin: var(--space-block) 0;
  display: grid;
  gap: var(--s-5);
}
.contact-link {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  grid-template-areas:
    'label arrow'
    'value arrow'
    'hint  arrow';
  align-items: center;
  gap: var(--s-1) var(--s-5);
  padding: var(--space-card);
  background: var(--bg-tint);
  border: 1px solid var(--rule);
  border-left: 2px solid var(--violet);
  border-radius: var(--radius);
  text-decoration: none;
  min-height: 44px;
  transition: border-color 200ms ease, background 200ms ease;
}
.contact-link:hover,
.contact-link:focus-visible {
  border-color: var(--violet-hot);
  border-left-color: var(--violet-hot);
  background: var(--violet-soft);
}
.contact-link:focus-visible { outline: 2px solid var(--violet-glow); outline-offset: 3px; }
.contact-link__label {
  grid-area: label;
  display: inline-flex;
  align-items: center;
  gap: var(--s-3);
  font-family: var(--mono);
  font-size: var(--fs-label);
  font-weight: 500;
  letter-spacing: 0.32em;
  text-transform: uppercase;
  color: var(--violet-glow);
}
.contact-link__label::before {
  content: '';
  flex: none;
  width: 21px;                   /* double-link glyph, 128:96 viewBox */
  height: 16px;
  background: var(--chrome);
  -webkit-mask: url('/assets/chains/link-closed.svg') center / contain no-repeat;
          mask: url('/assets/chains/link-closed.svg') center / contain no-repeat;
}
.contact-link__value {
  grid-area: value;
  font-family: var(--display);
  font-weight: 600;
  font-size: clamp(1.35rem, 5.8vw, 2.1rem);   /* email fits one line at 390 */
  line-height: 1.15;
  color: var(--ink);
  overflow-wrap: anywhere;
}
.contact-link__hint {
  grid-area: hint;
  font-family: var(--mono);
  font-size: 0.72rem;
  letter-spacing: 0.06em;
  color: var(--ink-faint);
}
.contact-link__arrow {
  grid-area: arrow;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 44px;
  min-height: 44px;
  font-family: var(--mono);
  font-size: 1.85rem;
  font-weight: 700;
  color: var(--violet-glow);     /* AA on dark; was sub-AA violet-hot */
  transition: transform 220ms var(--ease-out);
}
/* Keyboard focus nudges the arrow too; pointer hover only where hover is real. */
.contact-link:focus-visible .contact-link__arrow { transform: translateX(6px); }
@media (hover: hover) and (pointer: fine) {
  .contact-link:hover .contact-link__arrow { transform: translateX(6px); }
}
/* Rank the two channels so they don't read as one flat pair: the stated
   fastest-reply card leads with a brighter violet spine + faint glow; the
   second recedes to a quiet steel spine. Underground, not SaaS — no fills. */
.contact-link--primary {
  border-left-width: 3px;
  box-shadow: -1px 0 22px -8px rgba(122, 0, 223, 0.55);
}
.contact-link--secondary { border-left-color: var(--rule-2); }
.contact-link--secondary .contact-link__label { color: var(--ink-mid); }
.contact-link--secondary:hover,
.contact-link--secondary:focus-visible { border-left-color: var(--violet-hot); }
@media (max-width: 600px) {
  .contact-link { grid-template-columns: minmax(0, 1fr); grid-template-areas: 'label' 'value' 'hint'; }
  .contact-link__arrow { display: none; }
}
/* ≤359px: keep info@unleashed.berlin on one line instead of an ugly
   mid-word break (overflow-wrap: anywhere stays as the safety net). */
@media (max-width: 359px) {
  .contact-link__value { font-size: 1.2rem; }
}

/* --------------------------------------------------------------------------
   Mobile vertical rhythm (raters #14/#18/#21). On phones the hero blocks,
   ticket rows and content sections previously stacked with near-identical
   small gaps and blurred together. Open up the rhythm: clearer separation
   between hero lineup / what-to-expect / countdown / phase / CTA, taller
   ticket rows, and more breathing room around the about/faq proof blocks.
   -------------------------------------------------------------------------- */
@media (max-width: 600px) {
  /* Distinct intra-hero spacing so each block reads as its own section. */
  .hero-lineup { margin-bottom: var(--s-6); }
  .hero-expect { margin-bottom: var(--s-6); gap: var(--s-4); }
  .hero-cd { margin-bottom: var(--s-6); padding: var(--s-4) 0; }
  .hero-phase { margin-bottom: var(--s-6); }
  .hero-what { margin-bottom: var(--s-6); }
  /* Taller, easier-to-scan ticket rows with a stronger date column. */
  .tix-row,
  .tix-row--noflyer { padding: var(--s-5) 0; min-height: 72px; }
  .tix-list { border-top-width: 2px; }
  .tix-row { border-bottom-color: var(--rule-2); }
  /* About/FAQ proof + content sections get real breathing room. */
  .atmo { margin: var(--space-block) 0; gap: var(--s-5); }
  .about-section { margin-bottom: var(--space-block); }
  .about-comforts li { line-height: 1.6; }
  .mid-cta { margin: var(--space-block) 0; }
}
/* FAQ proof/testimonial block — more vertical room + a slightly larger quote on
   mobile so the strongest social proof breathes under the hero (raters #21). */
@media (max-width: 760px) {
  .atmo--faq { margin-top: var(--space-block); margin-bottom: var(--space-block); }
  .atmo--faq .atmo__quote p { font-size: var(--fs-lead); }
}

/* Whole-card anchor variant (news.php wraps the card in one <a>). */
a.card { text-decoration: none; color: inherit; }
a.card .card__media { margin: 0; }            /* figure default-margin reset */
a.card:focus-visible { outline: none; box-shadow: var(--focus); }
.card__more {
  margin-top: auto;
  padding-top: var(--s-3);
  font-family: var(--mono);
  font-size: 0.66rem;
  font-weight: 700;
  letter-spacing: 0.26em;
  text-transform: uppercase;
  color: var(--violet-glow);
}
a.card:hover .card__more { color: var(--violet-pale); }

/* /#tickets deep link (pages/tickets.php → hero CTA row): clear the sticky head. */
#tickets { scroll-margin-top: calc(var(--head-h) + var(--s-5)); }

/* Hero without a renderable flyer (and the "coming soon" state): one centered column. */
.hero__inner--solo {
  grid-template-columns: minmax(0, 720px);
  justify-content: center;
}

@media (max-width: 600px) {
  /* Full-width stacked hero CTAs. */
  .cta-row .btn-tickets { flex: 1 1 100%; justify-content: center; }
  /* Pin the price tag + row arrow next to the date on row 1 (auto-placement
     would push them below the full-width name). */
  .upcoming__tag { grid-row: 1; grid-column: 2; justify-self: end; }
  .upcoming__cta { grid-row: 1; grid-column: 3; justify-self: end; }
}

/* --------------------------------------------------------------------------
   14. FAQ — chained accordion. The marker is the snapped-link motif's
   second home: closed link at rest, sprung open on [open].
   -------------------------------------------------------------------------- */
/* Inter-group rhythm matches .about-section / .card-grid / .verdict (one cadence
   sitewide); --space-section is reserved for page-top padding + end-of-page closers. */
.faq-group { margin: 0 0 var(--space-block); }
/* Editorial eyebrow: heavier weight + a short violet rule tick before the label
   so each group reads as a titled section, not just spaced-out rows. */
.faq-group__title {
  display: flex;
  align-items: center;
  gap: var(--s-3);
  margin: 0 0 var(--s-4);
  padding: 0 0 var(--s-3);
  border-bottom: 1px solid var(--rule-2);
  font-family: var(--mono);
  /* Section wayfinding — bumped from ~12px so the grouping reads as a deliberate
     header you don't skim past; brighter violet, slightly tighter tracking. */
  font-size: clamp(0.82rem, 1vw, 0.92rem);
  font-weight: 700;
  letter-spacing: 0.26em;
  text-transform: uppercase;
  color: var(--violet-glow);
}
.faq-group__title::before {
  content: '';
  flex: none;
  width: var(--s-5);
  height: 2px;
  background: var(--violet);
}
.faq-item { border-bottom: 1px solid var(--rule-2); transition: border-color 160ms ease; }
/* Visible row state: a 3:1 violet underline on hover/focus-within so a keyboard
   or pointer user always sees which question is active (raters #8). */
.faq-item:hover,
.faq-item:focus-within { border-bottom-color: var(--violet-hot); }
.faq-item__q {
  display: flex;
  align-items: center;
  gap: var(--s-4);
  padding: var(--s-5) var(--s-3) var(--s-5) 0;
  font-family: var(--display);
  font-weight: 500;
  font-size: var(--fs-h3);
  line-height: 1.3;
  color: var(--ink);
  cursor: pointer;
  list-style: none;
  transition: color 160ms ease, background 160ms ease;
}
.faq-item__q:hover { background: rgba(122, 0, 223, 0.06); }
.faq-item__q::-webkit-details-marker { display: none; }
.faq-item__q::marker { content: ''; }
.faq-item__q::before {
  content: '';
  flex: none;
  width: 31px;                   /* double-link glyph, 128:96 viewBox */
  height: 23px;
  background: var(--chrome);
  -webkit-mask: url('/assets/chains/link-closed.svg') center / contain no-repeat;
          mask: url('/assets/chains/link-closed.svg') center / contain no-repeat;
  transition: transform 220ms var(--ease-out);
}
/* A second, text-level toggle affordance so the chain icon isn't the only
   open/close signal: a +/− that doesn't rely on the chrome glyph alone. */
.faq-item__q::after {
  content: '+';
  margin-left: auto;
  flex: none;
  font-family: var(--mono);
  font-weight: 700;
  font-size: 1.2rem;
  line-height: 1;
  color: var(--violet-glow);
  transition: transform 200ms ease;
}
.faq-item__q:hover { color: var(--violet-pale); }
/* Visible focus ring — >=3:1 against the dark panel (violet-glow is 7.2:1). */
.faq-item__q:focus-visible {
  outline: 2px solid var(--violet-glow);
  outline-offset: 2px;
  border-radius: var(--radius);
}
.faq-item[open] > .faq-item__q { color: var(--ink); }
.faq-item[open] > .faq-item__q::after { content: '\2212'; color: var(--violet-pale); }
.faq-item[open] > .faq-item__q::before {
  -webkit-mask-image: url('/assets/chains/link-open.svg');
          mask-image: url('/assets/chains/link-open.svg');
  background: var(--chrome-violet);
  transform: rotate(8deg) scale(1.1);
}
.faq-item__a {
  max-width: 70ch;
  padding: 0 0 var(--s-5) calc(31px + var(--s-4));
}
.faq-item__a p:first-child { margin-top: 0; }
/* Smooth open: ease the answer down with height + fade rather than the native
   instant jump. Pure progressive enhancement — engines without ::details-content
   / interpolate-size ignore these rules and keep the native toggle, so nothing
   breaks. Gated on reduced-motion explicitly because the global motion guard's
   selector list (*, ::before, ::after) does NOT match ::details-content, so
   opt-out users would otherwise still get the animated height. */
@media (prefers-reduced-motion: no-preference) {
  .faq-item { interpolate-size: allow-keywords; }
  .faq-item::details-content {
    height: 0;
    opacity: 0;
    overflow: clip;
    transition: height 280ms var(--ease-out), opacity 200ms ease,
                content-visibility 280ms allow-discrete;
  }
  .faq-item[open]::details-content {
    height: auto;
    opacity: 1;
  }
}

/* --------------------------------------------------------------------------
   17b. Newsletter — Turnstile reserve + progressive-profile modal.
   The base .modal CSS (§11) was built for the wide event dossier; the
   newsletter profile dialog (JS-built, class .modal nl-profile-modal) needs a
   small, centered, self-sized dialog instead of the 960px dossier frame. The
   .modal--compact alias is provided for any future hidden-markup variant.
   -------------------------------------------------------------------------- */

/* Turnstile container — reserve height so the widget appearing on first form
   interaction doesn't shove the consent line / submit button (no layout shift).
   Empty until newsletter.js renders the Cloudflare iframe into it. */
.nl-turnstile {
  min-height: 65px;
  margin: var(--s-2) 0 0;
}
.nl-turnstile:empty { min-height: 65px; }

/* Compact profile dialog — clean small centered card, not full-bleed. */
.nl-profile-modal .modal__dialog,
.modal--compact .modal__dialog {
  width: min(440px, 100%);
  max-width: min(440px, calc(100vw - 8vw));
  max-height: min(88vh, 640px);
  padding: var(--s-7) var(--space-card) var(--space-card);
  background: var(--bg-tint);
  border: 1px solid var(--rule-2);
  border-radius: var(--radius);
}
.nl-profile-modal .modal__body,
.modal--compact .modal__body {
  min-height: 0;
  overflow: visible;
}
/* The JS-built dialog opens its title as a plain <h2>; give it the panel voice. */
.nl-profile-modal #nl-profile-modal-title {
  margin: 0 0 var(--s-2);
  font-family: var(--display);
  font-weight: 600;
  font-size: var(--fs-h3);
  line-height: 1.15;
  letter-spacing: 0.01em;
  color: var(--ink);
}
.nl-profile-modal .nl-form__hint { margin: 0 0 var(--s-5); }
.nl-profile-modal .nl-form { gap: var(--s-4); }
.nl-profile-modal .nl-form__row:last-child {
  margin-top: var(--s-2);
  gap: var(--s-3);
}
.nl-profile-modal .nl-form__msg--ok { color: var(--violet-glow); }

/* --------------------------------------------------------------------------
   15. Motion / contrast / print guards.
   -------------------------------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
    scroll-behavior: auto !important;
  }
  #chain-cursor { display: none !important; }
  /* Kill the parallax layers' transform only — the img rotations inside are
     orientation, not motion, and must survive. */
  .hero-chains__tr, .hero-chains__tl { transform: none !important; }
  .modal--info .modal__dialog,
  .modal--info .modal__dialog--wide { transform: none !important; }
}

@media (forced-colors: active) {
  .btn,
  .btn-tickets,
  .upcoming__btn,
  .modal__dialog,
  .modal__close,
  .nav-toggle,
  .nl-input,
  .card,
  .team-card,
  .rule-list > li,
  .notice,
  .contact-link,
  .tix-row__cta,
  .faq-item {
    border: 1px solid CanvasText;
    forced-color-adjust: none;
    background: Canvas;
    color: CanvasText;
  }
  :focus-visible { outline: 2px solid Highlight !important; outline-offset: 3px; }
  .skip-link { background: Highlight; color: HighlightText; }
}

@media print {
  #chain-cursor, .grain, .hero-chains, .nav-toggle, .skip-link,
  .modal, .foot-newsletter, .cta-row, .hero-cd { display: none !important; }
  html, body { background: #fff; color: #000; }
  .site-head { position: static; background: none; border-bottom: 1px solid #000; }
  .site-foot { background: none; border-top: 1px solid #000; }
  .site-foot::before { display: none; }
  a { color: #000; }
  .page-title, .prose h2, .prose strong, .card__title,
  .upcoming__btn { color: #000; }
  .brand__logo { filter: invert(1); }
  .prose, .page-lead, .card__excerpt { color: #222; }
}

/* --------------------------------------------------------------------------
   18. Round-4 content components.
   Dress-code "instant verdict" strip (WEAR THIS / LEAVE IT HOME), the Outfit AI
   feature card, and the chains brand-tie-in line. Built only from existing
   tokens; mobile-first; AA contrast; :focus-visible on every interactive el.
   -------------------------------------------------------------------------- */

/* ---- Instant verdict strip — the dress-code centerpiece. ---------------- */
.verdict { margin: var(--space-block) 0; }
.verdict .prose { margin-bottom: var(--s-5); }

.verdict__grid {
  display: grid;
  grid-template-columns: 1fr;          /* stacked mobile-first */
  gap: var(--s-4);
}
@media (min-width: 720px) {
  .verdict__grid { grid-template-columns: 1fr 1fr; gap: var(--s-5); }
}

.verdict__col {
  position: relative;
  display: flex;
  flex-direction: column;
  padding: var(--space-card);
  background: var(--bg-tint);
  border: 1px solid var(--rule);
  border-top: 2px solid var(--rule-2);
  border-radius: var(--radius);
  transition: border-color 200ms ease;
}
/* YES carries the brand violet; NO stays steel-neutral (a "no", not an alarm). */
.verdict--yes { border-top-color: var(--violet); }
.verdict--no  { border-top-color: var(--steel-dim); }
.verdict--yes:hover { border-color: var(--violet-hot); border-top-color: var(--violet-hot); }
.verdict--no:hover  { border-color: var(--steel-dim); }

.verdict__verdict {
  display: flex;
  align-items: center;
  gap: var(--s-3);
  margin: 0 0 var(--s-5);
  font-family: var(--display);
  font-weight: 700;
  font-size: var(--fs-h3);
  line-height: 1.1;
  letter-spacing: 0.01em;
  text-transform: uppercase;
  color: var(--ink);
}
.verdict__mark {
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  border-radius: var(--radius);
  font-family: var(--mono);
  font-size: 0.9rem;
  font-weight: 700;
  line-height: 1;
}
.verdict--yes .verdict__mark { background: var(--violet); color: var(--ink); }
.verdict--no  .verdict__mark { background: rgba(241, 236, 250, 0.10); color: var(--ink-faint); }

.verdict__list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: var(--s-2);
}
.verdict__list li {
  position: relative;
  padding-left: var(--s-5);
  font-size: var(--fs-small);
  line-height: 1.5;
  color: var(--ink-mid);
}
.verdict__list li::before {
  position: absolute;
  left: 0;
  top: 0.05em;
  font-family: var(--mono);
  font-weight: 700;
  font-size: 0.95em;
}
.verdict--yes .verdict__list li { color: var(--ink); }
.verdict--yes .verdict__list li::before { content: '+'; color: var(--violet-glow); }
.verdict--no  .verdict__list li::before { content: '\2212'; color: var(--ink-faint); }   /* minus */

/* ---- Outfit AI feature card. ------------------------------------------- */
.outfit-ai {
  position: relative;
  margin: var(--space-block) 0;
  padding: var(--space-card);
  background: var(--bg-tint);
  border: 1px solid var(--rule-2);
  border-left: 2px solid var(--violet);
  border-radius: var(--radius);
  overflow: hidden;
}
/* Faint chrome-violet wash in the top-right corner — futuristic, no glow. */
.outfit-ai::after {
  content: '';
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: radial-gradient(120% 140% at 100% 0%, var(--violet-soft) 0%, transparent 55%);
}
.outfit-ai > * { position: relative; z-index: 1; }
.outfit-ai__body { min-width: 0; }
/* On-brand example exchange (replaces the pasted Telegram screenshot): styled
   chat bubbles + a score chip, all in the chrome/violet system. */
.outfit-ai__exchange {
  margin: var(--s-5) 0 0;
  display: flex;
  flex-direction: column;
  gap: var(--s-3);
  padding: var(--space-card);
  background: rgba(7, 3, 16, 0.55);
  border: 1px solid var(--rule-2);
  border-radius: var(--radius);
}
.outfit-ai__msg {
  margin: 0;
  padding: var(--s-3) var(--s-4);
  font-size: 0.9rem;
  line-height: 1.45;
  color: var(--ink);
  border-radius: var(--radius);
  border: 1px solid var(--rule);
}
.outfit-ai__who {
  display: block;
  margin-bottom: 0.25em;
  font-family: var(--mono);
  font-size: 0.6rem;
  font-weight: 700;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--violet-glow);
}
.outfit-ai__msg--you { align-self: flex-end; max-width: 85%; background: var(--bg-tint); color: var(--ink-mid); }
.outfit-ai__msg--bot { align-self: flex-start; max-width: 92%; background: var(--violet-soft); border-color: var(--violet-deep); }
.outfit-ai__score {
  align-self: flex-start;
  margin: var(--s-1) 0 0;
  display: inline-flex;
  align-items: baseline;
  gap: var(--s-2);
  padding: var(--s-2) var(--s-4);
  background: var(--violet);
  border-radius: var(--radius);
}
.outfit-ai__score-n {
  font-family: var(--display);
  font-weight: 700;
  font-size: 1.7rem;
  line-height: 1;
  color: #fff;
}
.outfit-ai__score-unit {
  font-family: var(--mono);
  font-size: 0.62rem;
  font-weight: 700;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: #fff;
}
@media (min-width: 720px) {
  .outfit-ai {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 0.85fr);
    align-items: center;
    gap: var(--space-card);
  }
  .outfit-ai__exchange { margin: 0; }
}
.outfit-ai__kicker {
  margin: 0 0 var(--s-3);
  font-family: var(--mono);
  font-size: var(--fs-label);
  font-weight: 500;
  letter-spacing: 0.32em;
  text-transform: uppercase;
  color: var(--violet-glow);
}
.outfit-ai__title {
  margin: 0 0 var(--s-3);
  font-family: var(--display);
  font-weight: 600;
  font-size: var(--fs-h2);
  line-height: 1.12;
  letter-spacing: 0;
  color: var(--ink);
}
.outfit-ai__lead {
  margin: 0 0 var(--s-5);
  max-width: 56ch;
  font-size: var(--fs-body);
  line-height: 1.65;
  color: var(--ink-mid);
}
.outfit-ai__action {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s-3) var(--s-5);
}
.outfit-ai__note {
  margin: 0;
  flex: 1 1 200px;
  font-family: var(--mono);
  font-size: 0.72rem;
  letter-spacing: 0.04em;
  line-height: 1.5;
  color: var(--ink-faint);
}
@media (min-width: 720px) {
  .outfit-ai { padding: var(--s-7) var(--space-card); }
}

/* ---- Chains brand tie-in line. ----------------------------------------- */
.chains-line {
  position: relative;
  margin: var(--space-block) 0;
  max-width: 70ch;
  padding-left: calc(27px + var(--s-4));
  font-family: var(--display);
  font-weight: 500;
  font-size: var(--fs-lead);
  line-height: 1.45;
  letter-spacing: -0.005em;
  color: var(--ink);
}
.chains-line::before {
  content: '';
  position: absolute;
  left: 0;
  top: 0.18em;
  width: 27px;                         /* double-link glyph, 128:96 viewBox */
  height: 20px;
  background: var(--chrome-violet);
  -webkit-mask: url('/assets/chains/link-closed.svg') center / contain no-repeat;
          mask: url('/assets/chains/link-closed.svg') center / contain no-repeat;
}

/* §19 — about page full-width sections -------------------------------------
   Comforts + Venue span the full .page__inner width (like .team-grid /
   .card-grid above) instead of sitting inside a narrow 70ch .prose, so the
   bottom of the page no longer reads as orphaned. Headings align to the same
   left edge as the grids; running paragraphs keep a ~70ch measure.
   Scoped to about-page classes only — global .prose is untouched. */
.team-outro {
  max-width: 70ch;
  margin: var(--s-5) 0 0;
  color: var(--ink-mid);
}
.about-section {
  margin: var(--space-block) 0 0;
}
/* Closing conversion block — the story ends on the buy action. */
.about-close {
  margin: var(--space-section) 0 0;
  padding: var(--space-card);
  background: var(--bg-tint);
  border: 1px solid var(--rule);
  border-left: 2px solid var(--violet);
  border-radius: var(--radius);
}
.about-close__title { margin: 0 0 var(--s-3); }
.about-close__p {
  max-width: 60ch;
  margin: 0 0 var(--s-5);
  color: var(--ink-mid);
}
.about-section__title {
  margin: 0 0 var(--s-4);
}
.about-section__p {
  max-width: 70ch;
  margin: 0;
}
/* Venue: copy left, a committable facts card right — fills the formerly-empty
   right gutter so the closing section keeps the page's two-column discipline. */
.about-venue__grid {
  display: grid;
  grid-template-columns: minmax(0, 1.4fr) minmax(0, 1fr);
  gap: clamp(2rem, 5vw, 4rem);
  align-items: start;
}
.about-venue__card {
  padding: var(--space-card);
  background: var(--bg-tint);
  border: 1px solid var(--rule);
  border-top: 2px solid var(--violet);
  border-radius: var(--radius);
}
.venue-facts { margin: 0 0 var(--s-4); display: grid; gap: 0; }
.venue-facts__row {
  display: flex;
  justify-content: space-between;
  gap: var(--s-4);
  padding: var(--s-3) 0;
  border-bottom: 1px solid var(--rule);
}
.venue-facts__row:last-child { border-bottom: 0; }
.venue-facts dt {
  font-family: var(--mono);
  font-size: 0.72rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink-mid);
}
.venue-facts dd {
  margin: 0;
  text-align: right;
  font-weight: 500;
  color: var(--ink);
}
.venue-facts__map {
  display: inline-flex;
  align-items: center;
  min-height: 24px;   /* WCAG 2.5.8 AA target size */
  gap: var(--s-2);
  font-family: var(--mono);
  font-size: 0.74rem;
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--violet-glow);
  text-decoration: underline;
  text-underline-offset: 3px;
  text-decoration-color: rgba(188, 123, 255, 0.5);
}
.venue-facts__map:hover { color: var(--violet-pale); text-decoration-color: var(--violet-pale); }
@media (max-width: 860px) {
  .about-venue__grid { grid-template-columns: 1fr; gap: var(--s-5); }
}
.about-comforts {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--s-3) var(--s-6);
}
@media (min-width: 720px) {
  .about-comforts { grid-template-columns: 1fr 1fr; }
}
.about-comforts li {
  position: relative;
  /* Clear the 27px chain glyph + ~12px so it reads as a deliberate marker and
     never touches the first word. */
  padding-left: 2.6rem;
  color: var(--ink-mid);
}
.about-comforts li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0.18em;
  width: 27px;
  height: 20px;
  background: var(--chrome-violet);
  -webkit-mask: url('/assets/chains/link-closed.svg') center / contain no-repeat;
          mask: url('/assets/chains/link-closed.svg') center / contain no-repeat;
}

/* ---------------------------------------------------------------------------
   §20 — header Buy-Tickets CTA. Persistent standout button at the far right of
   the header (right of the EN/DE switch on desktop; left of the hamburger on
   mobile, always visible — never collapses into the menu).
   --------------------------------------------------------------------------- */
.nav-cta {
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 44px;            /* >=44px touch target (was 40px) */
  padding: 0 var(--s-5);
  font-family: var(--mono);
  font-size: 0.79rem;          /* ~12.6px — reads as a button, not a chip */
  font-weight: 700;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  white-space: nowrap;
  text-decoration: none;
  /* Same primary-button recipe as .btn--primary: light --ink on --violet
     (~6:1, passes AA). --violet is never small dark text on dark. */
  color: var(--ink);
  background: var(--violet);
  border: 1px solid var(--violet);
  border-radius: var(--radius);
  transition: background 180ms ease, border-color 180ms ease, transform var(--dur-press) var(--ease-out);
}
.nav-cta:hover { background: var(--violet-hot); border-color: var(--violet-hot); }
.nav-cta:active { transform: scale(0.97); }
.nav-cta:focus-visible { outline: 2px solid var(--ink); outline-offset: 3px; }
/* Active page (you're on /buy-tickets/): keep the AA-passing --violet fill —
   --violet-hot dropped --ink contrast to ~4.0:1 (fails for the small mono
   label). Signal "current" with the pale border + inset ring, not a lighter
   fill. */
.nav-cta--active {
  background: var(--violet);
  border-color: var(--violet-pale);
  box-shadow: inset 0 0 0 1px var(--violet-pale);
}
/* Desktop horizontal nav (≥1121px): brand + 8-item nav (incl. Shop) + language
   switch + Buy-Tickets CTA must all fit the 1160px header column — in German,
   whose labels run longer, this is tight at EVERY desktop width. Apply one
   consistent compact sizing across the whole range with NO upper cap, so it
   never reverts to the roomy base spacing above 1500px (which clipped the CTA
   once Shop became the 8th item): tighter gaps, a lean CTA, a slightly smaller
   wordmark, and the small "by UNDR" tag dropped — the mark + wordmark still read
   UNLEASHED and the footer carries the full "by UNDR" lockup; the sub returns
   ≤1120px where the hamburger layout has room. These overrides live late so they
   win over the earlier mid-range block. Below 1121px → hamburger dropdown. */
@media (min-width: 1121px) {
  .site-head__inner { gap: var(--s-3); }
  .nav { gap: var(--s-2); }
  .brand__logo, .brand__mark { height: 22px; }
  .brand__sub { display: none; }
  .nav__link { padding: 0 var(--s-2); letter-spacing: 0.05em; }
  .nav__link::after { left: var(--s-2); right: var(--s-2); }
  .lang-switch { padding-left: var(--s-2); }
  .nav-cta { padding: 0 var(--s-2); font-size: 0.76rem; letter-spacing: 0.1em; }
}
/* Bottom edge of the desktop range (1121–1180px), just above the hamburger, is
   the tightest: the header column is at its narrowest while still horizontal, so
   shave the last few px (gaps + tracking) so the German CTA still clears. */
@media (max-width: 1180px) and (min-width: 1121px) {
  .site-head__inner { gap: var(--s-2); }
  .nav { gap: var(--s-1); }
  .nav__link { letter-spacing: 0.02em; }
  .nav-cta { letter-spacing: 0.06em; }
}
@media (max-width: 1120px) {
  /* One row that always fits: logo left, 44x44 toggle pinned right. The header
     CTA is dropped here down to mobile — the hero/body already carry the primary
     Buy Tickets, and at 360-414px logo+CTA+toggle overflowed the viewport. */
  .nav-cta { display: none; }
  .nav-toggle { order: 3; margin-left: auto; }
}

/* --------------------------------------------------------------------------
   R1 fixer additions — FAQ ticket CTA, contact build-out, newsletter scarcity.
   -------------------------------------------------------------------------- */
/* FAQ end-of-page conversion — a reassured first-timer now has a clear path. */
.faq-cta {
  margin: var(--space-block) 0 0;
  padding: var(--space-card);
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-5);
  background: var(--bg-tint);
  border: 1px solid var(--rule-2);
  border-left: 3px solid var(--violet);
  border-radius: var(--radius);
}
.faq-cta__copy { flex: 1 1 22rem; }
.faq-cta__line {
  margin: 0 0 var(--s-2);
  font-family: var(--display);
  font-weight: 600;
  font-size: var(--fs-h3);
  line-height: 1.3;
  color: var(--ink);
}
.faq-cta__date {
  margin: 0;
  font-family: var(--mono);
  /* Strongest urgency lever on the page — given real size/weight instead of a
     faint footnote, with the date in bright violet. */
  font-size: 0.92rem;
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink);
}
.faq-cta__date b { color: var(--violet-glow); font-weight: 700; }
.faq-cta .btn { flex: none; }
@media (max-width: 560px) {
  .faq-cta .btn { width: 100%; }
}

/* Contact — response-time + press/booking split, then a trust link row, so the
   page reads as built and doubles as a conversion touchpoint. */
.contact-detail {
  margin: var(--space-block) 0;
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--s-6);
}
.contact-detail__title {
  margin: 0 0 var(--s-3);
  font-family: var(--mono);
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.26em;
  text-transform: uppercase;
  color: var(--violet-glow);
}
.contact-detail__p { margin: 0; color: var(--ink-mid); line-height: 1.65; max-width: 46ch; }
.contact-more { margin: var(--space-block) 0 0; }
.contact-more__title {
  margin: 0 0 var(--s-2);
  font-family: var(--display);
  font-weight: 600;
  font-size: var(--fs-h2);
  color: var(--ink);
}
.contact-more__trust {
  margin: 0 0 var(--s-3);
  font-family: var(--sans);
  font-size: var(--fs-lead);
  font-weight: 500;
  line-height: 1.45;
  color: var(--ink);
}
.contact-more__safety {
  white-space: nowrap;
  color: var(--violet-glow);
  font-weight: 700;
  /* Inline link in a text block needs a non-color indicator (WCAG 1.4.1) — match
     the underline treatment used by .vendor-card__link / .venue-facts__map. */
  text-decoration: underline;
  text-decoration-color: rgba(188, 123, 255, 0.5);
  text-underline-offset: 3px;
}
.contact-more__safety:hover { color: var(--violet-pale); text-decoration-color: var(--violet-pale); }
.contact-more__intro { margin: 0 0 var(--s-5); color: var(--ink-mid); }
.contact-more__row { display: flex; flex-wrap: wrap; gap: var(--s-3); }
@media (max-width: 560px) {
  .contact-detail { grid-template-columns: 1fr; gap: var(--s-5); }
  .contact-more__row .btn { width: 100%; }
}

/* Newsletter scarcity line — presale FOMO above the form. Persuasive copy → brand
   body sans (mono is for small ALL-CAPS labels only). */
.nl-scarcity {
  display: flex;
  align-items: flex-start;
  gap: var(--s-3);
  margin: 0 0 var(--space-block);
  max-width: 60ch;
  font-family: var(--sans);
  font-size: var(--fs-body);
  line-height: 1.55;
  color: var(--ink);
}
.nl-benefits { margin: 0 0 var(--space-block); }
.nl-scarcity__mark {
  flex: none;
  width: 18px;
  height: 14px;
  margin-top: 0.25em;
  background: var(--chrome-violet);
  -webkit-mask: url('/assets/chains/link-closed.svg') center / contain no-repeat;
          mask: url('/assets/chains/link-closed.svg') center / contain no-repeat;
}

/* --------------------------------------------------------------------------
   Instagram — GDPR-clean server-side feed (homepage). Posts are fetched from a
   Make webhook and their images cached locally (public/lib/instagram.php), then
   rendered as a tile grid linking out to each post. Falls back to a Follow card
   when nothing is cached. The browser only ever loads images from our domain.
   -------------------------------------------------------------------------- */
.ig {
  max-width: var(--max-w);
  margin: 0 auto;
  padding: var(--space-section) var(--gutter) 0;
}
.ig__head { text-align: center; max-width: var(--prose-w); margin: 0 auto; }
.ig__kicker {
  font-family: var(--mono);
  font-size: var(--fs-label);
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--ink-faint);
  margin: var(--space-block) 0 var(--s-3);
}
.ig__title {
  font-family: var(--mono);
  font-weight: 700;
  font-size: var(--fs-h2);
  letter-spacing: 0.02em;
  color: var(--ink);
  margin: 0 0 var(--s-3);
  overflow-wrap: anywhere;
}
.ig__line {
  color: var(--ink-mid);
  max-width: 52ch;
  margin: 0 auto var(--s-5);
}
.ig__actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-4);
  justify-content: center;
}
.ig__feed {
  list-style: none;
  margin: var(--space-block) auto 0;
  padding: 0;
  max-width: var(--max-w);
  display: grid;
  grid-template-columns: repeat(2, 1fr);   /* mobile: a clean 2×2 */
  gap: var(--s-3);
}
.ig__cell { margin: 0; }
/* Mobile shows only the 4 most recent posts (2×2); the 5th joins once there's
   room for the full desktop row. */
.ig__cell:nth-child(n+5) { display: none; }
@media (min-width: 720px) {
  .ig__feed { grid-template-columns: repeat(5, 1fr); } /* desktop: one row of 5 */
  .ig__cell:nth-child(n+5) { display: block; }
}
.ig__tile {
  display: block;
  aspect-ratio: 1 / 1;
  overflow: hidden;
  border-radius: var(--radius);
  border: 1px solid var(--steel-dim);   /* chrome edge so dark video stills read as framed tiles, not empty boxes */
  background: var(--bg-tint);
}
.ig__tile img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform 300ms var(--ease-out);
}
.ig__tile:focus-visible { outline: 2px solid var(--violet-glow); outline-offset: 2px; }
/* Zoom on hover only where a real pointer can hover — a tap must not leave a
   tile stuck zoomed. */
@media (hover: hover) and (pointer: fine) {
  .ig__tile:hover img { transform: scale(1.05); }
}

/* ==========================================================================
   Pre-launch rework (2026-06): hero lineup floors, CTA row, intro band, IG pics
   grid + play badge, dress-code gallery 5-up, About aside meta, footer reorder.
   Appended last so these override the earlier definitions by source order.
   ========================================================================== */

/* Hero lineup — Main / Dungeon, every DJ at one size (no headliner hierarchy). */
.hero-lineup__floors { display: flex; flex-wrap: wrap; gap: var(--s-5) var(--s-7); }
.hero-lineup__floor { min-width: 0; }
.hero-lineup__floor-name {
  font-family: var(--mono);
  font-size: var(--fs-label);
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--violet-glow);
  margin: 0 0 var(--s-2);
}
.hero-lineup__djs { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--s-1); }
.hero-lineup__dj {
  font-family: var(--display);
  font-weight: 600;
  font-size: var(--fs-h3);
  line-height: 1.15;
  color: var(--ink);
  overflow-wrap: anywhere;
}

/* CTA row — one line on desktop, stacked full-width on phones. */
.cta-row { flex-wrap: nowrap; }
.cta-row .btn-tickets { flex: 0 1 auto; min-width: 0; }
@media (max-width: 600px) {
  .cta-row { flex-direction: column; align-items: stretch; }
  .cta-row > .btn,
  .cta-row .btn-tickets { width: 100%; flex: 0 0 auto; }
}

/* Homepage intro band (manifesto). */
.intro { max-width: var(--max-w); margin: 0 auto var(--space-section); padding: 0 var(--gutter); }
.intro__title {
  font-family: var(--display);
  font-weight: 600;
  font-size: var(--fs-h2);
  color: var(--ink);
  margin: var(--space-block) 0 var(--s-4);
}
.intro__p { color: var(--ink-mid); font-size: var(--fs-lead); line-height: 1.6; margin: 0 0 var(--s-4); max-width: 62ch; }
.intro__cta { display: flex; flex-wrap: wrap; gap: var(--s-3); margin-top: var(--s-5); }

/* Homepage quick-links — refined link tiles (mono label + one-line descriptor +
   arrow) into the key pre-visit pages; replaced the manifesto band. Chrome
   top-edge + hover lift, matching the brand's tile language. */
/* Lives inside the proof grid as a full-width bottom row (not a lone section). */
.home-explore { grid-column: 1 / -1; margin-top: var(--space-block); }
.home-explore__list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: var(--s-4);
}
.home-explore__list li { margin: 0; display: flex; }
.explore-link {
  display: flex;
  flex-direction: column;
  gap: var(--s-2);
  flex: 1;
  min-height: 132px;
  padding: var(--s-5);
  background: var(--bg-tint);
  border: 1px solid var(--rule-2);
  border-top: 2px solid var(--violet);
  border-radius: var(--radius);
  text-decoration: none;
  color: var(--ink);
  transition: border-color 200ms var(--ease-out), transform 200ms var(--ease-out);
}
.explore-link__label {
  font-family: var(--mono);
  font-size: var(--fs-label);
  font-weight: 500;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--violet-glow);
}
.explore-link__desc { font-size: 0.95rem; line-height: 1.45; color: var(--ink-mid); }
.explore-link__arrow {
  margin-top: auto;
  font-family: var(--mono);
  font-size: 1.05rem;
  line-height: 1;
  color: var(--steel);
  transition: transform 200ms var(--ease-out), color 200ms var(--ease-out);
}
.explore-link:focus-visible { outline: none; box-shadow: var(--focus); border-color: var(--violet-hot); }
@media (hover: hover) and (pointer: fine) {
  .explore-link:hover { border-color: var(--steel-dim); transform: translateY(-3px); }
  .explore-link:hover .explore-link__arrow { transform: translateX(4px); color: var(--violet-glow); }
}
@media (max-width: 760px) {
  .home-explore__list { grid-template-columns: 1fr 1fr; }
}
@media (max-width: 440px) {
  .home-explore__list { grid-template-columns: 1fr; }
}

/* Instagram — heading (dress code), inline variant, and video play badge. */
.ig__heading {
  font-family: var(--mono);
  font-size: var(--fs-label);
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--ink-faint);
  text-align: center;
  margin: var(--space-block) 0 var(--s-4);
}
.ig--inline { max-width: none; padding: 0; }
.ig__tile { position: relative; }
.ig__play {
  position: absolute;
  inset: 0;
  margin: auto;
  width: 46px;
  height: 46px;
  border-radius: 50%;
  background: rgba(7, 3, 16, 0.45);
  -webkit-backdrop-filter: blur(2px);
          backdrop-filter: blur(2px);
  pointer-events: none;
}
.ig__play::after {
  content: '';
  position: absolute;
  inset: 0;
  margin: auto;
  width: 0;
  height: 0;
  border-style: solid;
  border-width: 8px 0 8px 13px;
  border-color: transparent transparent transparent rgba(241, 236, 250, 0.95);
  transform: translateX(2px);
}

/* Dress-code inspiration gallery: 3×5 on desktop, AT the page content width
   (matches the cards/text above — no full-bleed breakout). */
@media (min-width: 721px) {
  .gallery { grid-template-columns: repeat(5, 1fr); }
}

/* About right-rail meta — organized by UNDR + Best Kinky Party 2025. */
.about-aside__meta {
  display: flex;
  flex-direction: column;
  gap: var(--s-2);
  margin-top: var(--s-5);
  padding-top: var(--s-4);
  border-top: 1px solid var(--rule);
}
.about-aside__org {
  font-family: var(--mono);
  font-size: var(--fs-label);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink-faint);
  text-decoration: none;
}
.about-aside__org:hover { color: var(--violet-glow); }
.about-aside__award {
  font-family: var(--mono);
  font-size: var(--fs-label);
  letter-spacing: 0.04em;
  color: var(--violet-glow);
  text-decoration: none;
}
.about-aside__award:hover { color: var(--violet-pale); }

/* Footer reorder: brand/socials → newsletter → links (was newsletter → links → brand). */
@media (min-width: 1024px) {
  .site-foot__grid { grid-template-columns: minmax(0, 1fr) minmax(0, 1.5fr) minmax(0, 0.8fr); }
  .foot-brand { align-items: flex-start; text-align: left; }
  .foot-socials { justify-content: flex-start; }
}

/* ===== Punch-list polish round 2 (2026-06) ===== */

/* Bigger, more present "Next up" tag on the featured ticket row. */
.tix-row__tag {
  font-size: 0.78rem;
  letter-spacing: 0.18em;
  padding: 0.34em 0.72em;
}

/* "Where we sell" vendor cards: drop the oversized index watermark, let cards
   take their natural height (no stretch → no dead space under short cards). */
.vendor-grid { align-items: stretch; }   /* equal-height cards (aligned bottoms), not ragged */
.vendor-card__no { display: none; }

/* Homepage "follow for more" link + dress-code Instagram CTA. */
.ig__more { text-align: center; margin: var(--s-5) 0 0; }
.ig__more a {
  font-family: var(--mono);
  font-size: var(--fs-label);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--violet-glow);
  text-decoration: none;
}
.ig__more a:hover { color: var(--violet-pale); }
/* Gallery "show more" toggle + Instagram link share one row; the open <details>
   spans the full width so the revealed gallery isn't cramped (IG link wraps below). */
.dc-gallery-actions { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-3); margin: var(--space-block) 0 0; }
.dc-gallery-actions .gallery-more { margin: 0; }
.dc-gallery-actions .gallery-more[open] { flex: 1 1 100%; }
.dc-ig-cta { margin: 0; }

/* Hero: the right column stacks flyer → countdown → scarcity → CTAs as one block.
   The column is stretched to the left column's height and the whole stack is
   vertically centred, so the flyer sits opposite the big date with the act-now
   cluster directly beneath it (a calmer, balanced hero). */
.hero__inner { align-items: stretch; }
.hero__visual { display: flex; flex-direction: column; gap: var(--s-5); justify-content: flex-end; }
.hero__visual > * { margin: 0; }
/* Flyer + caption + countdown sit together as one group, bottom-aligned with the
   left column's CTA buttons (plain hairline divider, the whole group is pushed
   down via justify-content:flex-end). */
.hero__visual .hero-rule { margin: 0; border: 0; height: 1px; background: var(--rule-2); }
/* CTAs now live in the LEFT column, centred under the floor tiles — natural
   width, side by side on desktop; the ≤600px rule still stacks them full-width. */
.hero__copy .cta-row { justify-content: center; }
